From cf0b22771b5e2ae17a5cc6b9559bfd9463b0f9f5 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 26 Mar 2023 19:23:09 +0200 Subject: [PATCH 001/656] Cache extruder settings. When writing GCode, there are certain settings that need to be checked over and over again. Instead of pulling them from the Settings hash table every time, read them once and then save them. This speeds up single-threaded slicing by about 3% (one thread of a 5950X; with many threads, it seems to be more like 10%, since GCode writing then takes proportionally more of the time). There is a very large long tail of code that keeps reading these settings, in particular from mesh settings, so there is probably more to be gained here, but this patch takes out the two biggest cases. --- include/gcodeExport.h | 2 ++ src/gcodeExport.cpp | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/gcodeExport.h b/include/gcodeExport.h index a8c2c57b55..d577bd94d6 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -87,6 +87,8 @@ class GCodeExport : public NoCopy double last_e_value_after_wipe; //!< The current material amount extruded since last wipe unsigned fan_number; // nozzle print cooling fan number + Point nozzle_offset; //!< Cache of setting machine_nozzle_offset_[xy] + bool machine_firmware_retract; //!< Cache of setting machine_firmware_retract std::deque extruded_volume_at_previous_n_retractions; // in mm^3 diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 4758ec5503..e75a084a0d 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -87,6 +87,18 @@ void GCodeExport::preSetup(const size_t start_extruder) extruder_attr[extruder_nr].last_retraction_prime_speed = train.settings.get("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured... extruder_attr[extruder_nr].fan_number = train.settings.get("machine_extruder_cooling_fan_number"); + + // Cache some settings that we use frequently. + const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[extruder_nr].settings; + if (use_extruder_offset_to_offset_coords) + { + extruder_attr[extruder_nr].nozzle_offset = Point(extruder_settings.get("machine_nozzle_offset_x"), extruder_settings.get("machine_nozzle_offset_y")); + } + else + { + extruder_attr[extruder_nr].nozzle_offset = Point(0, 0); + } + extruder_attr[extruder_nr].machine_firmware_retract = extruder_settings.get("machine_firmware_retract"); } machine_name = mesh_group->settings.get("machine_name"); @@ -294,18 +306,9 @@ bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const Point GCodeExport::getGcodePos(const coord_t x, const coord_t y, const int extruder_train) const { - if (use_extruder_offset_to_offset_coords) - { - const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[extruder_train].settings; - return Point(x - extruder_settings.get("machine_nozzle_offset_x"), y - extruder_settings.get("machine_nozzle_offset_y")); - } - else - { - return Point(x, y); - } + return Point(x, y) - extruder_attr[extruder_train].nozzle_offset; } - void GCodeExport::setFlavor(EGCodeFlavor flavor) { this->flavor = flavor; @@ -383,8 +386,7 @@ void GCodeExport::setFilamentDiameter(const size_t extruder, const coord_t diame double GCodeExport::getCurrentExtrudedVolume() const { double extrusion_amount = current_e_value; - const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings; - if (! extruder_settings.get("machine_firmware_retract")) + if (! extruder_attr[current_extruder].machine_firmware_retract) { // no E values are changed to perform a retraction extrusion_amount -= extruder_attr[current_extruder].retraction_e_amount_at_e_start; // subtract the increment in E which was used for the first unretraction instead of extrusion extrusion_amount += extruder_attr[current_extruder].retraction_e_amount_current; // add the decrement in E which the filament is behind on extrusion due to the last retraction @@ -832,8 +834,7 @@ void GCodeExport::writeUnretractionAndPrime() current_e_value += prime_volume_e; if (extruder_attr[current_extruder].retraction_e_amount_current) { - const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings; - if (extruder_settings.get("machine_firmware_retract")) + if (extruder_attr[current_extruder].machine_firmware_retract) { // note that BFB is handled differently *output_stream << "G11" << new_line; // Assume default UM2 retraction settings. @@ -926,8 +927,7 @@ void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bo } } - const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings; - if (extruder_settings.get("machine_firmware_retract")) + if (extruder_attr[current_extruder].machine_firmware_retract) { if (extruder_switch && extr_attr.retraction_e_amount_current) { From e476bd73dca91bcb773f9fa9cd39538925042a80 Mon Sep 17 00:00:00 2001 From: Scott Martin Date: Fri, 31 Mar 2023 20:45:50 -0400 Subject: [PATCH 002/656] Add TARGET_MACHINE.NAME header comments to indicate the machine the gcode is intended to run on --- src/gcodeExport.cpp | 1 + tests/GCodeExportTest.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 4758ec5503..9767e09cdf 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -226,6 +226,7 @@ std::string GCodeExport::getFileHeader(const std::vector& extruder_is_used break; default: prefix << ";FLAVOR:" << flavorToString(flavor) << new_line; + prefix << ";TARGET_MACHINE.NAME:" << transliterate(machine_name) << new_line; prefix << ";TIME:" << ((print_time) ? static_cast(*print_time) : 6666) << new_line; if (flavor == EGCodeFlavor::ULTIGCODE) { diff --git a/tests/GCodeExportTest.cpp b/tests/GCodeExportTest.cpp index 2fe477e021..a4d3b7e7b1 100644 --- a/tests/GCodeExportTest.cpp +++ b/tests/GCodeExportTest.cpp @@ -330,7 +330,7 @@ TEST_F(GCodeExportTest, HeaderUltiGCode) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); EXPECT_EQ(result, - ";FLAVOR:UltiGCode\n;TIME:1337\n;MATERIAL:100\n;MATERIAL2:200\n;NOZZLE_DIAMETER:0.4\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;" + ";FLAVOR:UltiGCode\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;MATERIAL:100\n;MATERIAL2:200\n;NOZZLE_DIAMETER:0.4\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;" "MAXY:1\n;MAXZ:1\n"); } @@ -349,7 +349,7 @@ TEST_F(GCodeExportTest, HeaderRepRap) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); EXPECT_EQ(result, - ";FLAVOR:RepRap\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " + ";FLAVOR:RepRap\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n"); } @@ -368,7 +368,7 @@ TEST_F(GCodeExportTest, HeaderMarlin) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); EXPECT_EQ(result, - ";FLAVOR:Marlin\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " + ";FLAVOR:Marlin\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n"); } @@ -385,7 +385,7 @@ TEST_F(GCodeExportTest, HeaderMarlinVolumetric) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); EXPECT_EQ(result, - ";FLAVOR:Marlin(Volumetric)\n;TIME:1337\n;Filament used: 100mm3, 200mm3\n;Layer height: " + ";FLAVOR:Marlin(Volumetric)\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;Filament used: 100mm3, 200mm3\n;Layer height: " "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n"); } @@ -711,4 +711,4 @@ TEST_F(GCodeExportTest, insertWipeScriptHopEnable) EXPECT_EQ(std::string(";WIPE_SCRIPT_END"), token) << "Wipe script should always end with tag."; } } // namespace cura -// NOLINTEND(*-magic-numbers) \ No newline at end of file +// NOLINTEND(*-magic-numbers) From 5db1eae3a06fbb39bb7d41625a5675d6ca455e1f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 25 Apr 2023 09:49:07 +0200 Subject: [PATCH 003/656] Initial CuraEngine plugin architecture Contributes to [CURA-10475] --- CMakeLists.txt | 7 +- conanfile.py | 4 +- include/Application.h | 6 +- include/plugins/pluginproxy.h | 110 +++++++++++++++++++++++ include/plugins/slots.h | 45 ++++++++++ include/plugins/types.h | 43 +++++++++ include/plugins/validator.h | 44 +++++++++ plugin.proto | 46 ++++++++++ src/Application.cpp | 10 +++ src/communication/ArcusCommunication.cpp | 4 +- 10 files changed, 312 insertions(+), 7 deletions(-) create mode 100644 include/plugins/pluginproxy.h create mode 100644 include/plugins/slots.h create mode 100644 include/plugins/types.h create mode 100644 include/plugins/validator.h create mode 100644 plugin.proto diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b86a3bad4..6dc9aa87da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,8 @@ if (ENABLE_ARCUS) find_package(arcus REQUIRED) find_package(protobuf REQUIRED) - protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto) + protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto plugin.proto) + protobuf_generate_cpp(plugin_PB_SRCS plugin_PB_HEADERS plugin.proto) endif () ### Compiling CuraEngine ### @@ -133,7 +134,7 @@ set(engine_SRCS # Except main.cpp. src/utils/ToolpathVisualizer.cpp src/utils/VoronoiUtils.cpp src/utils/VoxelUtils.cpp - ) + include/plugins/validator.h) add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) use_threads(_CuraEngine) @@ -178,6 +179,7 @@ find_package(spdlog REQUIRED) find_package(fmt REQUIRED) find_package(range-v3 REQUIRED) find_package(scripta REQUIRED) +find_package(neargye-semver REQUIRED) if (ENABLE_TESTING) find_package(GTest REQUIRED) @@ -193,6 +195,7 @@ target_link_libraries(_CuraEngine stb::stb boost::boost scripta::scripta + neargye-semver::neargye-semver $<$:GTest::gtest>) if (NOT WIN32) diff --git a/conanfile.py b/conanfile.py index db8f50331e..ea9ebc6896 100644 --- a/conanfile.py +++ b/conanfile.py @@ -64,7 +64,7 @@ def validate(self): def build_requirements(self): self.test_requires("standardprojectsettings/[>=0.1.0]@ultimaker/stable") if self.options.enable_arcus: - self.test_requires("protobuf/3.21.4") + self.test_requires("protobuf/3.21.9") if self.options.enable_testing: self.test_requires("gtest/1.12.1") if self.options.enable_benchmarks: @@ -72,6 +72,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: + self.requires("protobuf/3.21.9") self.requires("arcus/5.2.2") self.requires("zlib/1.2.12") self.requires("clipper/6.4.2") @@ -82,6 +83,7 @@ def requirements(self): self.requires("fmt/9.0.0") self.requires("range-v3/0.12.0") self.requires("scripta/0.1.0@ultimaker/testing") + self.requires("neargye-semver/0.3.0") def generate(self): deps = CMakeDeps(self) diff --git a/include/Application.h b/include/Application.h index 3d781f702a..eaedbc79e5 100644 --- a/include/Application.h +++ b/include/Application.h @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef APPLICATION_H #define APPLICATION_H @@ -134,6 +134,8 @@ class Application : NoCopy * This destroys the Communication instance along with it. */ ~Application(); + + void registerPlugins(); }; } //Cura namespace. diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h new file mode 100644 index 0000000000..846c4b0866 --- /dev/null +++ b/include/plugins/pluginproxy.h @@ -0,0 +1,110 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_INCLUDE_PLUGINS_PLUGINPROXY_H +#define CURAENGINE_INCLUDE_PLUGINS_PLUGINPROXY_H + +#include +#include + +#include +#include +#include +#include +#include + +#include "plugins/validator.h" + +#include "plugin.pb.h" + +namespace cura::plugins +{ +namespace detail +{ +template +class PluginListener : public Arcus::SocketListener +{ +public: + PluginListener(std::shared_ptr validator) noexcept : validator_{ validator } + { + } + + void stateChanged(Arcus::SocketState) override{}; + + void messageReceived() override + { + auto message = getSocket()->takeNextMessage(); + // Check if the message is a Version_ret and update the validator + if (message->GetDescriptor() == &plugins::proto::Version_ret::default_instance()) + { + auto* version_ret = dynamic_cast(message.get()); + validator_->version = semver::from_string(version_ret->version()); + spdlog::info("Plugin version: {}", validator_->version.to_string()); + } + + // TODO: Handle Receive and Send messages + }; + + void error(const Arcus::Error& error) override{}; + +private: + std::shared_ptr validator_{}; +}; + +} // namespace detail + +template +class PluginProxy +{ + std::unique_ptr socket_{}; + std::shared_ptr> validator_{}; + std::unique_ptr>> listener_{}; + ranges::semiregular_box projection_{}; + + PluginProxy(const std::string& ip, int port, Proj&& proj = Proj{}) noexcept + : socket_{ std::make_unique() } + , validator_{ std::make_shared>() } + , listener_{ std::make_unique(validator_) } + , projection_{ std::forward(proj) } + { + // Add the listener + socket_->addListener(listener_.get()); + + // Register all message types + socket_->registerMessageType(&plugins::proto::Version_ret::default_instance()); + socket_->registerMessageType(&Receive::default_instance()); + socket_->registerMessageType(&Send::default_instance()); + + // Connect to the plugin + spdlog::info("Connecting to plugin at {}:{}", ip, port); + socket_->connect(ip, port); + while (socket_->getState() == Arcus::SocketState::Connecting || socket_->getState() == Arcus::SocketState::Opening) + { + std::this_thread::sleep_for(std::chrono::milliseconds{ 100 }); // TODO: Fix magic number + // TODO: Add timeout??? Is this blocking even necessary? + } + + // Send request for version + auto version_args = std::make_shared(); + version_args->set_version_range(VersionRange.value); + socket_->sendMessage(version_args); + } + + auto operator()(auto&&... args) + { + if (*validator_ && socket_->getState() == Arcus::SocketState::Connected) + { + auto send = std::make_shared(std::forward(args)...); + socket_->sendMessage(send); + // TODO: Block until message is received + // TODO: Convert return message to actual return value + return projection_(std::forward(args)...); // FIXME: This is not correct + } + return projection_(std::forward(args)...); + } +}; + +} // namespace cura::plugins + + +#endif // CURAENGINE_INCLUDE_PLUGINS_PLUGINPROXY_H diff --git a/include/plugins/slots.h b/include/plugins/slots.h new file mode 100644 index 0000000000..4320e5de77 --- /dev/null +++ b/include/plugins/slots.h @@ -0,0 +1,45 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTS_H +#define CURAENGINE_INCLUDE_PLUGINS_SLOTS_H + +#include +#include + +#include "plugins/types.h" + +namespace cura::plugins +{ + +class Slots +{ + constexpr Slots() noexcept = default; + std::unordered_map> slots_{}; + +public: + Slots(const Slots&) = delete; + Slots(Slots&&) = delete; + + static Slots& instance() noexcept + { + static Slots instance{}; + return instance; + } + + /*! \brief Register a plugin to a slot + * + * \param slot The slot to register the plugin to + * \param plugin The plugin to register + */ + void registerSlot(Slot slot, std::shared_ptr plugin) noexcept + { + slots_.insert_or_assign(slot, std::move(plugin)); + } +}; + + +} // namespace cura::plugins + + +#endif // CURAENGINE_INCLUDE_PLUGINS_SLOTS_H diff --git a/include/plugins/types.h b/include/plugins/types.h new file mode 100644 index 0000000000..0b143e93b5 --- /dev/null +++ b/include/plugins/types.h @@ -0,0 +1,43 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_INCLUDE_PLUGINS_TYPES_H +#define CURAENGINE_INCLUDE_PLUGINS_TYPES_H + +#include "plugins/pluginproxy.h" +#include "utils/polygon.h" + +#include "plugin.pb.h" + +#include + +namespace cura::plugins +{ +namespace defaults +{ +constexpr auto simplify = [](const Polygons& polygons, const size_t max_deviation, const size_t max_angle) -> Polygons { return polygons; }; +constexpr auto postprocess = [](const std::string& gcode_word) -> std::string { return gcode_word; }; + +} // namespace defaults + +// Register the plugin types +// TODO: Try to determine the projections from the instantiation of the PluginProxy. +// TODO: Add custom converters for proto calls to CuraEngine types. +using simplify_plugin = PluginProxy<">=1.0.0 <2.0.0 || >3.2.1", plugins::proto::Simplify_args, plugins::proto::Simplify_ret, decltype(defaults::simplify)>; +using postproces_plugin = PluginProxy<">=1.0.0 <2.0.0 || >3.2.1", plugins::proto::Postprocess_args, plugins::proto::Postprocess_ret, decltype(defaults::postprocess)>; + +using plugin_t = std::variant; + +// Register the slots here. +enum class Slot +{ + SIMPLIFY, + POSTPROCESS +}; + + + +} // namespace cura::plugins + + +#endif // CURAENGINE_INCLUDE_PLUGINS_TYPES_H diff --git a/include/plugins/validator.h b/include/plugins/validator.h new file mode 100644 index 0000000000..ea3b578490 --- /dev/null +++ b/include/plugins/validator.h @@ -0,0 +1,44 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H +#define CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H + +#include + +namespace cura::plugins +{ + +template +struct VersionRangeLiteral +{ + constexpr VersionRangeLiteral(const char (&str)[N]) + { + std::copy_n(str, N, value); + } + + char value[N]; +}; + + +template +struct Validator +{ + semver::version version{ "1.0.0" }; + bool include_prerelease{ false }; + semver::range::detail::range version_range{ VersionRange.value }; + + constexpr operator bool() const noexcept + { + return version_range.satisfies(version, include_prerelease); + } + + constexpr bool operator()(const semver::version& actual_version) const noexcept + { + return version_range.satisfies(actual_version, include_prerelease); + } +}; + +} // namespace cura::plugins + +#endif // CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H diff --git a/plugin.proto b/plugin.proto new file mode 100644 index 0000000000..8c008709dc --- /dev/null +++ b/plugin.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +package cura.plugins.proto; + +// TODO: Add error propagation +// TODO: Add progress reporting +// TODO: Add priority queueing (if multiple plugins are hooked up to the slot) + +message Version_args { + string version_range = 1; +} + +message Version_ret { + string version = 1; +} + +message Point_2d { + sint64 x = 1; + sint64 y = 2; +} + +message Polygon { + repeated Point_2d path = 1; +} + +message Polygons { + repeated Polygon paths = 1; +} + +message Simplify_args { + Polygons polygons = 1; + uint64 max_deviation = 2; + uint64 max_angle = 3; +} + +message Simplify_ret { + Polygons polygons = 1; +} + +message Postprocess_args { + string gcode_word = 1; +} + +message Postprocess_ret { + string gcode_word = 1; +} \ No newline at end of file diff --git a/src/Application.cpp b/src/Application.cpp index c5a3a6f102..27e6a10c16 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -23,6 +23,8 @@ #include "utils/ThreadPool.h" #include "utils/string.h" //For stringcasecompare. +#include "plugins/slots.h" + namespace cura { @@ -186,6 +188,8 @@ void Application::run(const size_t argc, char** argv) exit(1); } + registerPlugins(); + #ifdef ARCUS if (stringcasecompare(argv[1], "connect") == 0) { @@ -247,4 +251,10 @@ void Application::startThreadPool(int nworkers) thread_pool = new ThreadPool(nthreads); } +void Application::registerPlugins() +{ +// plugins::Slots::instance().register("simplify", "[>=0.1.0]"); +// plugins::Slots::instance().register("postprocess", "[>=0.1.0]"); +} + } // namespace cura \ No newline at end of file diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 30786e2cce..c1686d34e0 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #ifdef ARCUS @@ -316,7 +316,7 @@ void ArcusCommunication::connect(const std::string& ip, const uint16_t port) std::this_thread::sleep_for(std::chrono::milliseconds(private_data->millisecUntilNextTry)); // Wait until we're connected. Check every XXXms. socket_state = private_data->socket->getState(); } - if (socket_state != Arcus::SocketState::Connected) + if (socket_state == Arcus::SocketState::Connected) { spdlog::info("Connected to {}:{}", ip, port); } From 27f021604d65770b27c4fb425aef67ec99b55f00 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 25 Apr 2023 18:37:34 +0200 Subject: [PATCH 004/656] Added converters to the PluginProxy [CURA-10475] --- CMakeLists.txt | 2 +- include/plugins/pluginproxy.h | 71 +++++++++++++++++------------- include/plugins/slots.h | 35 ++++++++++----- include/plugins/types.h | 83 +++++++++++++++++++++++++++-------- include/plugins/validator.h | 19 ++++---- plugin.proto | 14 ++++-- 6 files changed, 150 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dc9aa87da..7729d06e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,7 +134,7 @@ set(engine_SRCS # Except main.cpp. src/utils/ToolpathVisualizer.cpp src/utils/VoronoiUtils.cpp src/utils/VoxelUtils.cpp - include/plugins/validator.h) + ) add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) use_threads(_CuraEngine) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 846c4b0866..e9d705e38a 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,13 +19,15 @@ namespace cura::plugins { +using SlotID = proto::SlotID; + namespace detail { -template +template class PluginListener : public Arcus::SocketListener { public: - PluginListener(std::shared_ptr validator) noexcept : validator_{ validator } + PluginListener(std::shared_ptr plugin) noexcept : plugin_{ plugin } { } @@ -34,46 +36,53 @@ class PluginListener : public Arcus::SocketListener void messageReceived() override { auto message = getSocket()->takeNextMessage(); - // Check if the message is a Version_ret and update the validator - if (message->GetDescriptor() == &plugins::proto::Version_ret::default_instance()) + + if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") { - auto* version_ret = dynamic_cast(message.get()); - validator_->version = semver::from_string(version_ret->version()); - spdlog::info("Plugin version: {}", validator_->version.to_string()); + auto* plugin_ret = dynamic_cast(message.get()); + plugin_->validator->version = semver::from_string( plugin_ret->version()); + plugin_->validator->plugin_hash = plugin_ret->plugin_hash(); + spdlog::info("Plugin version: {}", plugin_->validator->version.to_string()); + } + else if (message->GetTypeName() == Plugin::receive_t::default_instance().GetTypeName()) + { + // unblock plugin } - - // TODO: Handle Receive and Send messages }; void error(const Arcus::Error& error) override{}; private: - std::shared_ptr validator_{}; + std::shared_ptr plugin_{}; }; } // namespace detail -template +template class PluginProxy { +public: + using receive_t = Converter::receive_t; + using send_t = Converter::send_t; + using validator_t = Validator; + using plugin_t = PluginProxy; + static constexpr SlotID slot_id{ Slot }; + std::unique_ptr socket_{}; - std::shared_ptr> validator_{}; - std::unique_ptr>> listener_{}; - ranges::semiregular_box projection_{}; + validator_t validator{}; + std::unique_ptr> listener_{}; + Converter converter{ }; - PluginProxy(const std::string& ip, int port, Proj&& proj = Proj{}) noexcept + PluginProxy(const std::string& ip, int port) noexcept : socket_{ std::make_unique() } - , validator_{ std::make_shared>() } - , listener_{ std::make_unique(validator_) } - , projection_{ std::forward(proj) } + , listener_{ std::make_unique(std::make_shared(this)) } { // Add the listener socket_->addListener(listener_.get()); - // Register all message types - socket_->registerMessageType(&plugins::proto::Version_ret::default_instance()); - socket_->registerMessageType(&Receive::default_instance()); - socket_->registerMessageType(&Send::default_instance()); + // Register all receiving message types + socket_->registerMessageType(&plugins::proto::Plugin_ret::default_instance()); + socket_->registerMessageType(&receive_t::default_instance()); // Connect to the plugin spdlog::info("Connecting to plugin at {}:{}", ip, port); @@ -84,23 +93,23 @@ class PluginProxy // TODO: Add timeout??? Is this blocking even necessary? } - // Send request for version - auto version_args = std::make_shared(); - version_args->set_version_range(VersionRange.value); - socket_->sendMessage(version_args); + // TODO: Use Plugin Message instead of Version_args + auto plugin_args = std::make_shared(); + plugin_args->set_id(slot_id); + socket_->sendMessage(plugin_args); + // No need to wait for the response, since the listener will set the version } auto operator()(auto&&... args) { - if (*validator_ && socket_->getState() == Arcus::SocketState::Connected) + if (validator && socket_->getState() == Arcus::SocketState::Connected) { - auto send = std::make_shared(std::forward(args)...); - socket_->sendMessage(send); + socket_->sendMessage(converter(std::forward(args)...)); // TODO: Block until message is received // TODO: Convert return message to actual return value - return projection_(std::forward(args)...); // FIXME: This is not correct + return 1; // FIXME: This is not correct } - return projection_(std::forward(args)...); + return 1; // FIXME: handle plugin not connected } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 4320e5de77..7a2328de31 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -6,16 +6,26 @@ #include #include +#include +#include "plugins/pluginproxy.h" +#include "plugins/validator.h" #include "plugins/types.h" namespace cura::plugins { +using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "">, details::simplify_converter_fn>; +using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "">, details::postprocess_converter_fn>; + + +using plugins_t = std::variant; + + class Slots { constexpr Slots() noexcept = default; - std::unordered_map> slots_{}; + std::unordered_map slots_{}; public: Slots(const Slots&) = delete; @@ -27,15 +37,20 @@ class Slots return instance; } - /*! \brief Register a plugin to a slot - * - * \param slot The slot to register the plugin to - * \param plugin The plugin to register - */ - void registerSlot(Slot slot, std::shared_ptr plugin) noexcept - { - slots_.insert_or_assign(slot, std::move(plugin)); - } + + +// template +// constexpr void registerSlot(Slot&& slot) +// { +// slots_.emplace(slot.slot_id, std::forward(slot)); +// } +// +// +// simplify_slot getSlot() +// { +// return std::get(slots_.at(SlotID::SIMPLIFY)); +// } + }; diff --git a/include/plugins/types.h b/include/plugins/types.h index 0b143e93b5..0e654b18f8 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -4,37 +4,84 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_TYPES_H #define CURAENGINE_INCLUDE_PLUGINS_TYPES_H -#include "plugins/pluginproxy.h" +#include + +#include + +#include "utils/IntPoint.h" #include "utils/polygon.h" #include "plugin.pb.h" -#include - namespace cura::plugins { -namespace defaults +namespace details { -constexpr auto simplify = [](const Polygons& polygons, const size_t max_deviation, const size_t max_angle) -> Polygons { return polygons; }; -constexpr auto postprocess = [](const std::string& gcode_word) -> std::string { return gcode_word; }; - -} // namespace defaults +template +struct converter_base +{ + using send_t = Send; + using receive_t = Receive; +}; -// Register the plugin types -// TODO: Try to determine the projections from the instantiation of the PluginProxy. -// TODO: Add custom converters for proto calls to CuraEngine types. -using simplify_plugin = PluginProxy<">=1.0.0 <2.0.0 || >3.2.1", plugins::proto::Simplify_args, plugins::proto::Simplify_ret, decltype(defaults::simplify)>; -using postproces_plugin = PluginProxy<">=1.0.0 <2.0.0 || >3.2.1", plugins::proto::Postprocess_args, plugins::proto::Postprocess_ret, decltype(defaults::postprocess)>; +template +struct simplify_converter_fn : public converter_base +{ + Polygons operator()(const Receive& args) const noexcept + { + Polygons poly{}; + for (const auto& paths : args.polygons().paths() ) + { + Polygon p{}; + for (const auto& point : paths.path() ) + { + p.add(Point { point.y(), point.y() }); + } + poly.add(p); + } + return poly; + } -using plugin_t = std::variant; + auto operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const noexcept + { + Send args{}; + args.set_max_deviation(max_deviation); + args.set_max_angle(max_angle); + for (const auto& polygon : polygons.paths ) + { + auto poly = args.polygons(); + for (const auto& path : polygons.paths) + { + auto* p = poly.add_paths(); + for (const auto& point : path ) + { + auto* pt = p->add_path(); + pt->set_x(point.X); + pt->set_y(point.Y); + } + } + } + return std::make_shared(args); + } +}; -// Register the slots here. -enum class Slot +template +struct postprocess_converter_fn : public converter_base { - SIMPLIFY, - POSTPROCESS + std::string operator()(const Receive& args) const noexcept + { + return args.gcode(); + } + + auto operator()(const std::string& gcode) const noexcept + { + Send args{}; + args.set_gcode(gcode); + return std::make_shared(args); + } }; +} // namespace details } // namespace cura::plugins diff --git a/include/plugins/validator.h b/include/plugins/validator.h index ea3b578490..f9fd18d28a 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -8,11 +8,12 @@ namespace cura::plugins { - +namespace details +{ template -struct VersionRangeLiteral +struct CharRangeLiteral { - constexpr VersionRangeLiteral(const char (&str)[N]) + constexpr CharRangeLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } @@ -20,22 +21,20 @@ struct VersionRangeLiteral char value[N]; }; +} // namespace details -template +template struct Validator { semver::version version{ "1.0.0" }; + std::string_view plugin_hash{ }; bool include_prerelease{ false }; semver::range::detail::range version_range{ VersionRange.value }; constexpr operator bool() const noexcept { - return version_range.satisfies(version, include_prerelease); - } - - constexpr bool operator()(const semver::version& actual_version) const noexcept - { - return version_range.satisfies(actual_version, include_prerelease); + // TODO: Add proper security checking + return version_range.satisfies(version, include_prerelease) && plugin_hash == PluginHash.value; } }; diff --git a/plugin.proto b/plugin.proto index 8c008709dc..e3122b14e8 100644 --- a/plugin.proto +++ b/plugin.proto @@ -6,12 +6,18 @@ package cura.plugins.proto; // TODO: Add progress reporting // TODO: Add priority queueing (if multiple plugins are hooked up to the slot) -message Version_args { - string version_range = 1; +enum SlotID { + SIMPLIFY = 0; + POSTPROCESS = 1; } -message Version_ret { - string version = 1; +message Plugin_args { + SlotID id = 1; +} + +message Plugin_ret { + string plugin_hash = 1; + string version = 2; } message Point_2d { From a6ff6a494400ec98ac66187dc566af74fbd2ce60 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 26 Apr 2023 08:02:04 +0200 Subject: [PATCH 005/656] Add documentation Contributes to [CURA-10475] --- plugin.proto | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugin.proto b/plugin.proto index e3122b14e8..0632036ec2 100644 --- a/plugin.proto +++ b/plugin.proto @@ -2,24 +2,32 @@ syntax = "proto3"; package cura.plugins.proto; +// TODO: Move proto defintions to a separate repo // TODO: Add error propagation // TODO: Add progress reporting // TODO: Add priority queueing (if multiple plugins are hooked up to the slot) + +// These are the different slots that plugins can hook into enum SlotID { SIMPLIFY = 0; POSTPROCESS = 1; } +// --------------------------------------------------------------------- +// This is the message which is sent to the plugin to request identification message Plugin_args { SlotID id = 1; } +// This is the message which is sent back to CuraEngine to identify and validate the plugin message Plugin_ret { string plugin_hash = 1; string version = 2; } +// --------------------------------------------------------------------- +// Some basic types that are used in the messages below message Point_2d { sint64 x = 1; sint64 y = 2; @@ -33,20 +41,26 @@ message Polygons { repeated Polygon paths = 1; } +// --------------------------------------------------------------------- +// The SIMPLIFY slot request message message Simplify_args { Polygons polygons = 1; uint64 max_deviation = 2; uint64 max_angle = 3; } +// The SIMPLIFY slot response message message Simplify_ret { Polygons polygons = 1; } +// --------------------------------------------------------------------- +// The POSTPROCESS slot request message message Postprocess_args { string gcode_word = 1; } +// The POSTPROCESS slot response message message Postprocess_ret { string gcode_word = 1; } \ No newline at end of file From 2e800c4f2891e8ecdb41f7c185ae86c943c81a25 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 26 Apr 2023 08:04:20 +0200 Subject: [PATCH 006/656] Add hashes Just some fake hash to play around with [CURA-10475] --- include/plugins/slots.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 7a2328de31..6e0125dbec 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -9,14 +9,14 @@ #include #include "plugins/pluginproxy.h" -#include "plugins/validator.h" #include "plugins/types.h" +#include "plugins/validator.h" namespace cura::plugins { -using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "">, details::simplify_converter_fn>; -using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "">, details::postprocess_converter_fn>; +using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, details::simplify_converter_fn>; +using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, details::postprocess_converter_fn>; using plugins_t = std::variant; From ef8de6d4883d89836611ac52c3b2482334ee35ad Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 26 Apr 2023 08:40:08 +0200 Subject: [PATCH 007/656] Cleaned up some type aliases and namespaces [CURA-10475] --- include/plugins/pluginproxy.h | 43 +++++++++++++++++++---------------- include/plugins/slots.h | 4 ++-- include/plugins/types.h | 29 +++++++++++++++-------- include/plugins/validator.h | 2 +- 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index e9d705e38a..a957bcc2b6 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -23,11 +23,14 @@ using SlotID = proto::SlotID; namespace detail { -template +template class PluginListener : public Arcus::SocketListener { public: - PluginListener(std::shared_ptr plugin) noexcept : plugin_{ plugin } + using validator_t = Validator; + using receive_t = Receive; + + PluginListener(std::shared_ptr validator) noexcept : validator_{ validator_ } { } @@ -40,11 +43,11 @@ class PluginListener : public Arcus::SocketListener if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") { auto* plugin_ret = dynamic_cast(message.get()); - plugin_->validator->version = semver::from_string( plugin_ret->version()); - plugin_->validator->plugin_hash = plugin_ret->plugin_hash(); - spdlog::info("Plugin version: {}", plugin_->validator->version.to_string()); + validator_->version = semver::from_string(plugin_ret->version()); + validator_->plugin_hash = plugin_ret->plugin_hash(); + spdlog::info("Plugin version: {}", validator_->version.to_string()); } - else if (message->GetTypeName() == Plugin::receive_t::default_instance().GetTypeName()) + else if (message->GetTypeName() == receive_t::default_instance().GetTypeName()) { // unblock plugin } @@ -53,7 +56,7 @@ class PluginListener : public Arcus::SocketListener void error(const Arcus::Error& error) override{}; private: - std::shared_ptr plugin_{}; + std::shared_ptr validator_{}; }; } // namespace detail @@ -62,23 +65,28 @@ template class PluginProxy { public: + // type aliases for easy use using receive_t = Converter::receive_t; using send_t = Converter::send_t; using validator_t = Validator; + using converter_t = Converter; using plugin_t = PluginProxy; + using listener_t = detail::PluginListener; + static constexpr SlotID slot_id{ Slot }; + std::shared_ptr validator{}; + converter_t converter{}; + +private: std::unique_ptr socket_{}; - validator_t validator{}; - std::unique_ptr> listener_{}; - Converter converter{ }; + std::shared_ptr listener_{}; - PluginProxy(const std::string& ip, int port) noexcept - : socket_{ std::make_unique() } - , listener_{ std::make_unique(std::make_shared(this)) } +public: + PluginProxy(const std::string& ip, int port) : socket_{ std::make_unique() }, listener_{ std::make_shared(validator) } { // Add the listener - socket_->addListener(listener_.get()); + socket_->addListener(listener_); // Register all receiving message types socket_->registerMessageType(&plugins::proto::Plugin_ret::default_instance()); @@ -93,10 +101,7 @@ class PluginProxy // TODO: Add timeout??? Is this blocking even necessary? } - // TODO: Use Plugin Message instead of Version_args - auto plugin_args = std::make_shared(); - plugin_args->set_id(slot_id); - socket_->sendMessage(plugin_args); + socket_->sendMessage(converter(slot_id)); // No need to wait for the response, since the listener will set the version } @@ -109,7 +114,7 @@ class PluginProxy // TODO: Convert return message to actual return value return 1; // FIXME: This is not correct } - return 1; // FIXME: handle plugin not connected + return 1; // FIXME: handle plugin not connected } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 6e0125dbec..34a386d48d 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -15,8 +15,8 @@ namespace cura::plugins { -using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, details::simplify_converter_fn>; -using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, details::postprocess_converter_fn>; +using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::simplify_converter_fn>; +using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::postprocess_converter_fn>; using plugins_t = std::variant; diff --git a/include/plugins/types.h b/include/plugins/types.h index 0e654b18f8..db57bfec9d 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -5,8 +5,7 @@ #define CURAENGINE_INCLUDE_PLUGINS_TYPES_H #include - -#include +#include #include "utils/IntPoint.h" #include "utils/polygon.h" @@ -15,13 +14,25 @@ namespace cura::plugins { -namespace details +namespace converters { template struct converter_base { using send_t = Send; using receive_t = Receive; + + auto operator()(const SlotID slot_id) + { + proto::Plugin_args args{}; + args.set_id(slot_id); + return std::make_shared(args); + } + + std::tuple operator()(const proto::Plugin_ret& args) + { + return { args.version(), args.plugin_hash() }; + } }; template @@ -30,12 +41,12 @@ struct simplify_converter_fn : public converter_base Polygons operator()(const Receive& args) const noexcept { Polygons poly{}; - for (const auto& paths : args.polygons().paths() ) + for (const auto& paths : args.polygons().paths()) { Polygon p{}; - for (const auto& point : paths.path() ) + for (const auto& point : paths.path()) { - p.add(Point { point.y(), point.y() }); + p.add(Point{ point.y(), point.y() }); } poly.add(p); } @@ -47,13 +58,13 @@ struct simplify_converter_fn : public converter_base Send args{}; args.set_max_deviation(max_deviation); args.set_max_angle(max_angle); - for (const auto& polygon : polygons.paths ) + for (const auto& polygon : polygons.paths) { auto poly = args.polygons(); for (const auto& path : polygons.paths) { auto* p = poly.add_paths(); - for (const auto& point : path ) + for (const auto& point : path) { auto* pt = p->add_path(); pt->set_x(point.X); @@ -81,7 +92,7 @@ struct postprocess_converter_fn : public converter_base } }; -} // namespace details +} // namespace converters } // namespace cura::plugins diff --git a/include/plugins/validator.h b/include/plugins/validator.h index f9fd18d28a..f1e1e4d249 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -27,7 +27,7 @@ template Date: Wed, 26 Apr 2023 08:41:44 +0200 Subject: [PATCH 008/656] Moved CharLiteral to types [CURA-10475] --- include/plugins/types.h | 15 +++++++++++++++ include/plugins/validator.h | 15 ++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/include/plugins/types.h b/include/plugins/types.h index db57bfec9d..d4b181316b 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -14,6 +14,21 @@ namespace cura::plugins { +namespace details +{ +template +struct CharRangeLiteral +{ + constexpr CharRangeLiteral(const char (&str)[N]) + { + std::copy_n(str, N, value); + } + + char value[N]; +}; + +} // namespace details + namespace converters { template diff --git a/include/plugins/validator.h b/include/plugins/validator.h index f1e1e4d249..48ff6e8b69 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -4,24 +4,13 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H #define CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H +#include "plugins/types.h" + #include namespace cura::plugins { -namespace details -{ -template -struct CharRangeLiteral -{ - constexpr CharRangeLiteral(const char (&str)[N]) - { - std::copy_n(str, N, value); - } - - char value[N]; -}; -} // namespace details template struct Validator From 4c5fe4370f533718ab581e0a41ca74b0edcbf326 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 26 Apr 2023 09:23:31 +0200 Subject: [PATCH 009/656] Listener uses converter [CURA-10475] --- include/plugins/pluginproxy.h | 20 +++++++++++--------- include/plugins/types.h | 2 ++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index a957bcc2b6..119f309c86 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -13,22 +13,23 @@ #include #include +#include "plugins/types.h" #include "plugins/validator.h" #include "plugin.pb.h" namespace cura::plugins { -using SlotID = proto::SlotID; namespace detail { -template +template class PluginListener : public Arcus::SocketListener { public: using validator_t = Validator; using receive_t = Receive; + using converter_t = Converter; PluginListener(std::shared_ptr validator) noexcept : validator_{ validator_ } { @@ -42,9 +43,9 @@ class PluginListener : public Arcus::SocketListener if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") { - auto* plugin_ret = dynamic_cast(message.get()); - validator_->version = semver::from_string(plugin_ret->version()); - validator_->plugin_hash = plugin_ret->plugin_hash(); + auto [version, plugin_hash] = converter_(*message); + validator_->version = semver::from_string(version); + validator_->plugin_hash = plugin_hash; spdlog::info("Plugin version: {}", validator_->version.to_string()); } else if (message->GetTypeName() == receive_t::default_instance().GetTypeName()) @@ -57,6 +58,7 @@ class PluginListener : public Arcus::SocketListener private: std::shared_ptr validator_{}; + converter_t converter_{}; }; } // namespace detail @@ -71,16 +73,16 @@ class PluginProxy using validator_t = Validator; using converter_t = Converter; using plugin_t = PluginProxy; - using listener_t = detail::PluginListener; + using listener_t = detail::PluginListener; static constexpr SlotID slot_id{ Slot }; std::shared_ptr validator{}; - converter_t converter{}; private: std::unique_ptr socket_{}; std::shared_ptr listener_{}; + converter_t converter_{}; public: PluginProxy(const std::string& ip, int port) : socket_{ std::make_unique() }, listener_{ std::make_shared(validator) } @@ -101,7 +103,7 @@ class PluginProxy // TODO: Add timeout??? Is this blocking even necessary? } - socket_->sendMessage(converter(slot_id)); + socket_->sendMessage(converter_(slot_id)); // No need to wait for the response, since the listener will set the version } @@ -109,7 +111,7 @@ class PluginProxy { if (validator && socket_->getState() == Arcus::SocketState::Connected) { - socket_->sendMessage(converter(std::forward(args)...)); + socket_->sendMessage(converter_(std::forward(args)...)); // TODO: Block until message is received // TODO: Convert return message to actual return value return 1; // FIXME: This is not correct diff --git a/include/plugins/types.h b/include/plugins/types.h index d4b181316b..c15f8dfb18 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -14,6 +14,8 @@ namespace cura::plugins { +using SlotID = proto::SlotID; + namespace details { template From 7924542b52fb14548032944e2bb7b626b85d3b14 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 1 May 2023 11:47:00 +0200 Subject: [PATCH 010/656] Add converter Still WIP and doesn't compile due to the overloading of the operator() in child class. [CURA-10475] --- include/plugins/pluginproxy.h | 88 +++++++++++++++++++++++------------ include/plugins/slots.h | 4 ++ include/plugins/types.h | 53 +++++++++++++-------- src/Application.cpp | 2 + 4 files changed, 98 insertions(+), 49 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 119f309c86..d50261beb1 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -23,15 +23,28 @@ namespace cura::plugins namespace detail { -template +template class PluginListener : public Arcus::SocketListener { public: using validator_t = Validator; - using receive_t = Receive; + using receive_t = Converter::receive_t; using converter_t = Converter; - PluginListener(std::shared_ptr validator) noexcept : validator_{ validator_ } + bool msgReceived() noexcept + { + bool msg_received = msg_received_; + msg_received_ = false; + return msg_received; + } + +private: + std::shared_ptr validator_{}; + converter_t converter_{}; + bool msg_received_{ false }; + +public: + explicit PluginListener(std::shared_ptr validator) noexcept : validator_{ validator_ } { } @@ -39,31 +52,29 @@ class PluginListener : public Arcus::SocketListener void messageReceived() override { - auto message = getSocket()->takeNextMessage(); - - if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") - { - auto [version, plugin_hash] = converter_(*message); - validator_->version = semver::from_string(version); - validator_->plugin_hash = plugin_hash; - spdlog::info("Plugin version: {}", validator_->version.to_string()); - } - else if (message->GetTypeName() == receive_t::default_instance().GetTypeName()) - { - // unblock plugin - } +// auto message = getSocket()->takeNextMessage(); +// +// if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") +// { +// auto* msg = dynamic_cast(message.get()); +// cura::plugins::proto::Plugin_ret mm {}; +// auto x = converter_(mm); +//// validator_->version = semver::from_string(version); +//// validator_->plugin_hash = plugin_hash; +// spdlog::info("Plugin version: {}", validator_->version.to_string()); +// } +// else if (message->GetTypeName() == receive_t::default_instance().GetTypeName()) +// { +// // unblock plugin +// } }; void error(const Arcus::Error& error) override{}; - -private: - std::shared_ptr validator_{}; - converter_t converter_{}; }; } // namespace detail -template +template class PluginProxy { public: @@ -72,20 +83,19 @@ class PluginProxy using send_t = Converter::send_t; using validator_t = Validator; using converter_t = Converter; - using plugin_t = PluginProxy; - using listener_t = detail::PluginListener; + using listener_t = detail::PluginListener; - static constexpr SlotID slot_id{ Slot }; + plugins::SlotID slot_id{ Slot }; - std::shared_ptr validator{}; + std::shared_ptr validator; private: - std::unique_ptr socket_{}; - std::shared_ptr listener_{}; + std::unique_ptr socket_; + listener_t* listener_; // FIXME: in Arcus use smart_ptr for listeners otherwise we need to add a deconstructor in this class and that forces us to define the big 6 converter_t converter_{}; public: - PluginProxy(const std::string& ip, int port) : socket_{ std::make_unique() }, listener_{ std::make_shared(validator) } + PluginProxy(const std::string& ip, int port) : validator{ std::make_shared() }, socket_{ std::make_unique() }, listener_{ new listener_t(validator) } { // Add the listener socket_->addListener(listener_); @@ -103,7 +113,27 @@ class PluginProxy // TODO: Add timeout??? Is this blocking even necessary? } - socket_->sendMessage(converter_(slot_id)); + converters::simplify_converter_fn convvvvv{}; + + SlotID slotId { SlotID::SIMPLIFY }; + auto x = convvvvv(slotId); + + proto::Plugin_args args{}; + args.set_id(slot_id); + + socket_->sendMessage(std::make_shared(args)); + if (listener_->msgReceived()) + { + auto message = socket_->takeNextMessage(); + if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") + { + auto* msg = dynamic_cast(message.get()); + validator->version = semver::from_string(msg->version()); + validator->plugin_hash = msg->plugin_hash(); + spdlog::info("Plugin version: {}", validator->version.to_string()); + } + } + // // No need to wait for the response, since the listener will set the version } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 34a386d48d..7cb8aadd03 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -37,6 +37,10 @@ class Slots return instance; } + void registerPlugin(auto&& plugin) + { + slots_.emplace(plugin.slot_id, std::forward(plugin)); + } // template diff --git a/include/plugins/types.h b/include/plugins/types.h index c15f8dfb18..019ab26038 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -4,6 +4,7 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_TYPES_H #define CURAENGINE_INCLUDE_PLUGINS_TYPES_H +#include #include #include @@ -33,29 +34,37 @@ struct CharRangeLiteral namespace converters { -template -struct converter_base +template +class converter_base { - using send_t = Send; - using receive_t = Receive; - - auto operator()(const SlotID slot_id) + friend T; +public: + auto operator()(auto& arg, auto&&... args) { - proto::Plugin_args args{}; - args.set_id(slot_id); - return std::make_shared(args); - } - - std::tuple operator()(const proto::Plugin_ret& args) - { - return { args.version(), args.plugin_hash() }; + if constexpr (std::is_same_v) + { + proto::Plugin_args msg{}; + msg.set_id(arg); + return std::make_shared(msg); + } + else if constexpr (std::is_same_v) + { + return std::tuple{ arg.version(), arg.plugin_hash() }; + } + return 1; + //return static_cast(*this).make(arg, std::forward(args)...); } }; template -struct simplify_converter_fn : public converter_base +class simplify_converter_fn : public converter_base> { - Polygons operator()(const Receive& args) const noexcept +public: + using receive_t = Receive; + using send_t = Send; + +private: + Polygons make(const Receive& args) { Polygons poly{}; for (const auto& paths : args.polygons().paths()) @@ -70,7 +79,7 @@ struct simplify_converter_fn : public converter_base return poly; } - auto operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const noexcept + std::shared_ptr make(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) { Send args{}; args.set_max_deviation(max_deviation); @@ -94,14 +103,18 @@ struct simplify_converter_fn : public converter_base }; template -struct postprocess_converter_fn : public converter_base +class postprocess_converter_fn : public converter_base> { - std::string operator()(const Receive& args) const noexcept +public: + using receive_t = Receive; + using send_t = Send; + + std::string make(const Receive& args) { return args.gcode(); } - auto operator()(const std::string& gcode) const noexcept + std::shared_ptr make(const std::string& gcode) { Send args{}; args.set_gcode(gcode); diff --git a/src/Application.cpp b/src/Application.cpp index 27e6a10c16..1808317f78 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -253,6 +253,8 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { + auto simplifyPlugin = plugins::simplify_plugin { "127.0.0.1", 50010 }; +// plugins::Slots::instance().addPlugin(plugins::simplify_plugin{"127.0.0.1", 50010}); // plugins::Slots::instance().register("simplify", "[>=0.1.0]"); // plugins::Slots::instance().register("postprocess", "[>=0.1.0]"); } From d8dc2beb5a0874816005c1bc601564be7b555846 Mon Sep 17 00:00:00 2001 From: thecampbells384 Date: Fri, 5 May 2023 16:20:40 -0700 Subject: [PATCH 011/656] should avoid calculating comb boundary if mesh is infill OR anti_overhang, not if mesh is infill AND anti_overhang. --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 43def5f5db..c390457cbc 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -215,7 +215,7 @@ Polygons LayerPlan::computeCombBoundary(const CombBoundary boundary_type) { const SliceLayer& layer = mesh.layers[static_cast(layer_nr)]; // don't process infill_mesh or anti_overhang_mesh - if (mesh.settings.get("infill_mesh") && mesh.settings.get("anti_overhang_mesh")) + if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; } From 95e79a1cb6bda889cb2cb138e59121d1485ec92c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 12 May 2023 11:48:29 +0200 Subject: [PATCH 012/656] Make it compile on Win64+VS2022 part of CURA-10475 --- include/plugins/types.h | 2 +- src/InsetOrderOptimizer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/plugins/types.h b/include/plugins/types.h index 019ab26038..8decaa7ef0 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -51,7 +51,7 @@ class converter_base { return std::tuple{ arg.version(), arg.plugin_hash() }; } - return 1; + return std::make_shared(); //return static_cast(*this).make(arg, std::forward(args)...); } }; diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 7129ec2d52..e080f43989 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -14,8 +14,8 @@ #include #include +#include #include -#include #include #include #include From 97214493d1b412223069a003bd76d458037454aa Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 09:27:46 +0200 Subject: [PATCH 013/656] Write get/set for plugin in slot This currently fails compiling due to the unique_ptr of libArcus. But if we decide to go with Asio that shouldn't be a problem [CURA-10475] --- include/plugins/pluginproxy.h | 2 +- include/plugins/slots.h | 28 +++++++++------------------- src/Application.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index d50261beb1..87ec316db4 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -85,7 +85,7 @@ class PluginProxy using converter_t = Converter; using listener_t = detail::PluginListener; - plugins::SlotID slot_id{ Slot }; + static inline constexpr plugins::SlotID slot_id{ Slot }; std::shared_ptr validator; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 7cb8aadd03..8e1298b002 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -5,6 +5,7 @@ #define CURAENGINE_INCLUDE_PLUGINS_SLOTS_H #include +#include #include #include @@ -18,7 +19,6 @@ namespace cura::plugins using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::simplify_converter_fn>; using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::postprocess_converter_fn>; - using plugins_t = std::variant; @@ -37,28 +37,18 @@ class Slots return instance; } - void registerPlugin(auto&& plugin) + template + constexpr void set(T&& plugin) { - slots_.emplace(plugin.slot_id, std::forward(plugin)); + slots_.emplace(T::slot_id, std::forward(plugin)); } - -// template -// constexpr void registerSlot(Slot&& slot) -// { -// slots_.emplace(slot.slot_id, std::forward(slot)); -// } -// -// -// simplify_slot getSlot() -// { -// return std::get(slots_.at(SlotID::SIMPLIFY)); -// } - + template + constexpr auto get() const + { + return std::get(slots_.at(T::slot_id)); + } }; - - } // namespace cura::plugins - #endif // CURAENGINE_INCLUDE_PLUGINS_SLOTS_H diff --git a/src/Application.cpp b/src/Application.cpp index 1808317f78..56c3175a54 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -253,10 +253,10 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { - auto simplifyPlugin = plugins::simplify_plugin { "127.0.0.1", 50010 }; -// plugins::Slots::instance().addPlugin(plugins::simplify_plugin{"127.0.0.1", 50010}); -// plugins::Slots::instance().register("simplify", "[>=0.1.0]"); -// plugins::Slots::instance().register("postprocess", "[>=0.1.0]"); + // TODO: remove this + plugins::Slots::instance().set({ "127.0.0.1", 50010 }); + auto x = plugins::Slots::instance().get(); + auto y = x(); } } // namespace cura \ No newline at end of file From a991682756ba32141f83b4a907d397ed47d772f2 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Mon, 15 May 2023 10:06:41 +0200 Subject: [PATCH 014/656] W.I.P. on splitting converters to send/receive. Push now to prevent conflict. [Engine Plugin WIP]. part of CURA-10475 --- include/plugins/pluginproxy.h | 28 +++++++++++++++------------- include/plugins/types.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 87ec316db4..0b08cb7b0b 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -23,13 +23,12 @@ namespace cura::plugins namespace detail { -template +template class PluginListener : public Arcus::SocketListener { public: using validator_t = Validator; - using receive_t = Converter::receive_t; - using converter_t = Converter; + using receive_t = Receiver; bool msgReceived() noexcept { @@ -40,7 +39,7 @@ class PluginListener : public Arcus::SocketListener private: std::shared_ptr validator_{}; - converter_t converter_{}; + receive_t receiver_{}; bool msg_received_{ false }; public: @@ -74,28 +73,31 @@ class PluginListener : public Arcus::SocketListener } // namespace detail -template -class PluginProxy +template +class SlotProxy { public: // type aliases for easy use - using receive_t = Converter::receive_t; - using send_t = Converter::send_t; + using receive_t = Receiver; + using send_t = Sender; using validator_t = Validator; - using converter_t = Converter; - using listener_t = detail::PluginListener; + using listener_t = detail::PluginListener; static inline constexpr plugins::SlotID slot_id{ Slot }; - std::shared_ptr validator; + //std::shared_ptr validator; private: std::unique_ptr socket_; listener_t* listener_; // FIXME: in Arcus use smart_ptr for listeners otherwise we need to add a deconstructor in this class and that forces us to define the big 6 - converter_t converter_{}; + receiver_t receiver_{}; + sender_t sender_{}; public: - PluginProxy(const std::string& ip, int port) : validator{ std::make_shared() }, socket_{ std::make_unique() }, listener_{ new listener_t(validator) } + SlotProxy(const std::string& ip, int port) : + validator{ std::make_shared() }, + socket_{ std::make_unique() }, + listener_{ new listener_t(validator) } { // Add the listener socket_->addListener(listener_); diff --git a/include/plugins/types.h b/include/plugins/types.h index 8decaa7ef0..b1ef0cf946 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -34,6 +34,36 @@ struct CharRangeLiteral namespace converters { + +template +class ReceiveConverterBase +{ + friend T; +public: + auto operator()(const proto::Plugin_ret& message) + { + return std::tuple{ arg.version(), arg.plugin_hash() }; + } +}; + +template +class SendConverterBase +{ + friend T +public: + auto operator()(const cura::plugins::proto::SlotID& slot_id, auto&&... args) + { + proto::Plugin_args msg{}; + msg.set_id(arg); + return std::make_shared(msg); + } +}; + +//class SimplifyReceiveConverter : ReceiveConverterBase<> +//{ +// +//}; + template class converter_base { From eea59300b1c4ff006be759e340e2e5c47c52ba0b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 10:26:35 +0200 Subject: [PATCH 015/656] Add support for asio-grpc and update protobuf usage - Add asio-grpc to requirements and link it to _CuraEngine - Generate plugin types using asio_grpc_protobuf_generate - Update protobuf usage in conanfile.py and CMakeLists.txt - Update plugin.proto to use services and rpc calls [CURA-10475] --- CMakeLists.txt | 25 ++++++++++++++++++------- conanfile.py | 17 ++++++++++++----- plugin.proto | 26 ++++++++++++++++++++------ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36135a78ab..1a7a390ad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,14 +13,22 @@ option(EXTENSIVE_WARNINGS "Build with all warnings" ON) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) -# Create Protobuf files if Arcus is used + +# Generate the plugin types +find_package(protobuf REQUIRED) +asio_grpc_protobuf_generate( + GENERATE_GRPC GENERATE_MOCK_CODE + OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" + OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" + IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}" + PROTOS "${CMAKE_CURRENT_LIST_DIR}/plugin.proto" +) + + if (ENABLE_ARCUS) message(STATUS "Building with Arcus") - find_package(arcus REQUIRED) - find_package(protobuf REQUIRED) - protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto plugin.proto) - protobuf_generate_cpp(plugin_PB_SRCS plugin_PB_HEADERS plugin.proto) + protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto) endif () ### Compiling CuraEngine ### @@ -137,7 +145,7 @@ set(engine_SRCS # Except main.cpp. src/utils/VoxelUtils.cpp ) -add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) +add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS} ${ASIO_GRPC_PLUGIN_PROTO_SOURCES}) use_threads(_CuraEngine) target_include_directories(_CuraEngine @@ -146,7 +154,9 @@ target_include_directories(_CuraEngine $ PRIVATE $ # Include Cura.pb.h + $ # Include generated plugin types ) + target_compile_definitions(_CuraEngine PUBLIC $<$:ARCUS> @@ -197,6 +207,7 @@ target_link_libraries(_CuraEngine boost::boost scripta::scripta neargye-semver::neargye-semver + asio-grpc::asio-grpc $<$:GTest::gtest>) if (NOT WIN32) @@ -226,4 +237,4 @@ endif () if (ENABLE_BENCHMARKS) add_subdirectory(benchmark) -endif() \ No newline at end of file +endif () \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index ea9ebc6896..6c6e3fd90e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -50,9 +50,15 @@ def export_sources(self): def configure(self): self.options["boost"].header_only = True self.options["clipper"].shared = True + self.options["protobuf"].shared = True + self.options["grpc"].csharp_plugin = False + self.options["grpc"].node_plugin = False + self.options["grpc"].objective_c_plugin = False + self.options["grpc"].php_plugin = False + self.options["grpc"].python_plugin = False + self.options["grpc"].ruby_plugin = False if self.options.enable_arcus: self.options["arcus"].shared = True - self.options["protobuf"].shared = True def validate(self): if self.settings.compiler.get_safe("cppstd"): @@ -63,8 +69,7 @@ def validate(self): def build_requirements(self): self.test_requires("standardprojectsettings/[>=0.1.0]@ultimaker/stable") - if self.options.enable_arcus: - self.test_requires("protobuf/3.21.9") + self.test_requires("protobuf/3.21.9") if self.options.enable_testing: self.test_requires("gtest/1.12.1") if self.options.enable_benchmarks: @@ -72,9 +77,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("protobuf/3.21.9") self.requires("arcus/5.2.2") - self.requires("zlib/1.2.12") self.requires("clipper/6.4.2") self.requires("boost/1.79.0") self.requires("rapidjson/1.1.0") @@ -84,6 +87,10 @@ def requirements(self): self.requires("range-v3/0.12.0") self.requires("scripta/0.1.0@ultimaker/testing") self.requires("neargye-semver/0.3.0") + self.requires("protobuf/3.21.9") + self.requires("zlib/1.2.12") + self.requires("openssl/1.1.1l") + self.requires("asio-grpc/2.4.0") def generate(self): deps = CMakeDeps(self) diff --git a/plugin.proto b/plugin.proto index 0632036ec2..a435e00685 100644 --- a/plugin.proto +++ b/plugin.proto @@ -16,12 +16,16 @@ enum SlotID { // --------------------------------------------------------------------- // This is the message which is sent to the plugin to request identification -message Plugin_args { +service Plugin { + rpc Identify(PluginRequest) returns (PluginResponse) {} +} + +message PluginRequest { SlotID id = 1; } // This is the message which is sent back to CuraEngine to identify and validate the plugin -message Plugin_ret { +message PluginResponse { string plugin_hash = 1; string version = 2; } @@ -43,24 +47,34 @@ message Polygons { // --------------------------------------------------------------------- // The SIMPLIFY slot request message -message Simplify_args { + +service Simplify { + rpc Simplify(SimplifyRequest) returns (SimplifyResponse) {} +} + +message SimplifyRequest { Polygons polygons = 1; uint64 max_deviation = 2; uint64 max_angle = 3; } // The SIMPLIFY slot response message -message Simplify_ret { +message SimplifyResponse { Polygons polygons = 1; } // --------------------------------------------------------------------- // The POSTPROCESS slot request message -message Postprocess_args { + +service Postprocess { + rpc Postprocess(PostprocessRequest) returns (PostprocessResponse) {} +} + +message PostprocessRequest { string gcode_word = 1; } // The POSTPROCESS slot response message -message Postprocess_ret { +message PostprocessResponse { string gcode_word = 1; } \ No newline at end of file From a5d5a4d23fa4c8ddb14fbc02ec894367f0cf65a6 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 10:33:17 +0200 Subject: [PATCH 016/656] Add UNKNOWN SlotID As is common practice when working with gRPC and Protobuf [CURA-10475] --- plugin.proto | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugin.proto b/plugin.proto index a435e00685..dba2fea2f2 100644 --- a/plugin.proto +++ b/plugin.proto @@ -10,8 +10,9 @@ package cura.plugins.proto; // These are the different slots that plugins can hook into enum SlotID { - SIMPLIFY = 0; - POSTPROCESS = 1; + UNKNOWN = 0; + SIMPLIFY = 1; + POSTPROCESS = 2; } // --------------------------------------------------------------------- From e6cc85853de78e5e0b71be8dd0b9281d541d6db3 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 10:55:35 +0200 Subject: [PATCH 017/656] Remove protobuf from build requirements and add asio-grpc to CMakeLists.txt - Remove protobuf from build requirements in conanfile.py - Add asio-grpc to find_package in CMakeLists.txt [CURA-10475] --- CMakeLists.txt | 1 + conanfile.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a7a390ad1..83e152de3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) # Generate the plugin types find_package(protobuf REQUIRED) +find_package(asio-grpc REQUIRED) asio_grpc_protobuf_generate( GENERATE_GRPC GENERATE_MOCK_CODE OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" diff --git a/conanfile.py b/conanfile.py index 6c6e3fd90e..99a176cee9 100644 --- a/conanfile.py +++ b/conanfile.py @@ -69,7 +69,6 @@ def validate(self): def build_requirements(self): self.test_requires("standardprojectsettings/[>=0.1.0]@ultimaker/stable") - self.test_requires("protobuf/3.21.9") if self.options.enable_testing: self.test_requires("gtest/1.12.1") if self.options.enable_benchmarks: From fd7a8962054ffcc554c8bbd46e55ae1af4c02c9f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 11:25:03 +0200 Subject: [PATCH 018/656] Refactor: Rename PluginProxy to SlotProxy - Renamed include/plugins/pluginproxy.h to include/plugins/slotproxy.h - Updated references to PluginProxy to SlotProxy in include/plugins/slots.h - Updated include guard in include/plugins/slotproxy.h - Updated plugin.pb.h to plugin.grpc.pb.h in include/plugins/types.h [CURA-10475] --- include/plugins/{pluginproxy.h => slotproxy.h} | 6 +++--- include/plugins/slots.h | 10 +++++----- include/plugins/types.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename include/plugins/{pluginproxy.h => slotproxy.h} (97%) diff --git a/include/plugins/pluginproxy.h b/include/plugins/slotproxy.h similarity index 97% rename from include/plugins/pluginproxy.h rename to include/plugins/slotproxy.h index 0b08cb7b0b..59877d8e0f 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/slotproxy.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef CURAENGINE_INCLUDE_PLUGINS_PLUGINPROXY_H -#define CURAENGINE_INCLUDE_PLUGINS_PLUGINPROXY_H +#ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H +#define CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H #include #include @@ -155,4 +155,4 @@ class SlotProxy } // namespace cura::plugins -#endif // CURAENGINE_INCLUDE_PLUGINS_PLUGINPROXY_H +#endif // CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 8e1298b002..b4877286df 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -9,23 +9,23 @@ #include #include -#include "plugins/pluginproxy.h" +#include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" namespace cura::plugins { -using simplify_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::simplify_converter_fn>; -using postprocess_plugin = PluginProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::postprocess_converter_fn>; +using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::simplify_converter_fn>; +using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::postprocess_converter_fn>; -using plugins_t = std::variant; +using slots_t = std::variant; class Slots { constexpr Slots() noexcept = default; - std::unordered_map slots_{}; + std::unordered_map slots_{}; public: Slots(const Slots&) = delete; diff --git a/include/plugins/types.h b/include/plugins/types.h index b1ef0cf946..725026cf42 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -11,7 +11,7 @@ #include "utils/IntPoint.h" #include "utils/polygon.h" -#include "plugin.pb.h" +#include "plugin.grpc.pb.h" namespace cura::plugins { From 47d1127bf8093c412c050bb427b8ee62d8372821 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 12:53:42 +0200 Subject: [PATCH 019/656] Update Conan and CMake configurations - Change protobuf option to shared=False in conanfile.py - Add protobuf/3.21.9 as a tool requirement in conanfile.py - Update arcus requirement to latest version in conanfile.py - Remove libprotobuf from target_link_libraries for _CuraEngine when ENABLE_ARCUS is true in CMakeLists.txt - Add libprotobuf to target_link_libraries for _CuraEngine in CMakeLists.txt [CURA-10475] --- CMakeLists.txt | 5 +++-- conanfile.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83e152de3e..5bc58a4ed5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) # Generate the plugin types find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) + asio_grpc_protobuf_generate( GENERATE_GRPC GENERATE_MOCK_CODE OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" @@ -25,7 +26,6 @@ asio_grpc_protobuf_generate( PROTOS "${CMAKE_CURRENT_LIST_DIR}/plugin.proto" ) - if (ENABLE_ARCUS) message(STATUS "Building with Arcus") find_package(arcus REQUIRED) @@ -180,7 +180,7 @@ if (${EXTENSIVE_WARNINGS}) endif () if (ENABLE_ARCUS) - target_link_libraries(_CuraEngine PRIVATE arcus::arcus protobuf::libprotobuf) + target_link_libraries(_CuraEngine PUBLIC arcus::arcus ) endif () find_package(clipper REQUIRED) @@ -209,6 +209,7 @@ target_link_libraries(_CuraEngine scripta::scripta neargye-semver::neargye-semver asio-grpc::asio-grpc + protobuf::libprotobuf $<$:GTest::gtest>) if (NOT WIN32) diff --git a/conanfile.py b/conanfile.py index 99a176cee9..d87d83b840 100644 --- a/conanfile.py +++ b/conanfile.py @@ -50,7 +50,8 @@ def export_sources(self): def configure(self): self.options["boost"].header_only = True self.options["clipper"].shared = True - self.options["protobuf"].shared = True + + self.options["protobuf"].shared = False self.options["grpc"].csharp_plugin = False self.options["grpc"].node_plugin = False self.options["grpc"].objective_c_plugin = False @@ -69,6 +70,7 @@ def validate(self): def build_requirements(self): self.test_requires("standardprojectsettings/[>=0.1.0]@ultimaker/stable") + self.tool_requires("protobuf/3.21.9") if self.options.enable_testing: self.test_requires("gtest/1.12.1") if self.options.enable_benchmarks: @@ -76,7 +78,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/5.2.2") + self.requires("arcus/(latest)@ultimaker/cura_10475") self.requires("clipper/6.4.2") self.requires("boost/1.79.0") self.requires("rapidjson/1.1.0") From 63757a1c46b8dee7728db9358c2e060af4c43921 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 13:14:41 +0200 Subject: [PATCH 020/656] Refactor SlotProxy and related classes - Comment out code in SlotProxy constructor and operator() methods - Change class to struct for ReceiveConverterBase and SendConverterBase - Update proto::Plugin_args and proto::Plugin_ret to proto::PluginRequest and proto::PluginResponse respectively - Update simplify_plugin to simplify_slot in Application::registerPlugins() - Add includes for plugin.grpc.pb.h and plugin.pb.h in slots.h - Update simplify_slot and postprocess_slot to use ReceiveConverterBase and SendConverterBase [CURA-10475] --- include/plugins/slotproxy.h | 84 ++++++++++++++++++------------------- include/plugins/slots.h | 6 ++- include/plugins/types.h | 28 +++++-------- src/Application.cpp | 4 +- 4 files changed, 59 insertions(+), 63 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 59877d8e0f..027fcad04e 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -88,62 +88,62 @@ class SlotProxy //std::shared_ptr validator; private: - std::unique_ptr socket_; - listener_t* listener_; // FIXME: in Arcus use smart_ptr for listeners otherwise we need to add a deconstructor in this class and that forces us to define the big 6 - receiver_t receiver_{}; - sender_t sender_{}; +// std::unique_ptr socket_; +// listener_t* listener_; // FIXME: in Arcus use smart_ptr for listeners otherwise we need to add a deconstructor in this class and that forces us to define the big 6 +// receiver_t receiver_{}; +// sender_t sender_{}; public: - SlotProxy(const std::string& ip, int port) : - validator{ std::make_shared() }, - socket_{ std::make_unique() }, - listener_{ new listener_t(validator) } + SlotProxy(const std::string& ip, int port) +// validator{ std::make_shared() }, +// socket_{ std::make_unique() } +// listener_{ new listener_t(validator) } { // Add the listener - socket_->addListener(listener_); - - // Register all receiving message types - socket_->registerMessageType(&plugins::proto::Plugin_ret::default_instance()); - socket_->registerMessageType(&receive_t::default_instance()); - - // Connect to the plugin - spdlog::info("Connecting to plugin at {}:{}", ip, port); - socket_->connect(ip, port); - while (socket_->getState() == Arcus::SocketState::Connecting || socket_->getState() == Arcus::SocketState::Opening) - { - std::this_thread::sleep_for(std::chrono::milliseconds{ 100 }); // TODO: Fix magic number - // TODO: Add timeout??? Is this blocking even necessary? - } - - converters::simplify_converter_fn convvvvv{}; +// socket_->addListener(listener_); +// +// // Register all receiving message types +// socket_->registerMessageType(&plugins::proto::PluginRequest::default_instance()); +// socket_->registerMessageType(&receive_t::default_instance()); +// +// // Connect to the plugin +// spdlog::info("Connecting to plugin at {}:{}", ip, port); +// socket_->connect(ip, port); +// while (socket_->getState() == Arcus::SocketState::Connecting || socket_->getState() == Arcus::SocketState::Opening) +// { +// std::this_thread::sleep_for(std::chrono::milliseconds{ 100 }); // TODO: Fix magic number +// // TODO: Add timeout??? Is this blocking even necessary? +// } - SlotID slotId { SlotID::SIMPLIFY }; - auto x = convvvvv(slotId); +// converters::simplify_converter_fn convvvvv{}; - proto::Plugin_args args{}; - args.set_id(slot_id); +// SlotID slotId { SlotID::SIMPLIFY }; +// auto x = convvvvv(slotId); +// +// proto::PluginRequest args{}; +// args.set_id(slot_id); - socket_->sendMessage(std::make_shared(args)); - if (listener_->msgReceived()) - { - auto message = socket_->takeNextMessage(); - if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") - { - auto* msg = dynamic_cast(message.get()); - validator->version = semver::from_string(msg->version()); - validator->plugin_hash = msg->plugin_hash(); - spdlog::info("Plugin version: {}", validator->version.to_string()); - } - } +// socket_->sendMessage(std::make_shared(args)); +// if (listener_->msgReceived()) +// { +// auto message = socket_->takeNextMessage(); +// if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") +// { +//// auto* msg = dynamic_cast(message.get()); +//// validator->version = semver::from_string(msg->version()); +//// validator->plugin_hash = msg->plugin_hash(); +//// spdlog::info("Plugin version: {}", validator->version.to_string()); +// } +// } // // No need to wait for the response, since the listener will set the version } auto operator()(auto&&... args) { - if (validator && socket_->getState() == Arcus::SocketState::Connected) + if (true)//validator && socket_->getState() == Arcus::SocketState::Connected) { - socket_->sendMessage(converter_(std::forward(args)...)); +// socket_->sendMessage(converter_(std::forward(args)...)); // TODO: Block until message is received // TODO: Convert return message to actual return value return 1; // FIXME: This is not correct diff --git a/include/plugins/slots.h b/include/plugins/slots.h index b4877286df..c0c03f13e1 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -9,6 +9,8 @@ #include #include +#include "plugin.grpc.pb.h" +#include "plugin.pb.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" @@ -16,8 +18,8 @@ namespace cura::plugins { -using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::simplify_converter_fn>; -using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::postprocess_converter_fn>; +using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::ReceiveConverterBase, converters::SendConverterBase>; +using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::ReceiveConverterBase, converters::SendConverterBase>; using slots_t = std::variant; diff --git a/include/plugins/types.h b/include/plugins/types.h index 725026cf42..4f1983aa99 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -36,26 +36,22 @@ namespace converters { template -class ReceiveConverterBase +struct ReceiveConverterBase { - friend T; -public: - auto operator()(const proto::Plugin_ret& message) + auto operator()(const proto::PluginResponse& message) { - return std::tuple{ arg.version(), arg.plugin_hash() }; + return std::tuple{ message.version(), message.plugin_hash() }; } }; template -class SendConverterBase +struct SendConverterBase { - friend T -public: auto operator()(const cura::plugins::proto::SlotID& slot_id, auto&&... args) { - proto::Plugin_args msg{}; - msg.set_id(arg); - return std::make_shared(msg); + proto::PluginRequest msg{}; + msg.set_id(slot_id); + return std::make_shared(msg); } }; @@ -67,21 +63,19 @@ class SendConverterBase template class converter_base { - friend T; -public: auto operator()(auto& arg, auto&&... args) { if constexpr (std::is_same_v) { - proto::Plugin_args msg{}; + proto::PluginRequest msg{}; msg.set_id(arg); - return std::make_shared(msg); + return std::make_shared(msg); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return std::tuple{ arg.version(), arg.plugin_hash() }; } - return std::make_shared(); + return std::make_shared(); //return static_cast(*this).make(arg, std::forward(args)...); } }; diff --git a/src/Application.cpp b/src/Application.cpp index 56c3175a54..d874f9c474 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -254,8 +254,8 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { // TODO: remove this - plugins::Slots::instance().set({ "127.0.0.1", 50010 }); - auto x = plugins::Slots::instance().get(); + plugins::Slots::instance().set({ "127.0.0.1", 50010 }); + auto x = plugins::Slots::instance().get(); auto y = x(); } From 474027de2091f4dd3f4e0e476cf3c575334950c5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 13:54:50 +0200 Subject: [PATCH 021/656] Refactor SlotProxy to use gRPC - Remove Arcus::Socket and Arcus::SocketListener related code - Add includes for agrpc/grpc_context.hpp, boost/asio/awaitable.hpp, agrpc/asio_grpc.hpp, boost/asio/co_spawn.hpp, boost/asio/detached.hpp, grpcpp/client_context.h, grpcpp/create_channel.h - Add includes for plugin.grpc.pb.h and plugin.pb.h - Update SlotProxy constructor to use gRPC [CURA-10475] --- include/plugins/slotproxy.h | 138 ++++++++---------------------------- 1 file changed, 31 insertions(+), 107 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 027fcad04e..1a74f3977e 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,75 +4,27 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H #define CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H +#include +#include #include #include -#include -#include +#include +#include +#include +#include +#include #include -#include #include +#include "plugin.grpc.pb.h" +#include "plugin.pb.h" #include "plugins/types.h" #include "plugins/validator.h" -#include "plugin.pb.h" - namespace cura::plugins { -namespace detail -{ -template -class PluginListener : public Arcus::SocketListener -{ -public: - using validator_t = Validator; - using receive_t = Receiver; - - bool msgReceived() noexcept - { - bool msg_received = msg_received_; - msg_received_ = false; - return msg_received; - } - -private: - std::shared_ptr validator_{}; - receive_t receiver_{}; - bool msg_received_{ false }; - -public: - explicit PluginListener(std::shared_ptr validator) noexcept : validator_{ validator_ } - { - } - - void stateChanged(Arcus::SocketState) override{}; - - void messageReceived() override - { -// auto message = getSocket()->takeNextMessage(); -// -// if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") -// { -// auto* msg = dynamic_cast(message.get()); -// cura::plugins::proto::Plugin_ret mm {}; -// auto x = converter_(mm); -//// validator_->version = semver::from_string(version); -//// validator_->plugin_hash = plugin_hash; -// spdlog::info("Plugin version: {}", validator_->version.to_string()); -// } -// else if (message->GetTypeName() == receive_t::default_instance().GetTypeName()) -// { -// // unblock plugin -// } - }; - - void error(const Arcus::Error& error) override{}; -}; - -} // namespace detail - template class SlotProxy { @@ -81,69 +33,41 @@ class SlotProxy using receive_t = Receiver; using send_t = Sender; using validator_t = Validator; - using listener_t = detail::PluginListener; static inline constexpr plugins::SlotID slot_id{ Slot }; - //std::shared_ptr validator; - private: -// std::unique_ptr socket_; -// listener_t* listener_; // FIXME: in Arcus use smart_ptr for listeners otherwise we need to add a deconstructor in this class and that forces us to define the big 6 -// receiver_t receiver_{}; -// sender_t sender_{}; + // receiver_t receiver_{}; + // sender_t sender_{}; public: SlotProxy(const std::string& ip, int port) -// validator{ std::make_shared() }, -// socket_{ std::make_unique() } -// listener_{ new listener_t(validator) } { - // Add the listener -// socket_->addListener(listener_); -// -// // Register all receiving message types -// socket_->registerMessageType(&plugins::proto::PluginRequest::default_instance()); -// socket_->registerMessageType(&receive_t::default_instance()); -// -// // Connect to the plugin -// spdlog::info("Connecting to plugin at {}:{}", ip, port); -// socket_->connect(ip, port); -// while (socket_->getState() == Arcus::SocketState::Connecting || socket_->getState() == Arcus::SocketState::Opening) -// { -// std::this_thread::sleep_for(std::chrono::milliseconds{ 100 }); // TODO: Fix magic number -// // TODO: Add timeout??? Is this blocking even necessary? -// } - -// converters::simplify_converter_fn convvvvv{}; - -// SlotID slotId { SlotID::SIMPLIFY }; -// auto x = convvvvv(slotId); -// -// proto::PluginRequest args{}; -// args.set_id(slot_id); - -// socket_->sendMessage(std::make_shared(args)); -// if (listener_->msgReceived()) -// { -// auto message = socket_->takeNextMessage(); -// if (message->GetTypeName() == "cura.plugins.proto.Plugin_ret") -// { -//// auto* msg = dynamic_cast(message.get()); -//// validator->version = semver::from_string(msg->version()); -//// validator->plugin_hash = msg->plugin_hash(); -//// spdlog::info("Plugin version: {}", validator->version.to_string()); -// } -// } - // - // No need to wait for the response, since the listener will set the version + grpc::Status status; + proto::Plugin::Stub plugin_stub(grpc::CreateChannel(ip + ":" + std::to_string(port), grpc::InsecureChannelCredentials())); + agrpc::GrpcContext grpc_context; + + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; + grpc::ClientContext client_context{}; + proto::PluginRequest request{}; + request.set_id(slot_id); + proto::PluginResponse response{}; + status = co_await RPC::request(grpc_context, plugin_stub, client_context, request, response, boost::asio::use_awaitable); + spdlog::info("Received response from plugin: {}", response.DebugString()); + }, + boost::asio::detached); + grpc_context.run(); } auto operator()(auto&&... args) { - if (true)//validator && socket_->getState() == Arcus::SocketState::Connected) + if (true) // validator && socket_->getState() == Arcus::SocketState::Connected) { -// socket_->sendMessage(converter_(std::forward(args)...)); + // socket_->sendMessage(converter_(std::forward(args)...)); // TODO: Block until message is received // TODO: Convert return message to actual return value return 1; // FIXME: This is not correct From 8ea8818640c8b1f1b15fc3e842c6f999982de6eb Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 15 May 2023 14:03:35 +0200 Subject: [PATCH 022/656] Refactor SlotProxy to store plugin_stub_ and status_ - Add member variables plugin_stub_ and status_ - Update SlotProxy constructor to initialize plugin_stub_ - Update co_spawn lambda to use plugin_stub_ and status_ member variables [CURA-10475] --- include/plugins/slotproxy.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 1a74f3977e..ef641ccfc0 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -40,12 +40,13 @@ class SlotProxy // receiver_t receiver_{}; // sender_t sender_{}; + proto::Plugin::Stub plugin_stub_; + grpc::Status status_; + public: - SlotProxy(const std::string& ip, int port) + SlotProxy(const std::string& ip, int port) : plugin_stub_(grpc::CreateChannel(ip + ":" + std::to_string(port), grpc::InsecureChannelCredentials())) { - grpc::Status status; - proto::Plugin::Stub plugin_stub(grpc::CreateChannel(ip + ":" + std::to_string(port), grpc::InsecureChannelCredentials())); - agrpc::GrpcContext grpc_context; + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? boost::asio::co_spawn( grpc_context, @@ -56,7 +57,7 @@ class SlotProxy proto::PluginRequest request{}; request.set_id(slot_id); proto::PluginResponse response{}; - status = co_await RPC::request(grpc_context, plugin_stub, client_context, request, response, boost::asio::use_awaitable); + status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); spdlog::info("Received response from plugin: {}", response.DebugString()); }, boost::asio::detached); From 83e256f2cd39e35d6d16b0ed9709e1297f3cc617 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 May 2023 14:56:21 +0200 Subject: [PATCH 023/656] Fix copyright date in printLicense --- src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application.cpp b/src/Application.cpp index d874f9c474..5a559667d9 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -147,7 +147,7 @@ void Application::printLicense() const { fmt::print("\n"); fmt::print("Cura_SteamEngine version {}\n", CURA_ENGINE_VERSION); - fmt::print("Copyright (C) 2022 Ultimaker\n"); + fmt::print("Copyright (C) 2023 Ultimaker\n"); fmt::print("\n"); fmt::print("This program is free software: you can redistribute it and/or modify\n"); fmt::print("it under the terms of the GNU Affero General Public License as published by\n"); From 1025f12b895fa856cb5b49c09cfa5709feab83a9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Mon, 15 May 2023 14:27:29 +0200 Subject: [PATCH 024/656] Rework converters into separeted (send,receive) concepted closures. Work in progress! Just adjusted the types.h, the rest of the code has been changed underneath of me anyway (since we now have about 3 to 4 devs on the same piece of code) -- Need to make it work afterwards. This change, if it works properly, should simplify everything, since you only need to specify two 'loose' functions that get typechecked properly by the concepting. part of CURA-10475 --- include/plugins/types.h | 109 ++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 61 deletions(-) diff --git a/include/plugins/types.h b/include/plugins/types.h index 4f1983aa99..0bb03cf098 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -16,6 +16,9 @@ namespace cura::plugins { using SlotID = proto::SlotID; +using MessageIn = proto::PluginResponse; +using MessageOut = proto::PluginRequest; +using MessageOutPtr = std::shared_ptr; namespace details { @@ -35,63 +38,44 @@ struct CharRangeLiteral namespace converters { -template -struct ReceiveConverterBase +template +concept ReceiveCallable = requires(C callable, MessageIn message) { - auto operator()(const proto::PluginResponse& message) - { - return std::tuple{ message.version(), message.plugin_hash() }; - } + { callable(message) } -> std::same_as; +}; + +template +concept SendCallable = requires(C callable, S... args) +{ + { callable(args...) } -> std::same_as; }; -template -struct SendConverterBase +const auto receive_slot_id { - auto operator()(const cura::plugins::proto::SlotID& slot_id, auto&&... args) + [](const MessageIn& message) { - proto::PluginRequest msg{}; - msg.set_id(slot_id); - return std::make_shared(msg); + return std::tuple{ message.version(), message.plugin_hash() }; } }; +static_assert(ReceiveCallable>); -//class SimplifyReceiveConverter : ReceiveConverterBase<> -//{ -// -//}; - -template -class converter_base +const auto send_slot_id { - auto operator()(auto& arg, auto&&... args) + [](const cura::plugins::proto::SlotID& slot_id) { - if constexpr (std::is_same_v) - { - proto::PluginRequest msg{}; - msg.set_id(arg); - return std::make_shared(msg); - } - else if constexpr (std::is_same_v) - { - return std::tuple{ arg.version(), arg.plugin_hash() }; - } - return std::make_shared(); - //return static_cast(*this).make(arg, std::forward(args)...); + MessageOut message{}; + message.set_id(slot_id); + return std::make_shared(message); } }; +static_assert(SendCallable); -template -class simplify_converter_fn : public converter_base> +const auto receive_simplify { -public: - using receive_t = Receive; - using send_t = Send; - -private: - Polygons make(const Receive& args) + [](const MessageIn& message) { Polygons poly{}; - for (const auto& paths : args.polygons().paths()) + for (const auto& paths : message.polygons().paths()) { Polygon p{}; for (const auto& point : paths.path()) @@ -102,15 +86,19 @@ class simplify_converter_fn : public converter_base); - std::shared_ptr make(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) +const auto send_simplify +{ + [](const Polygons& polygons, const size_t max_deviation, const size_t max_angle) { - Send args{}; - args.set_max_deviation(max_deviation); - args.set_max_angle(max_angle); + MessageOut message{}; + message.set_max_deviation(max_deviation); + message.set_max_angle(max_angle); for (const auto& polygon : polygons.paths) { - auto poly = args.polygons(); + auto poly = message.polygons(); for (const auto& path : polygons.paths) { auto* p = poly.add_paths(); @@ -122,34 +110,33 @@ class simplify_converter_fn : public converter_base(args); + return std::make_shared(message); } }; +static_assert(SendCallable); -template -class postprocess_converter_fn : public converter_base> +const auto receive_postprocess { -public: - using receive_t = Receive; - using send_t = Send; - - std::string make(const Receive& args) + [](const MessageIn& message) { - return args.gcode(); + return message.gcode(); } +}; +static_assert(ReceiveCallable); - std::shared_ptr make(const std::string& gcode) +const auto send_postprocess +{ + [](const std::string& gcode) { - Send args{}; - args.set_gcode(gcode); - return std::make_shared(args); + MessageOut message{}; + message.set_gcode(gcode); + return std::make_shared(message); } }; +static_assert(SendCallable); } // namespace converters - } // namespace cura::plugins - #endif // CURAENGINE_INCLUDE_PLUGINS_TYPES_H From 5b188e7f7862d16859bd02a4c57e04a24c59c4a6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Mon, 15 May 2023 20:03:32 +0200 Subject: [PATCH 025/656] Fix complilation of asio (boost) on windows. Otherwise it'll complain about the winsock header being included already. done as part of CURA-10475 --- include/utils/gettime.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/gettime.h b/include/utils/gettime.h index b2c8088a97..52bc73556a 100644 --- a/include/utils/gettime.h +++ b/include/utils/gettime.h @@ -5,6 +5,7 @@ #define GETTIME_H #ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN 1 #include #else #ifdef USE_CPU_TIME From 656cc375e1dfb255433fd1dbaf57f5f0a266c508 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Mon, 15 May 2023 23:52:08 +0200 Subject: [PATCH 026/656] More work on converters. The things we receive and send aren't plain protobuf messages of course, adapt the concepts and the rest of the types.h code to fit that reality. part of CURA-10475 --- include/plugins/slotproxy.h | 2 +- include/plugins/slots.h | 4 ++-- include/plugins/types.h | 47 ++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index ef641ccfc0..c2f2bafd8f 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -25,7 +25,7 @@ namespace cura::plugins { -template +template class SlotProxy { public: diff --git a/include/plugins/slots.h b/include/plugins/slots.h index c0c03f13e1..40f87f5f89 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -18,8 +18,8 @@ namespace cura::plugins { -using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::ReceiveConverterBase, converters::SendConverterBase>; -using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::ReceiveConverterBase, converters::SendConverterBase>; +using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::receive_simplify, converters::send_simplify>; +using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::receive_postprocess, converters::send_postprocess>; using slots_t = std::variant; diff --git a/include/plugins/types.h b/include/plugins/types.h index 0bb03cf098..71aeb36e08 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -16,9 +16,6 @@ namespace cura::plugins { using SlotID = proto::SlotID; -using MessageIn = proto::PluginResponse; -using MessageOut = proto::PluginRequest; -using MessageOutPtr = std::shared_ptr; namespace details { @@ -38,41 +35,43 @@ struct CharRangeLiteral namespace converters { -template -concept ReceiveCallable = requires(C callable, MessageIn message) +template +concept ReceiveCallable = requires(C callable, M message) { { callable(message) } -> std::same_as; + std::is_base_of_v; }; -template +template concept SendCallable = requires(C callable, S... args) { - { callable(args...) } -> std::same_as; + { callable(args...) } -> std::same_as>; + std::is_base_of_v; }; const auto receive_slot_id { - [](const MessageIn& message) + [](const proto::PluginResponse& message) { return std::tuple{ message.version(), message.plugin_hash() }; } }; -static_assert(ReceiveCallable>); +static_assert(ReceiveCallable>); const auto send_slot_id { [](const cura::plugins::proto::SlotID& slot_id) { - MessageOut message{}; + proto::PluginRequest message{}; message.set_id(slot_id); - return std::make_shared(message); + return std::make_shared(message); } }; -static_assert(SendCallable); +static_assert(SendCallable); const auto receive_simplify { - [](const MessageIn& message) + [](const proto::SimplifyResponse& message) { Polygons poly{}; for (const auto& paths : message.polygons().paths()) @@ -87,13 +86,13 @@ const auto receive_simplify return poly; } }; -static_assert(ReceiveCallable); +static_assert(ReceiveCallable); const auto send_simplify { [](const Polygons& polygons, const size_t max_deviation, const size_t max_angle) { - MessageOut message{}; + proto::SimplifyRequest message{}; message.set_max_deviation(max_deviation); message.set_max_angle(max_angle); for (const auto& polygon : polygons.paths) @@ -110,30 +109,30 @@ const auto send_simplify } } } - return std::make_shared(message); + return std::make_shared(message); } }; -static_assert(SendCallable); +static_assert(SendCallable); const auto receive_postprocess { - [](const MessageIn& message) + [](const proto::PostprocessResponse& message) { - return message.gcode(); + return message.gcode_word(); } }; -static_assert(ReceiveCallable); +static_assert(ReceiveCallable); const auto send_postprocess { [](const std::string& gcode) { - MessageOut message{}; - message.set_gcode(gcode); - return std::make_shared(message); + proto::PostprocessRequest message{}; + message.set_gcode_word(gcode); + return std::make_shared(message); } }; -static_assert(SendCallable); +static_assert(SendCallable); } // namespace converters From 185cc85c47cf6ce4941371e96de6e7ef6d1ed430 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 08:26:49 +0200 Subject: [PATCH 027/656] Refactor slot proxies to use grpc stubs - Replace ip and port parameters with a shared grpc channel - Add a stub template parameter to each slot proxy type - Use the stub to call the corresponding grpc service method - Remove unused receiver and sender members from slot proxy [CURA-10475] --- include/plugins/slotproxy.h | 10 +++++----- include/plugins/slots.h | 4 ++-- src/Application.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index c2f2bafd8f..c341fefd72 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,7 @@ namespace cura::plugins { -template +template class SlotProxy { public: @@ -33,18 +34,17 @@ class SlotProxy using receive_t = Receiver; using send_t = Sender; using validator_t = Validator; + using stub_t = Stub; static inline constexpr plugins::SlotID slot_id{ Slot }; private: - // receiver_t receiver_{}; - // sender_t sender_{}; - proto::Plugin::Stub plugin_stub_; + stub_t process_stub_; grpc::Status status_; public: - SlotProxy(const std::string& ip, int port) : plugin_stub_(grpc::CreateChannel(ip + ":" + std::to_string(port), grpc::InsecureChannelCredentials())) + SlotProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) { agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 40f87f5f89..982232e15a 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -18,8 +18,8 @@ namespace cura::plugins { -using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::receive_simplify, converters::send_simplify>; -using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, converters::receive_postprocess, converters::send_postprocess>; +using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Simplify::Stub, converters::receive_simplify, converters::send_simplify>; +using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, converters::receive_postprocess, converters::send_postprocess>; using slots_t = std::variant; diff --git a/src/Application.cpp b/src/Application.cpp index 5a559667d9..8c84219276 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -254,7 +254,7 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { // TODO: remove this - plugins::Slots::instance().set({ "127.0.0.1", 50010 }); + plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 5555), grpc::InsecureChannelCredentials())); auto x = plugins::Slots::instance().get(); auto y = x(); } From 419c3616fff7d3ed4094e73dcabe7b84dc15aa1c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 09:03:15 +0200 Subject: [PATCH 028/656] Refactor SlotProxy template arguments and move concepts to utils - Move ReceiveCallable and SendCallable concepts to utils/concepts/generic.h and rename them to receive_callable and send_callable - Change SlotProxy template arguments to use class types instead of concepts - Add missing includes in slotproxy.h and generic.h - Reformat simplify_slot and postprocess_slot definitions in slots.h [CURA-10475] --- include/plugins/slotproxy.h | 7 +++++-- include/plugins/slots.h | 12 ++++++++++-- include/plugins/types.h | 27 +++++++-------------------- include/utils/concepts/generic.h | 16 ++++++++++++++++ 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index c341fefd72..b8b0a8e93d 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -12,9 +12,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -26,7 +26,7 @@ namespace cura::plugins { -template +template class SlotProxy { public: @@ -39,6 +39,9 @@ class SlotProxy static inline constexpr plugins::SlotID slot_id{ Slot }; private: + receive_t receive_conv_ {}; + send_t send_conv_{}; + validator_t valid_{}; proto::Plugin::Stub plugin_stub_; stub_t process_stub_; grpc::Status status_; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 982232e15a..1dc5837baa 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -18,8 +18,16 @@ namespace cura::plugins { -using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Simplify::Stub, converters::receive_simplify, converters::send_simplify>; -using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, converters::receive_postprocess, converters::send_postprocess>; +using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, + proto::Simplify::Stub, + decltype(converters::receive_simplify), + decltype(converters::send_simplify)>; +using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, + proto::Postprocess::Stub, + decltype(converters::receive_postprocess), + decltype(converters::send_postprocess)>; using slots_t = std::variant; diff --git a/include/plugins/types.h b/include/plugins/types.h index 71aeb36e08..c0b1e7b6b0 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -9,6 +9,7 @@ #include #include "utils/IntPoint.h" +#include "utils/concepts/generic.h" #include "utils/polygon.h" #include "plugin.grpc.pb.h" @@ -35,20 +36,6 @@ struct CharRangeLiteral namespace converters { -template -concept ReceiveCallable = requires(C callable, M message) -{ - { callable(message) } -> std::same_as; - std::is_base_of_v; -}; - -template -concept SendCallable = requires(C callable, S... args) -{ - { callable(args...) } -> std::same_as>; - std::is_base_of_v; -}; - const auto receive_slot_id { [](const proto::PluginResponse& message) @@ -56,7 +43,7 @@ const auto receive_slot_id return std::tuple{ message.version(), message.plugin_hash() }; } }; -static_assert(ReceiveCallable>); +static_assert(receive_callable>); const auto send_slot_id { @@ -67,7 +54,7 @@ const auto send_slot_id return std::make_shared(message); } }; -static_assert(SendCallable); +static_assert(send_callable); const auto receive_simplify { @@ -86,7 +73,7 @@ const auto receive_simplify return poly; } }; -static_assert(ReceiveCallable); +static_assert(receive_callable); const auto send_simplify { @@ -112,7 +99,7 @@ const auto send_simplify return std::make_shared(message); } }; -static_assert(SendCallable); +static_assert(send_callable); const auto receive_postprocess { @@ -121,7 +108,7 @@ const auto receive_postprocess return message.gcode_word(); } }; -static_assert(ReceiveCallable); +static_assert(receive_callable); const auto send_postprocess { @@ -132,7 +119,7 @@ const auto send_postprocess return std::make_shared(message); } }; -static_assert(SendCallable); +static_assert(send_callable); } // namespace converters diff --git a/include/utils/concepts/generic.h b/include/utils/concepts/generic.h index 309c5c115f..523d681db0 100644 --- a/include/utils/concepts/generic.h +++ b/include/utils/concepts/generic.h @@ -7,6 +7,8 @@ #include #include +#include + namespace cura { template @@ -14,6 +16,20 @@ concept hashable = requires(T value) { { std::hash{}(value) } -> concepts::convertible_to; }; + +template +concept receive_callable = requires(C callable, M message) +{ + { callable(message) } -> std::same_as; + std::is_base_of_v; +}; + +template +concept send_callable = requires(C callable, S... args) +{ + { callable(args...) } -> std::same_as>; + std::is_base_of_v; +}; } // namespace cura #endif // CURAENGINE_GENERIC_H From de783c41a17ba3fdac818defa7a94123f634de4b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 09:37:26 +0200 Subject: [PATCH 029/656] Use constexpr [CURA-10475] --- include/plugins/types.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/plugins/types.h b/include/plugins/types.h index c0b1e7b6b0..8db6303bc2 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -36,7 +36,7 @@ struct CharRangeLiteral namespace converters { -const auto receive_slot_id +constexpr auto receive_slot_id { [](const proto::PluginResponse& message) { @@ -45,7 +45,7 @@ const auto receive_slot_id }; static_assert(receive_callable>); -const auto send_slot_id +constexpr auto send_slot_id { [](const cura::plugins::proto::SlotID& slot_id) { @@ -56,7 +56,7 @@ const auto send_slot_id }; static_assert(send_callable); -const auto receive_simplify +constexpr auto receive_simplify { [](const proto::SimplifyResponse& message) { @@ -75,7 +75,7 @@ const auto receive_simplify }; static_assert(receive_callable); -const auto send_simplify +constexpr auto send_simplify { [](const Polygons& polygons, const size_t max_deviation, const size_t max_angle) { @@ -101,7 +101,7 @@ const auto send_simplify }; static_assert(send_callable); -const auto receive_postprocess +constexpr auto receive_postprocess { [](const proto::PostprocessResponse& message) { @@ -110,7 +110,7 @@ const auto receive_postprocess }; static_assert(receive_callable); -const auto send_postprocess +constexpr auto send_postprocess { [](const std::string& gcode) { From ee5d4023e46c8f47761a0573d08e6111e3de3b6f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 11:52:31 +0200 Subject: [PATCH 030/656] Reworked converters The used proto types are needed in the slotproxy instead of sending that type info twice, the simple closures had to be replaced with a slightly heavier one. - Placed converters in their own header - Use structs - Update slotproxy to use these converters [CURA-10475] --- include/plugins/converters.h | 127 +++++++++++++++++++++++++++++++++++ include/plugins/slotproxy.h | 25 ++++--- include/plugins/slots.h | 9 +-- include/plugins/types.h | 90 ------------------------- 4 files changed, 147 insertions(+), 104 deletions(-) create mode 100644 include/plugins/converters.h diff --git a/include/plugins/converters.h b/include/plugins/converters.h new file mode 100644 index 0000000000..46259a851e --- /dev/null +++ b/include/plugins/converters.h @@ -0,0 +1,127 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PLUGINS_CONVERTERS_H +#define PLUGINS_CONVERTERS_H + +#include + +#include "plugin.grpc.pb.h" +#include "plugins/types.h" + +namespace cura::plugins +{ + +namespace details +{ +template +struct plugin_request +{ + using value_type = Request; + + auto operator()(const cura::plugins::proto::SlotID& slot_id) const + { + proto::PluginRequest message{}; + message.set_id(slot_id); + return std::make_shared(message); + } +}; + +template +struct plugin_response +{ + using value_type = Response; + + auto operator()(const value_type& message) const + { + return std::make_pair( message.version(), message.plugin_hash() ); + } +}; + +template +struct simplify_request +{ + using value_type = Request; + + auto operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const + { + proto::SimplifyRequest message{}; + message.set_max_deviation(max_deviation); + message.set_max_angle(max_angle); + for (const auto& polygon : polygons.paths) + { + auto poly = message.polygons(); + for (const auto& path : polygons.paths) + { + auto* p = poly.add_paths(); + for (const auto& point : path) + { + auto* pt = p->add_path(); + pt->set_x(point.X); + pt->set_y(point.Y); + } + } + } + return std::make_shared(message); + } +}; + +template +struct simplify_response +{ + using value_type = Response; + + auto operator()(const value_type& message) const + { + Polygons poly{}; + for (const auto& paths : message.polygons().paths()) + { + Polygon p{}; + for (const auto& point : paths.path()) + { + p.add(Point{ point.y(), point.y() }); + } + poly.add(p); + } + return poly; + } +}; + +template +struct postprocess_request +{ + using value_type = Request; + + auto operator()(const std::string& gcode) const + { + proto::PostprocessRequest message{}; + message.set_gcode_word(gcode); + return std::make_shared(message); + } +}; + +template +struct postprocess_response +{ + using value_type = Response; + + auto operator()(const value_type& message) const + { + return message.gcode_word(); + } +}; + +} // namespace details + + +using plugin_request_t = details::plugin_request; +using plugin_response_t = details::plugin_response; +using simplify_request_t = details::simplify_request; +using simplify_response_t = details::simplify_request; +using postprocess_request_t = details::postprocess_request; +using postprocess_response_t = details::postprocess_response; + +} // namespace cura::plugins + + +#endif diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index b8b0a8e93d..e39ff7541d 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -18,29 +18,34 @@ #include #include -#include "plugin.grpc.pb.h" -#include "plugin.pb.h" #include "plugins/types.h" #include "plugins/validator.h" +#include "plugins/converters.h" + +#include "plugin.grpc.pb.h" namespace cura::plugins { -template +template class SlotProxy { public: // type aliases for easy use - using receive_t = Receiver; - using send_t = Sender; + using request_plugin_t = typename plugin_request_t::value_type; + using response_plugin_t = typename plugin_response_t::value_type; + using request_converter_t = Request; + using request_process_t = typename Request::value_type; + using response_converter_t = Response; + using response_process_t = typename Response::value_type; using validator_t = Validator; using stub_t = Stub; static inline constexpr plugins::SlotID slot_id{ Slot }; private: - receive_t receive_conv_ {}; - send_t send_conv_{}; + request_converter_t request_converter_{}; + response_converter_t response_converter_{}; validator_t valid_{}; proto::Plugin::Stub plugin_stub_; stub_t process_stub_; @@ -49,7 +54,7 @@ class SlotProxy public: SlotProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? boost::asio::co_spawn( grpc_context, @@ -57,9 +62,9 @@ class SlotProxy { using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; grpc::ClientContext client_context{}; - proto::PluginRequest request{}; + request_plugin_t request{}; request.set_id(slot_id); - proto::PluginResponse response{}; + response_plugin_t response{}; status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); spdlog::info("Received response from plugin: {}", response.DebugString()); }, diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 1dc5837baa..35f7a2f0be 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -14,6 +14,7 @@ #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" +#include "plugins/converters.h" namespace cura::plugins { @@ -21,13 +22,13 @@ namespace cura::plugins using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Simplify::Stub, - decltype(converters::receive_simplify), - decltype(converters::send_simplify)>; + simplify_request_t, + simplify_response_t>; using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, - decltype(converters::receive_postprocess), - decltype(converters::send_postprocess)>; + postprocess_request_t, + postprocess_response_t>; using slots_t = std::variant; diff --git a/include/plugins/types.h b/include/plugins/types.h index 8db6303bc2..3c3c1b9f02 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -33,96 +33,6 @@ struct CharRangeLiteral } // namespace details -namespace converters -{ - -constexpr auto receive_slot_id -{ - [](const proto::PluginResponse& message) - { - return std::tuple{ message.version(), message.plugin_hash() }; - } -}; -static_assert(receive_callable>); - -constexpr auto send_slot_id -{ - [](const cura::plugins::proto::SlotID& slot_id) - { - proto::PluginRequest message{}; - message.set_id(slot_id); - return std::make_shared(message); - } -}; -static_assert(send_callable); - -constexpr auto receive_simplify -{ - [](const proto::SimplifyResponse& message) - { - Polygons poly{}; - for (const auto& paths : message.polygons().paths()) - { - Polygon p{}; - for (const auto& point : paths.path()) - { - p.add(Point{ point.y(), point.y() }); - } - poly.add(p); - } - return poly; - } -}; -static_assert(receive_callable); - -constexpr auto send_simplify -{ - [](const Polygons& polygons, const size_t max_deviation, const size_t max_angle) - { - proto::SimplifyRequest message{}; - message.set_max_deviation(max_deviation); - message.set_max_angle(max_angle); - for (const auto& polygon : polygons.paths) - { - auto poly = message.polygons(); - for (const auto& path : polygons.paths) - { - auto* p = poly.add_paths(); - for (const auto& point : path) - { - auto* pt = p->add_path(); - pt->set_x(point.X); - pt->set_y(point.Y); - } - } - } - return std::make_shared(message); - } -}; -static_assert(send_callable); - -constexpr auto receive_postprocess -{ - [](const proto::PostprocessResponse& message) - { - return message.gcode_word(); - } -}; -static_assert(receive_callable); - -constexpr auto send_postprocess -{ - [](const std::string& gcode) - { - proto::PostprocessRequest message{}; - message.set_gcode_word(gcode); - return std::make_shared(message); - } -}; -static_assert(send_callable); - -} // namespace converters - } // namespace cura::plugins #endif // CURAENGINE_INCLUDE_PLUGINS_TYPES_H From 73048fa62f724f762b562e4d8dbbd4195f618a51 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 11:59:56 +0200 Subject: [PATCH 031/656] operator implementation Doesn't compile yet but shows intent, how the SlotProxy, converters and protobuf message could work. [CURA-10475] --- include/plugins/slotproxy.h | 25 +++++++++++++++++-------- src/Application.cpp | 3 ++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index e39ff7541d..194054b2c1 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -18,9 +18,9 @@ #include #include +#include "plugins/converters.h" #include "plugins/types.h" #include "plugins/validator.h" -#include "plugins/converters.h" #include "plugin.grpc.pb.h" @@ -74,13 +74,22 @@ class SlotProxy auto operator()(auto&&... args) { - if (true) // validator && socket_->getState() == Arcus::SocketState::Connected) - { - // socket_->sendMessage(converter_(std::forward(args)...)); - // TODO: Block until message is received - // TODO: Convert return message to actual return value - return 1; // FIXME: This is not correct - } + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; + grpc::ClientContext client_context{}; + request_process_t request{ request_converter_(std::forward(args)...) }; + response_process_t response{}; + status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); + spdlog::info("Received response from plugin: {}", response.DebugString()); + }, + boost::asio::detached); + grpc_context.run(); + return 1; // FIXME: handle plugin not connected } }; diff --git a/src/Application.cpp b/src/Application.cpp index 8c84219276..a34fe5696b 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -256,7 +256,8 @@ void Application::registerPlugins() // TODO: remove this plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 5555), grpc::InsecureChannelCredentials())); auto x = plugins::Slots::instance().get(); - auto y = x(); + Polygons poly; + auto y = x(poly, 100, 100); } } // namespace cura \ No newline at end of file From d444e9a1218d89a6fe71abed7e2e641d4d1cd2e5 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 16 May 2023 15:33:36 +0200 Subject: [PATCH 032/656] Remove templating for converters, make compile again. Co-authored-by: Casper Lamboo Co-authored-by: Jelle Spijker --- include/plugins/converters.h | 36 +++++++++--------------------------- include/plugins/slotproxy.h | 34 +++++++++++++++++----------------- include/plugins/slots.h | 8 ++++---- 3 files changed, 30 insertions(+), 48 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 46259a851e..dec0e88e60 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -12,25 +12,21 @@ namespace cura::plugins { -namespace details -{ -template struct plugin_request { - using value_type = Request; + using value_type = proto::PluginRequest; auto operator()(const cura::plugins::proto::SlotID& slot_id) const { - proto::PluginRequest message{}; + value_type message{}; message.set_id(slot_id); return std::make_shared(message); } }; -template struct plugin_response { - using value_type = Response; + using value_type = proto::PluginResponse; auto operator()(const value_type& message) const { @@ -38,14 +34,13 @@ struct plugin_response } }; -template struct simplify_request { - using value_type = Request; + using value_type = proto::SimplifyRequest; auto operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const { - proto::SimplifyRequest message{}; + value_type message{}; message.set_max_deviation(max_deviation); message.set_max_angle(max_angle); for (const auto& polygon : polygons.paths) @@ -66,10 +61,9 @@ struct simplify_request } }; -template struct simplify_response { - using value_type = Response; + using value_type = proto::SimplifyResponse; auto operator()(const value_type& message) const { @@ -87,23 +81,21 @@ struct simplify_response } }; -template struct postprocess_request { - using value_type = Request; + using value_type = proto::PostprocessRequest; auto operator()(const std::string& gcode) const { - proto::PostprocessRequest message{}; + value_type message{}; message.set_gcode_word(gcode); return std::make_shared(message); } }; -template struct postprocess_response { - using value_type = Response; + using value_type = proto::PostprocessResponse; auto operator()(const value_type& message) const { @@ -111,16 +103,6 @@ struct postprocess_response } }; -} // namespace details - - -using plugin_request_t = details::plugin_request; -using plugin_response_t = details::plugin_response; -using simplify_request_t = details::simplify_request; -using simplify_response_t = details::simplify_request; -using postprocess_request_t = details::postprocess_request; -using postprocess_response_t = details::postprocess_response; - } // namespace cura::plugins diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 194054b2c1..4eacf9322a 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -32,8 +32,8 @@ class SlotProxy { public: // type aliases for easy use - using request_plugin_t = typename plugin_request_t::value_type; - using response_plugin_t = typename plugin_response_t::value_type; + using request_plugin_t = typename plugin_request::value_type; + using response_plugin_t = typename plugin_response::value_type; using request_converter_t = Request; using request_process_t = typename Request::value_type; using response_converter_t = Response; @@ -74,21 +74,21 @@ class SlotProxy auto operator()(auto&&... args) { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - boost::asio::co_spawn( - grpc_context, - [&]() -> boost::asio::awaitable - { - using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; - grpc::ClientContext client_context{}; - request_process_t request{ request_converter_(std::forward(args)...) }; - response_process_t response{}; - status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); - spdlog::info("Received response from plugin: {}", response.DebugString()); - }, - boost::asio::detached); - grpc_context.run(); + //agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + + //boost::asio::co_spawn( + // grpc_context, + // [&]() -> boost::asio::awaitable + // { + // using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; + // grpc::ClientContext client_context{}; + // request_process_t request{ request_converter_(std::forward(args)...) }; + // response_process_t response{}; + // status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); + // spdlog::info("Received response from plugin: {}", response.DebugString()); + // }, + // boost::asio::detached); + //grpc_context.run(); return 1; // FIXME: handle plugin not connected } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 35f7a2f0be..a68d830064 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -22,13 +22,13 @@ namespace cura::plugins using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Simplify::Stub, - simplify_request_t, - simplify_response_t>; + simplify_request, + simplify_response>; using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, - postprocess_request_t, - postprocess_response_t>; + postprocess_request, + postprocess_response>; using slots_t = std::variant; From b20d222cc3a082f55a7afc60088d42efd2096cbd Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 16 May 2023 16:29:44 +0200 Subject: [PATCH 033/656] Fix install on macOS CURA-10475 --- conanfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index d87d83b840..1dc0be75b3 100644 --- a/conanfile.py +++ b/conanfile.py @@ -58,6 +58,7 @@ def configure(self): self.options["grpc"].php_plugin = False self.options["grpc"].python_plugin = False self.options["grpc"].ruby_plugin = False + self.options["asio-grpc"].local_allocator = "recycling_allocator" if self.options.enable_arcus: self.options["arcus"].shared = True @@ -80,7 +81,7 @@ def requirements(self): if self.options.enable_arcus: self.requires("arcus/(latest)@ultimaker/cura_10475") self.requires("clipper/6.4.2") - self.requires("boost/1.79.0") + self.requires("boost/1.81.0") self.requires("rapidjson/1.1.0") self.requires("stb/20200203") self.requires("spdlog/1.10.0") From 46fbb9cfd52e2af5df2cec4d623a27b078f65c7a Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 16:35:09 +0200 Subject: [PATCH 034/656] Add support for async prepare in slotproxy Refactor slot proxy and converters for plugins - Reorder include headers in alphabetical order - Add Prepare template parameter to SlotProxy class - Use value_type instead of shared_ptr in Converter class - Uncomment grpc_context in SlotProxy operator() [CURA-10475] --- include/plugins/converters.h | 4 ++-- include/plugins/slotproxy.h | 35 +++++++++++++++++------------------ include/plugins/slots.h | 7 +++++-- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index dec0e88e60..d2df1e34a7 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -16,11 +16,11 @@ struct plugin_request { using value_type = proto::PluginRequest; - auto operator()(const cura::plugins::proto::SlotID& slot_id) const + value_type operator()(const cura::plugins::proto::SlotID& slot_id) const { value_type message{}; message.set_id(slot_id); - return std::make_shared(message); + return message; } }; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 4eacf9322a..d2a1452f23 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,11 +4,11 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H #define CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H -#include -#include #include #include +#include +#include #include #include #include @@ -27,7 +27,7 @@ namespace cura::plugins { -template +template class SlotProxy { public: @@ -74,21 +74,20 @@ class SlotProxy auto operator()(auto&&... args) { - //agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - //boost::asio::co_spawn( - // grpc_context, - // [&]() -> boost::asio::awaitable - // { - // using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; - // grpc::ClientContext client_context{}; - // request_process_t request{ request_converter_(std::forward(args)...) }; - // response_process_t response{}; - // status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); - // spdlog::info("Received response from plugin: {}", response.DebugString()); - // }, - // boost::asio::detached); - //grpc_context.run(); + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + grpc::ClientContext client_context{}; + request_process_t request {};// { request_converter_(std::forward(args)...) }; + response_process_t response{}; + status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); + spdlog::info("Received response from plugin: {}", response.DebugString()); + }, + boost::asio::detached); + grpc_context.run(); return 1; // FIXME: handle plugin not connected } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index a68d830064..4c225a1d20 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -9,24 +9,27 @@ #include #include -#include "plugin.grpc.pb.h" -#include "plugin.pb.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" #include "plugins/converters.h" +#include "plugin.grpc.pb.h" +#include "plugin.pb.h" + namespace cura::plugins { using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Simplify::Stub, + agrpc::RPC<&proto::Simplify::Stub::PrepareAsyncSimplify>, simplify_request, simplify_response>; using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, + agrpc::RPC<&proto::Postprocess::Stub::PrepareAsyncPostprocess>, postprocess_request, postprocess_response>; From 05609db7c90616b1d07912c6cd9c5f5a7894acc5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 16 May 2023 16:38:12 +0200 Subject: [PATCH 035/656] Fix request converter usage in slot proxy - Use value_type instead of shared_ptr in Converter classes - Pass converted request to Prepare::request in SlotProxy operator() [CURA-10475] --- include/plugins/converters.h | 8 ++++---- include/plugins/slotproxy.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index d2df1e34a7..4c3bb166dd 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -38,7 +38,7 @@ struct simplify_request { using value_type = proto::SimplifyRequest; - auto operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const + value_type operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const { value_type message{}; message.set_max_deviation(max_deviation); @@ -57,7 +57,7 @@ struct simplify_request } } } - return std::make_shared(message); + return message; } }; @@ -85,11 +85,11 @@ struct postprocess_request { using value_type = proto::PostprocessRequest; - auto operator()(const std::string& gcode) const + value_type operator()(const std::string& gcode) const { value_type message{}; message.set_gcode_word(gcode); - return std::make_shared(message); + return message; } }; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index d2a1452f23..7a1b884963 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -81,7 +81,7 @@ class SlotProxy [&]() -> boost::asio::awaitable { grpc::ClientContext client_context{}; - request_process_t request {};// { request_converter_(std::forward(args)...) }; + request_process_t request { request_converter_(std::forward(args)...) }; response_process_t response{}; status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); spdlog::info("Received response from plugin: {}", response.DebugString()); From e25cf92731b25dad864d389cc078bc9869da7f97 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 16 May 2023 17:07:45 +0200 Subject: [PATCH 036/656] Correctly initialise points CURA-10475 --- include/plugins/converters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index dec0e88e60..4c73df6043 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -73,7 +73,7 @@ struct simplify_response Polygon p{}; for (const auto& point : paths.path()) { - p.add(Point{ point.y(), point.y() }); + p.add(Point{ point.x(), point.y() }); } poly.add(p); } From 5b1643b8ab4163e0457412e66f282c29b304c386 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 08:06:52 +0200 Subject: [PATCH 037/656] Refactor validator and simplify slot classes - Change validator from struct to class and add constructor - Add TODO comment for hash and other checks - Change version range for simplify slot validator - Rename variables and add log messages in application and slotproxy [CURA-10475] --- include/plugins/slotproxy.h | 42 +++++++++++++++++++++---------------- include/plugins/slots.h | 2 +- include/plugins/validator.h | 27 ++++++++++++++---------- src/Application.cpp | 9 ++++---- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 7a1b884963..85d51e7b9e 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -62,11 +62,15 @@ class SlotProxy { using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; grpc::ClientContext client_context{}; - request_plugin_t request{}; - request.set_id(slot_id); + plugin_request plugin_request_conv{}; + request_plugin_t request{ plugin_request_conv(slot_id) }; response_plugin_t response{}; status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); - spdlog::info("Received response from plugin: {}", response.DebugString()); + plugin_response plugin_response_conv{}; + auto [version, _] = plugin_response_conv( response ); + spdlog::debug("Received response from plugin: {}", response.DebugString()); + valid_ = Validator{ version }; + spdlog::info("Plugin: {} validated: {}", slot_id, static_cast(valid_)); }, boost::asio::detached); grpc_context.run(); @@ -74,21 +78,23 @@ class SlotProxy auto operator()(auto&&... args) { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - boost::asio::co_spawn( - grpc_context, - [&]() -> boost::asio::awaitable - { - grpc::ClientContext client_context{}; - request_process_t request { request_converter_(std::forward(args)...) }; - response_process_t response{}; - status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); - spdlog::info("Received response from plugin: {}", response.DebugString()); - }, - boost::asio::detached); - grpc_context.run(); - + if (valid_) + { + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + grpc::ClientContext client_context{}; + request_process_t request{ request_converter_(std::forward(args)...) }; + response_process_t response{}; + status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); + spdlog::info("Received response from plugin: {}", response.DebugString()); + }, + boost::asio::detached); + grpc_context.run(); + } return 1; // FIXME: handle plugin not connected } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 4c225a1d20..85dfde53d6 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -21,7 +21,7 @@ namespace cura::plugins { using simplify_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, + Validator<"<=0.0.1", "qwerty-azerty-temp-hash">, proto::Simplify::Stub, agrpc::RPC<&proto::Simplify::Stub::PrepareAsyncSimplify>, simplify_request, diff --git a/include/plugins/validator.h b/include/plugins/validator.h index 48ff6e8b69..6f436d129f 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H -#define CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H +#ifndef PLUGINS_VALIDATOR_H +#define PLUGINS_VALIDATOR_H #include "plugins/types.h" @@ -11,22 +11,27 @@ namespace cura::plugins { - +// TODO: Implement hash and other checks template -struct Validator +class Validator { - semver::version version{ "1.0.0" }; - std::string_view plugin_hash{}; - bool include_prerelease{ false }; - semver::range::detail::range version_range{ VersionRange.value }; +public: + constexpr Validator() noexcept = default; + constexpr explicit Validator(std::string_view version) : version_{ version }, valid_{ version_range_.satisfies(version_, include_prerelease_) } {}; constexpr operator bool() const noexcept { - // TODO: Add proper security checking - return version_range.satisfies(version, include_prerelease) && plugin_hash == PluginHash.value; + return valid_; } + +private: + semver::version version_{ "1.0.0" }; + std::string_view plugin_hash_{}; + bool include_prerelease_{ false }; + semver::range::detail::range version_range_{ VersionRange.value }; + bool valid_{ false }; }; } // namespace cura::plugins -#endif // CURAENGINE_INCLUDE_PLUGINS_VALIDATOR_H +#endif // PLUGINS_VALIDATOR_H diff --git a/src/Application.cpp b/src/Application.cpp index a34fe5696b..af8991eaa3 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -254,10 +254,11 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { // TODO: remove this - plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 5555), grpc::InsecureChannelCredentials())); - auto x = plugins::Slots::instance().get(); - Polygons poly; - auto y = x(poly, 100, 100); + plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 50010), grpc::InsecureChannelCredentials())); + auto simplify_plugin = plugins::Slots::instance().get(); + Polygons poly{}; + auto x = simplify_plugin(poly, 100, 100); + spdlog::info("simplified poly received"); } } // namespace cura \ No newline at end of file From fd6b31eb690d7e5fbfd1c08014d0a0f9d4adce36 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 08:26:06 +0200 Subject: [PATCH 038/656] Use native types for plugin converters - Add native_value_type aliases for plugin request and response types - Use native types in converter operators and slot proxy operator - Add a test polygon in application - Reorder include headers in slot proxy [CURA-10475] --- include/plugins/converters.h | 20 +++++++++++++------- include/plugins/slotproxy.h | 31 ++++++++++++++++++++----------- src/Application.cpp | 1 + 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 68552ba417..646a4d0018 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -15,8 +15,9 @@ namespace cura::plugins struct plugin_request { using value_type = proto::PluginRequest; + using native_value_type = cura::plugins::SlotID; - value_type operator()(const cura::plugins::proto::SlotID& slot_id) const + value_type operator()(const native_value_type & slot_id) const { value_type message{}; message.set_id(slot_id); @@ -27,8 +28,9 @@ struct plugin_request struct plugin_response { using value_type = proto::PluginResponse; + using native_value_type = std::pair; - auto operator()(const value_type& message) const + native_value_type operator()(const value_type& message) const { return std::make_pair( message.version(), message.plugin_hash() ); } @@ -37,8 +39,9 @@ struct plugin_response struct simplify_request { using value_type = proto::SimplifyRequest; + using native_value_type = Polygons; - value_type operator()(const Polygons& polygons, const size_t max_deviation, const size_t max_angle) const + value_type operator()(const native_value_type& polygons, const size_t max_deviation, const size_t max_angle) const { value_type message{}; message.set_max_deviation(max_deviation); @@ -64,10 +67,11 @@ struct simplify_request struct simplify_response { using value_type = proto::SimplifyResponse; + using native_value_type = Polygons; - auto operator()(const value_type& message) const + native_value_type operator()(const value_type& message) const { - Polygons poly{}; + native_value_type poly{}; for (const auto& paths : message.polygons().paths()) { Polygon p{}; @@ -84,8 +88,9 @@ struct simplify_response struct postprocess_request { using value_type = proto::PostprocessRequest; + using native_value_type = std::string; - value_type operator()(const std::string& gcode) const + value_type operator()(const native_value_type& gcode) const { value_type message{}; message.set_gcode_word(gcode); @@ -96,8 +101,9 @@ struct postprocess_request struct postprocess_response { using value_type = proto::PostprocessResponse; + using native_value_type = std::string; - auto operator()(const value_type& message) const + native_value_type operator()(const value_type& message) const { return message.gcode_word(); } diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 85d51e7b9e..094756646b 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -7,9 +7,9 @@ #include #include -#include -#include #include +#include +#include #include #include #include @@ -32,25 +32,32 @@ class SlotProxy { public: // type aliases for easy use + using value_type = typename Response::native_value_type; + using request_plugin_t = typename plugin_request::value_type; using response_plugin_t = typename plugin_response::value_type; - using request_converter_t = Request; + using request_process_t = typename Request::value_type; - using response_converter_t = Response; using response_process_t = typename Response::value_type; + + using request_converter_t = Request; + using response_converter_t = Response; + using validator_t = Validator; - using stub_t = Stub; + using process_stub_t = Stub; static inline constexpr plugins::SlotID slot_id{ Slot }; private: + validator_t valid_{}; request_converter_t request_converter_{}; response_converter_t response_converter_{}; - validator_t valid_{}; - proto::Plugin::Stub plugin_stub_; - stub_t process_stub_; + grpc::Status status_; + proto::Plugin::Stub plugin_stub_; + process_stub_t process_stub_; + public: SlotProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) { @@ -67,7 +74,7 @@ class SlotProxy response_plugin_t response{}; status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); plugin_response plugin_response_conv{}; - auto [version, _] = plugin_response_conv( response ); + auto [version, _] = plugin_response_conv(response); spdlog::debug("Received response from plugin: {}", response.DebugString()); valid_ = Validator{ version }; spdlog::info("Plugin: {} validated: {}", slot_id, static_cast(valid_)); @@ -76,8 +83,9 @@ class SlotProxy grpc_context.run(); } - auto operator()(auto&&... args) + value_type operator()(auto&&... args) { + value_type ret_value {}; if (valid_) { agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? @@ -91,11 +99,12 @@ class SlotProxy response_process_t response{}; status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); spdlog::info("Received response from plugin: {}", response.DebugString()); + ret_value = response_converter_(response); }, boost::asio::detached); grpc_context.run(); } - return 1; // FIXME: handle plugin not connected + return ret_value; // FIXME: handle plugin not connected } }; diff --git a/src/Application.cpp b/src/Application.cpp index af8991eaa3..3b1a1cd103 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -257,6 +257,7 @@ void Application::registerPlugins() plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 50010), grpc::InsecureChannelCredentials())); auto simplify_plugin = plugins::Slots::instance().get(); Polygons poly{}; + poly.paths = {{0,1}, {2,3}, {4,5}}; auto x = simplify_plugin(poly, 100, 100); spdlog::info("simplified poly received"); } From bc61987635234455e0bfe0633cd4ea04311d88ea Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 10:12:17 +0200 Subject: [PATCH 039/656] Throw on validator and communication errors Validator: Added a member variable semver_range_ to store the version range Moved the version_range_ member variable to semver_range_ Added a valid_version() function to check if the version satisfies the range Added a getVersion() function to retrieve the version as a string SlotProxy: Added an #include directive for Added exception handling for plugin validation and communication errors Throws a std::runtime_error if the plugin version is invalid or cannot be validated Throws a std::runtime_error if communication with the plugin fails Improved log messages for plugin responses These changes improve the error handling and validation in the SlotProxy class. [CURA-10475] --- include/plugins/slotproxy.h | 27 ++++++++++++++++++++++++--- include/plugins/validator.h | 25 ++++++++++++++++++------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 094756646b..35991633fa 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,6 +4,7 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H #define CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H +#include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -75,17 +77,31 @@ class SlotProxy status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); plugin_response plugin_response_conv{}; auto [version, _] = plugin_response_conv(response); - spdlog::debug("Received response from plugin: {}", response.DebugString()); + spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); valid_ = Validator{ version }; spdlog::info("Plugin: {} validated: {}", slot_id, static_cast(valid_)); }, boost::asio::detached); grpc_context.run(); + + if (! valid_) + { + if (! valid_.valid_version()) + { + throw std::runtime_error(fmt::format("Could not validate plugin '{}' due to an invalid version '{}', expected '{}'!", slot_id, valid_.getVersion(), valid_.version_range)); + } + throw std::runtime_error(fmt::format("Could not validate plugin '{}' for an unknown reason!", slot_id)); + } + + if (! status_.ok()) + { + throw std::runtime_error(fmt::format("Communication with plugin '{}' {}", slot_id, status_.error_message())); + } } value_type operator()(auto&&... args) { - value_type ret_value {}; + value_type ret_value{}; if (valid_) { agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? @@ -98,12 +114,17 @@ class SlotProxy request_process_t request{ request_converter_(std::forward(args)...) }; response_process_t response{}; status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); - spdlog::info("Received response from plugin: {}", response.DebugString()); + spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); ret_value = response_converter_(response); }, boost::asio::detached); grpc_context.run(); } + + if (! status_.ok()) + { + throw std::runtime_error(fmt::format("Communication with plugin '{}' failed, due: {}", slot_id, status_.error_message())); + } return ret_value; // FIXME: handle plugin not connected } }; diff --git a/include/plugins/validator.h b/include/plugins/validator.h index 6f436d129f..c074ddbda0 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -15,21 +15,32 @@ namespace cura::plugins template class Validator { + semver::range::detail::range semver_range_{ VersionRange.value }; + semver::version version_{ "1.0.0" }; + std::string_view plugin_hash_{}; + bool include_prerelease_{ false }; + bool valid_{ false }; + public: constexpr Validator() noexcept = default; - constexpr explicit Validator(std::string_view version) : version_{ version }, valid_{ version_range_.satisfies(version_, include_prerelease_) } {}; + constexpr explicit Validator(std::string_view version) : version_{ version }, valid_{ valid_version() } {}; constexpr operator bool() const noexcept { return valid_; } -private: - semver::version version_{ "1.0.0" }; - std::string_view plugin_hash_{}; - bool include_prerelease_{ false }; - semver::range::detail::range version_range_{ VersionRange.value }; - bool valid_{ false }; + [[nodiscard]] constexpr bool valid_version() const + { + return semver_range_.satisfies(version_, include_prerelease_); + } + + std::string getVersion() const + { + return version_.to_string(); + } + + static inline constexpr std::string_view version_range{ VersionRange.value }; }; } // namespace cura::plugins From 8d8ed12836dbcc4a9556cfd5fc73d4868d913056 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 13:54:16 +0200 Subject: [PATCH 040/656] Allow for default behaviour if no plugin is used This commit updates the plugin registration in the Application class in the Application.cpp file. The changes include: Adding support for default functions in the simplify_slot and postprocess_slot classes. Creating a slot_registry typedef to hold the slots for simplification and post-processing. Conditionally registering the simplify_slot based on whether the front-end starts a plugin. Registering the postprocess_slot unconditionally. Demonstrating the usage of the simplify_slot by invoking it with sample data. These changes allow for flexible plugin registration and usage in the application. Note: Further modifications may be required based on the specific requirements and configuration of the application. [CURA-10475] --- include/plugins/converters.h | 16 ++++++++------ include/plugins/slotproxy.h | 25 +++++++++++++++++++-- include/plugins/slots.h | 42 +++++++++++++++++++++++------------- plugin.proto | 10 ++++----- src/Application.cpp | 38 ++++++++++++++++++++++++++++---- 5 files changed, 98 insertions(+), 33 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 646a4d0018..e65ae58ed6 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -17,7 +17,7 @@ struct plugin_request using value_type = proto::PluginRequest; using native_value_type = cura::plugins::SlotID; - value_type operator()(const native_value_type & slot_id) const + value_type operator()(const native_value_type& slot_id) const { value_type message{}; message.set_id(slot_id); @@ -32,7 +32,7 @@ struct plugin_response native_value_type operator()(const value_type& message) const { - return std::make_pair( message.version(), message.plugin_hash() ); + return std::make_pair(message.version(), message.plugin_hash()); } }; @@ -41,17 +41,19 @@ struct simplify_request using value_type = proto::SimplifyRequest; using native_value_type = Polygons; - value_type operator()(const native_value_type& polygons, const size_t max_deviation, const size_t max_angle) const + value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const { value_type message{}; - message.set_max_deviation(max_deviation); - message.set_max_angle(max_angle); + message.set_max_resolution(max_resolution); + message.set_max_deviation(max_resolution); + message.set_max_area_deviation(max_resolution); for (const auto& polygon : polygons.paths) { - auto poly = message.polygons(); + auto* poly = message.mutable_polygons(); for (const auto& path : polygons.paths) { - auto* p = poly.add_paths(); + auto p = poly->add_paths(); + for (const auto& point : path) { auto* pt = p->add_path(); diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 35991633fa..45d5f94216 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -30,7 +30,7 @@ namespace cura::plugins { template -class SlotProxy +class PluginProxy { public: // type aliases for easy use @@ -61,7 +61,7 @@ class SlotProxy process_stub_t process_stub_; public: - SlotProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) + PluginProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) { agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? @@ -129,6 +129,27 @@ class SlotProxy } }; +template +class SlotProxy +{ + std::optional> plugin_{ std::nullopt }; + +public: + static inline constexpr plugins::SlotID slot_id{ Slot }; + + constexpr SlotProxy() noexcept = default; + SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; + + auto operator()(auto&&... args) + { + if (plugin_.has_value()) + { + return std::invoke(plugin_.value(), std::forward(args)...); + } + return std::invoke(Default, std::forward(args)...); + } +}; + } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 85dfde53d6..250f6a8877 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -1,43 +1,56 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTS_H -#define CURAENGINE_INCLUDE_PLUGINS_SLOTS_H +#ifndef PLUGINS_SLOTS_H +#define PLUGINS_SLOTS_H #include -#include #include #include +#include +#include + +#include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" -#include "plugins/converters.h" #include "plugin.grpc.pb.h" #include "plugin.pb.h" +#include "utils/Simplify.h" // TODO: remove need for including implementation headers + namespace cura::plugins { +//namespace details +//{ +//constexpr auto process_default = [](auto&& args...){ return std::forward(args); }; +//} +template using simplify_slot = SlotProxy, proto::Simplify::Stub, agrpc::RPC<&proto::Simplify::Stub::PrepareAsyncSimplify>, simplify_request, - simplify_response>; + simplify_response, + Default>; + +template using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, agrpc::RPC<&proto::Postprocess::Stub::PrepareAsyncPostprocess>, postprocess_request, - postprocess_response>; - -using slots_t = std::variant; - + postprocess_response, + Default>; +template class Slots { + using slots_t = std::variant;//, Postprocess>; + constexpr Slots() noexcept = default; std::unordered_map slots_{}; @@ -51,18 +64,17 @@ class Slots return instance; } - template - constexpr void set(T&& plugin) + constexpr void set(auto&& plugin) { - slots_.emplace(T::slot_id, std::forward(plugin)); + slots_.emplace(plugin.slot_id, std::forward(plugin)); } - template + template constexpr auto get() const { - return std::get(slots_.at(T::slot_id)); + return std::get(slots_.at(SlotID)); } }; } // namespace cura::plugins -#endif // CURAENGINE_INCLUDE_PLUGINS_SLOTS_H +#endif // PLUGINS_SLOTS_H diff --git a/plugin.proto b/plugin.proto index dba2fea2f2..39a15fb0bb 100644 --- a/plugin.proto +++ b/plugin.proto @@ -10,9 +10,8 @@ package cura.plugins.proto; // These are the different slots that plugins can hook into enum SlotID { - UNKNOWN = 0; - SIMPLIFY = 1; - POSTPROCESS = 2; + SIMPLIFY = 0; + POSTPROCESS = 1; } // --------------------------------------------------------------------- @@ -55,8 +54,9 @@ service Simplify { message SimplifyRequest { Polygons polygons = 1; - uint64 max_deviation = 2; - uint64 max_angle = 3; + uint64 max_resolution = 2; + uint64 max_deviation = 3; + uint64 max_area_deviation = 4; } // The SIMPLIFY slot response message diff --git a/src/Application.cpp b/src/Application.cpp index 3b1a1cd103..ff23770e21 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -251,14 +251,44 @@ void Application::startThreadPool(int nworkers) thread_pool = new ThreadPool(nthreads); } + + + void Application::registerPlugins() { // TODO: remove this - plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 50010), grpc::InsecureChannelCredentials())); - auto simplify_plugin = plugins::Slots::instance().get(); + + auto host = "localhost"; + auto port = 50010; + + constexpr auto simplify_default = [](const Polygons& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) + { + const Simplify simplify{ max_resolution, max_deviation, max_area_deviation }; + return simplify.polygon(polygons); + }; + using simplify_t = plugins::simplify_slot; + + constexpr auto postprocess_default = [](std::string word){ return word; }; + using postprocess_t = plugins::postprocess_slot; + + using slot_registry = plugins::Slots; + + if (true) // determine wat to register depending if front-end starts a plugin + { + slot_registry::instance().set(simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + } + else + { + slot_registry::instance().set(simplify_t{}); + } + slot_registry::instance().set(postprocess_t{}); + + auto simplify_plugin = slot_registry::instance().get(); Polygons poly{}; - poly.paths = {{0,1}, {2,3}, {4,5}}; - auto x = simplify_plugin(poly, 100, 100); + Polygon p{}; + p.poly = {{0,1}, {2,3}, {4,5}}; + poly.add(p); + auto x = simplify_plugin(poly, 100, 200, 300); spdlog::info("simplified poly received"); } From 04c177b02cead363ea452f0c054f4b5094fcc28b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 16:42:55 +0200 Subject: [PATCH 041/656] Use curaengine_grpc_definitions conan package [plugin.proto] Remove plugin.proto and related definitions This commit removes the plugin.proto file and its related definitions. The file contained definitions for plugin identification and validation, as well as messages and services for the SIMPLIFY and POSTPROCESS slots. These definitions are no longer needed and can be removed from the codebase. [conanfile.py] Add dependency on curaengine_grpc_definitions This commit adds a new dependency on the curaengine_grpc_definitions package. The package provides the latest gRPC definitions for CuraEngine plugins. This dependency is necessary for proper integration with the plugin system and ensures that the correct gRPC definitions are available during the build process. [include/plugins/slots.h] Update gRPC include paths This commit updates the include paths for gRPC headers in the slots.h file. It replaces the previous inclusion of "plugin.grpc.pb.h" with "simplify.grpc.pb.h" and "postprocess.grpc.pb.h". This change reflects the updated structure of the gRPC definitions and ensures that the correct headers are included for the SIMPLIFY and POSTPROCESS slots. [include/plugins/converters.h] Update gRPC include paths This commit updates the include paths for gRPC headers in the converters.h file. It replaces the previous inclusion of "plugin.grpc.pb.h" with "simplify.grpc.pb.h" and "postprocess.grpc.pb.h". This change reflects the updated structure of the gRPC definitions and ensures that the correct headers are included for the SIMPLIFY and POSTPROCESS slots. [CMakeLists.txt] Update gRPC protos variable This commit updates the GRPC_PROTOS variable in CMakeLists.txt. The variable now contains a list of all gRPC definitions provided by the curaengine_grpc_definitions package. The list is used during the build process to generate the necessary code for gRPC services and messages. The updated variable ensures that all required gRPC definitions are included in the build. [CURA-10475] --- CMakeLists.txt | 6 +-- conanfile.py | 5 +++ include/plugins/converters.h | 3 +- include/plugins/slots.h | 3 +- plugin.proto | 81 ------------------------------------ 5 files changed, 12 insertions(+), 86 deletions(-) delete mode 100644 plugin.proto diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bc58a4ed5..4a006328c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,8 @@ option(ENABLE_TESTING "Build with unit tests" OFF) option(EXTENSIVE_WARNINGS "Build with all warnings" ON) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) - +#set(GRPC_PROTOS "List of all gRPC definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") +message(STATUS ${GRPC_PROTOS}) # Generate the plugin types find_package(protobuf REQUIRED) @@ -22,8 +23,7 @@ asio_grpc_protobuf_generate( GENERATE_GRPC GENERATE_MOCK_CODE OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" - IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}" - PROTOS "${CMAKE_CURRENT_LIST_DIR}/plugin.proto" + PROTOS "${GRPC_PROTOS}" ) if (ENABLE_ARCUS) diff --git a/conanfile.py b/conanfile.py index 1dc0be75b3..926154d084 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,6 +2,7 @@ # CuraEngine is released under the terms of the AGPLv3 or higher from os import path +from pathlib import Path from conan import ConanFile from conan.errors import ConanInvalidConfiguration @@ -93,6 +94,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") def generate(self): deps = CMakeDeps(self) @@ -104,6 +106,9 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings + cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info + tc.variables["GRPC_PROTOS"] = ";".join([str(p) for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) + tc.generate() def layout(self): diff --git a/include/plugins/converters.h b/include/plugins/converters.h index e65ae58ed6..dc618e9c65 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -6,8 +6,9 @@ #include -#include "plugin.grpc.pb.h" #include "plugins/types.h" +#include "simplify.grpc.pb.h" +#include "postprocess.grpc.pb.h" namespace cura::plugins { diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 250f6a8877..1bddb0641f 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -17,7 +17,8 @@ #include "plugins/validator.h" #include "plugin.grpc.pb.h" -#include "plugin.pb.h" +#include "simplify.grpc.pb.h" +#include "postprocess.grpc.pb.h" #include "utils/Simplify.h" // TODO: remove need for including implementation headers diff --git a/plugin.proto b/plugin.proto deleted file mode 100644 index 39a15fb0bb..0000000000 --- a/plugin.proto +++ /dev/null @@ -1,81 +0,0 @@ -syntax = "proto3"; - -package cura.plugins.proto; - -// TODO: Move proto defintions to a separate repo -// TODO: Add error propagation -// TODO: Add progress reporting -// TODO: Add priority queueing (if multiple plugins are hooked up to the slot) - - -// These are the different slots that plugins can hook into -enum SlotID { - SIMPLIFY = 0; - POSTPROCESS = 1; -} - -// --------------------------------------------------------------------- -// This is the message which is sent to the plugin to request identification -service Plugin { - rpc Identify(PluginRequest) returns (PluginResponse) {} -} - -message PluginRequest { - SlotID id = 1; -} - -// This is the message which is sent back to CuraEngine to identify and validate the plugin -message PluginResponse { - string plugin_hash = 1; - string version = 2; -} - -// --------------------------------------------------------------------- -// Some basic types that are used in the messages below -message Point_2d { - sint64 x = 1; - sint64 y = 2; -} - -message Polygon { - repeated Point_2d path = 1; -} - -message Polygons { - repeated Polygon paths = 1; -} - -// --------------------------------------------------------------------- -// The SIMPLIFY slot request message - -service Simplify { - rpc Simplify(SimplifyRequest) returns (SimplifyResponse) {} -} - -message SimplifyRequest { - Polygons polygons = 1; - uint64 max_resolution = 2; - uint64 max_deviation = 3; - uint64 max_area_deviation = 4; -} - -// The SIMPLIFY slot response message -message SimplifyResponse { - Polygons polygons = 1; -} - -// --------------------------------------------------------------------- -// The POSTPROCESS slot request message - -service Postprocess { - rpc Postprocess(PostprocessRequest) returns (PostprocessResponse) {} -} - -message PostprocessRequest { - string gcode_word = 1; -} - -// The POSTPROCESS slot response message -message PostprocessResponse { - string gcode_word = 1; -} \ No newline at end of file From 9ffebb37e0bbe5806f3ed9dfbca2ab3dedf07479 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 17:05:26 +0200 Subject: [PATCH 042/656] Move pluginproxy to seperate header include/utils/concepts/generic.h: The concept definition hashable is updated to use the std::convertible_to concept instead of concepts::convertible_to which likely caused a compilation error. include/plugins/slots.h: Unused includes for range/v3 and simplify.grpc.pb.h are removed. The commented-out code block related to details::process_default is removed. A comment mentioning the removal of the need for implementation headers is added as a TODO reminder. Some formatting changes are made for code readability. include/plugins/slotproxy.h: The unnecessary include guards and includes for , , , , , , , , , , and are removed. The unused plugin.grpc.pb.h include is removed. Formatting changes are made for code readability. src/Application.cpp: The missing include is added to fix a compilation error related to creating a gRPC channel. No other changes were made in this file. [CURA-10475] --- include/plugins/pluginproxy.h | 119 ++++++++++++++++++++++++++++++ include/plugins/slotproxy.h | 121 ++----------------------------- include/plugins/slots.h | 11 +-- include/utils/concepts/generic.h | 2 +- src/Application.cpp | 1 + 5 files changed, 128 insertions(+), 126 deletions(-) create mode 100644 include/plugins/pluginproxy.h diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h new file mode 100644 index 0000000000..6c25d6996f --- /dev/null +++ b/include/plugins/pluginproxy.h @@ -0,0 +1,119 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PLUGINS_PLUGINPROXY_H +#define PLUGINS_PLUGINPROXY_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "plugin.grpc.pb.h" + +namespace cura::plugins +{ + +template Validator, class Stub, class Prepare, class Request, class Response> +class PluginProxy +{ +public: + // type aliases for easy use + using value_type = typename Response::native_value_type; + + using request_plugin_t = typename plugin_request::value_type; + using response_plugin_t = typename plugin_response::value_type; + + using request_process_t = typename Request::value_type; + using response_process_t = typename Response::value_type; + + using request_converter_t = Request; + using response_converter_t = Response; + + using validator_t = Validator; + using process_stub_t = Stub; + + static inline constexpr plugins::SlotID slot_id{ Slot }; + +private: + validator_t valid_{}; + request_converter_t request_converter_{}; + response_converter_t response_converter_{}; + + grpc::Status status_; + + proto::Plugin::Stub plugin_stub_; + process_stub_t process_stub_; + +public: + PluginProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) + { + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; + grpc::ClientContext client_context{}; + plugin_request plugin_request_conv{}; + request_plugin_t request{ plugin_request_conv(slot_id) }; + response_plugin_t response{}; + status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); + plugin_response plugin_response_conv{}; + auto [version, _] = plugin_response_conv(response); + spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); + valid_ = Validator{ version }; + spdlog::info("Plugin: {} validated: {}", slot_id, static_cast(valid_)); + }, + boost::asio::detached); + grpc_context.run(); + + if (! valid_) + { + throw std::runtime_error(fmt::format("Could not validate plugin '{}'", slot_id)); + } + + if (! status_.ok()) + { + throw std::runtime_error(fmt::format("Communication with plugin '{}' {}", slot_id, status_.error_message())); + } + } + + value_type operator()(auto&&... args) + { + value_type ret_value{}; + if (valid_) + { + agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? + + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + grpc::ClientContext client_context{}; + request_process_t request{ request_converter_(std::forward(args)...) }; + response_process_t response{}; + status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); + spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); + ret_value = response_converter_(response); + }, + boost::asio::detached); + grpc_context.run(); + } + + if (! status_.ok()) + { + throw std::runtime_error(fmt::format("Communication with plugin '{}' failed, due: {}", slot_id, status_.error_message())); + } + return ret_value; // FIXME: handle plugin not connected + } +}; +} // namespace cura::plugins + +#endif // PLUGINS_PLUGINPROXY_H diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 45d5f94216..a677a622c1 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -1,134 +1,23 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H -#define CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H +#ifndef PLUGINS_SLOTPROXY_H +#define PLUGINS_SLOTPROXY_H -#include #include #include +#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include #include "plugins/converters.h" +#include "plugins/pluginproxy.h" #include "plugins/types.h" #include "plugins/validator.h" -#include "plugin.grpc.pb.h" - namespace cura::plugins { -template -class PluginProxy -{ -public: - // type aliases for easy use - using value_type = typename Response::native_value_type; - - using request_plugin_t = typename plugin_request::value_type; - using response_plugin_t = typename plugin_response::value_type; - - using request_process_t = typename Request::value_type; - using response_process_t = typename Response::value_type; - - using request_converter_t = Request; - using response_converter_t = Response; - - using validator_t = Validator; - using process_stub_t = Stub; - - static inline constexpr plugins::SlotID slot_id{ Slot }; - -private: - validator_t valid_{}; - request_converter_t request_converter_{}; - response_converter_t response_converter_{}; - - grpc::Status status_; - - proto::Plugin::Stub plugin_stub_; - process_stub_t process_stub_; - -public: - PluginProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) - { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - boost::asio::co_spawn( - grpc_context, - [&]() -> boost::asio::awaitable - { - using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; - grpc::ClientContext client_context{}; - plugin_request plugin_request_conv{}; - request_plugin_t request{ plugin_request_conv(slot_id) }; - response_plugin_t response{}; - status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); - plugin_response plugin_response_conv{}; - auto [version, _] = plugin_response_conv(response); - spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); - valid_ = Validator{ version }; - spdlog::info("Plugin: {} validated: {}", slot_id, static_cast(valid_)); - }, - boost::asio::detached); - grpc_context.run(); - - if (! valid_) - { - if (! valid_.valid_version()) - { - throw std::runtime_error(fmt::format("Could not validate plugin '{}' due to an invalid version '{}', expected '{}'!", slot_id, valid_.getVersion(), valid_.version_range)); - } - throw std::runtime_error(fmt::format("Could not validate plugin '{}' for an unknown reason!", slot_id)); - } - - if (! status_.ok()) - { - throw std::runtime_error(fmt::format("Communication with plugin '{}' {}", slot_id, status_.error_message())); - } - } - - value_type operator()(auto&&... args) - { - value_type ret_value{}; - if (valid_) - { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - boost::asio::co_spawn( - grpc_context, - [&]() -> boost::asio::awaitable - { - grpc::ClientContext client_context{}; - request_process_t request{ request_converter_(std::forward(args)...) }; - response_process_t response{}; - status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); - spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); - ret_value = response_converter_(response); - }, - boost::asio::detached); - grpc_context.run(); - } - - if (! status_.ok()) - { - throw std::runtime_error(fmt::format("Communication with plugin '{}' failed, due: {}", slot_id, status_.error_message())); - } - return ret_value; // FIXME: handle plugin not connected - } -}; - template class SlotProxy { @@ -153,4 +42,4 @@ class SlotProxy } // namespace cura::plugins -#endif // CURAENGINE_INCLUDE_PLUGINS_SLOTPROXY_H +#endif // PLUGINS_SLOTPROXY_H diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 1bddb0641f..0aab9ec114 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -8,26 +8,19 @@ #include #include -#include -#include - #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" #include "plugin.grpc.pb.h" -#include "simplify.grpc.pb.h" #include "postprocess.grpc.pb.h" +#include "simplify.grpc.pb.h" #include "utils/Simplify.h" // TODO: remove need for including implementation headers namespace cura::plugins { -//namespace details -//{ -//constexpr auto process_default = [](auto&& args...){ return std::forward(args); }; -//} template using simplify_slot = SlotProxy class Slots { - using slots_t = std::variant;//, Postprocess>; + using slots_t = std::variant; //, Postprocess>; constexpr Slots() noexcept = default; std::unordered_map slots_{}; diff --git a/include/utils/concepts/generic.h b/include/utils/concepts/generic.h index 523d681db0..197ad918cc 100644 --- a/include/utils/concepts/generic.h +++ b/include/utils/concepts/generic.h @@ -14,7 +14,7 @@ namespace cura template concept hashable = requires(T value) { - { std::hash{}(value) } -> concepts::convertible_to; + { std::hash{}(value) } -> std::convertible_to; }; template diff --git a/src/Application.cpp b/src/Application.cpp index ff23770e21..81bc3fb078 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include From 7b9ae1253ade7d342d7e66ccba1f59c8f5697bc9 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 17:31:39 +0200 Subject: [PATCH 043/656] Refactor plugin-related code and update includes This commit makes several changes to the plugin-related code: 1. include/plugins/pluginproxy.h: - Updated the template parameters of the PluginProxy class to use grpc_convertable for Request and Response types. - This change ensures compatibility with gRPC conversion requirements. 2. include/utils/concepts/generic.h: - Renamed the include guard to UTILS_CONCEPTS_GENERIC_H to match the file name. - Updated the concept grpc_convertable to handle gRPC-specific conversion requirements. 3. include/plugins/slotproxy.h: - Added the include to use concepts in the code. - Updated the template parameters of the SlotProxy class to use grpc_convertable for Request and Response types. - This change ensures compatibility with gRPC conversion requirements. 4. include/plugins/converters.h: - Reordered the include statements for clarity. - Removed the unnecessary include for postprocess.grpc.pb.h. These changes aim to improve code clarity, ensure compatibility with gRPC conversion requirements, and update includes for consistency and readability. [CURA-10475] --- include/plugins/converters.h | 3 ++- include/plugins/pluginproxy.h | 2 +- include/plugins/slotproxy.h | 3 ++- include/utils/concepts/generic.h | 21 ++++++++------------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index dc618e9c65..90b0015dd4 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -7,8 +7,9 @@ #include #include "plugins/types.h" -#include "simplify.grpc.pb.h" + #include "postprocess.grpc.pb.h" +#include "simplify.grpc.pb.h" namespace cura::plugins { diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 6c25d6996f..d45c35835a 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,7 +19,7 @@ namespace cura::plugins { -template Validator, class Stub, class Prepare, class Request, class Response> +template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response> class PluginProxy { public: diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index a677a622c1..58aab07099 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,6 +4,7 @@ #ifndef PLUGINS_SLOTPROXY_H #define PLUGINS_SLOTPROXY_H +#include #include #include #include @@ -18,7 +19,7 @@ namespace cura::plugins { -template +template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response, auto Default> class SlotProxy { std::optional> plugin_{ std::nullopt }; diff --git a/include/utils/concepts/generic.h b/include/utils/concepts/generic.h index 197ad918cc..72c4f70dba 100644 --- a/include/utils/concepts/generic.h +++ b/include/utils/concepts/generic.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef CURAENGINE_GENERIC_H -#define CURAENGINE_GENERIC_H +#ifndef UTILS_CONCEPTS_GENERIC_H +#define UTILS_CONCEPTS_GENERIC_H #include #include @@ -17,19 +17,14 @@ concept hashable = requires(T value) { std::hash{}(value) } -> std::convertible_to; }; -template -concept receive_callable = requires(C callable, M message) +template +concept grpc_convertable = requires(T value) { - { callable(message) } -> std::same_as; - std::is_base_of_v; + requires std::semiregular; + requires std::semiregular; + requires std::semiregular; }; -template -concept send_callable = requires(C callable, S... args) -{ - { callable(args...) } -> std::same_as>; - std::is_base_of_v; -}; } // namespace cura -#endif // CURAENGINE_GENERIC_H +#endif // UTILS_CONCEPTS_GENERIC_H From f892b1b5d67074968ae1807b7f0b8052c53d462a Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 17 May 2023 17:37:41 +0200 Subject: [PATCH 044/656] Prerequisites for secure validator. Have secure communication when enterprise compile flag is set. The engine and plugins will both be started with the same shibolet, a plugin will then hash this with an openly avialable public key, so the engine can check that with the private key. It's assumed that the key-pair will only have to change when there has been a security violation. part of CURA-10475 --- CMakeLists.txt | 2 ++ conanfile.py | 1 + include/Application.h | 4 +++- src/Application.cpp | 40 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bc58a4ed5..09d3dce1f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) # Generate the plugin types find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) +find_package(grpc REQUIRED) asio_grpc_protobuf_generate( GENERATE_GRPC GENERATE_MOCK_CODE @@ -209,6 +210,7 @@ target_link_libraries(_CuraEngine scripta::scripta neargye-semver::neargye-semver asio-grpc::asio-grpc + grpc::grpc protobuf::libprotobuf $<$:GTest::gtest>) diff --git a/conanfile.py b/conanfile.py index 1dc0be75b3..4c8af34f10 100644 --- a/conanfile.py +++ b/conanfile.py @@ -93,6 +93,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") + self.requires("grpc/1.50.1") def generate(self): deps = CMakeDeps(self) diff --git a/include/Application.h b/include/Application.h index eaedbc79e5..13dbdc150c 100644 --- a/include/Application.h +++ b/include/Application.h @@ -14,6 +14,8 @@ class Communication; class Slice; class ThreadPool; +struct PluginSetupConfiguration; + /*! * A singleton class that serves as the starting point for all slicing. * @@ -135,7 +137,7 @@ class Application : NoCopy */ ~Application(); - void registerPlugins(); + void registerPlugins(const PluginSetupConfiguration& plugins_config); }; } //Cura namespace. diff --git a/src/Application.cpp b/src/Application.cpp index af8991eaa3..fbae3d36c5 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -3,6 +3,9 @@ #include "Application.h" +// TODO: Make this (enterprise) happen from build-system. +#define ENTERPRISE_BUILD 1 + #include #include #include @@ -25,9 +28,23 @@ #include "plugins/slots.h" +#ifdef ENTERPRISE_BUILD +#include "../secrets/private.pem.h" +#endif + namespace cura { +//TODO: Get these from the command-line and/or frontend. +struct PluginSetupConfiguration +{ +public: + std::string host = "localhost"; + uint64_t port = 50010UL; + + uint64_t shibolet = 56785678UL; // Assume both us and the plugin have been given this on start-up, in a scenario that needs more security. +} PLUGIN_CONFIG; + Application::Application() { auto dup_sink = std::make_shared(std::chrono::seconds{ 10 }); @@ -188,7 +205,7 @@ void Application::run(const size_t argc, char** argv) exit(1); } - registerPlugins(); + registerPlugins(PLUGIN_CONFIG); #ifdef ARCUS if (stringcasecompare(argv[1], "connect") == 0) @@ -251,14 +268,29 @@ void Application::startThreadPool(int nworkers) thread_pool = new ThreadPool(nthreads); } -void Application::registerPlugins() +// TODO: Where to put this in the structure, does this belong to 'Application'? +auto createChannel(const PluginSetupConfiguration& plugins_config) +{ +#ifdef ENTERPRISE_BUILD + auto creds_config = grpc::SslCredentialsOptions(); + creds_config.pem_cert_chain = "./plugins.crt"; // TODO: Release this next to the engine. (It's ok, the private one can still be inside.) + creds_config.pem_private_key = secrets::private_key; + auto channel_creds = grpc::SslCredentials(creds_config); +#else // NOT ENTERPRISE_BUILD + // TODO: Do we want security to be on always? + auto channel_creds = grpc::InsecureChannelCredentials(); +#endif + return grpc::CreateChannel(fmt::format("{}:{}", plugins_config.host, plugins_config.port), channel_creds); +} + +void Application::registerPlugins(const PluginSetupConfiguration& plugins_config) { // TODO: remove this - plugins::Slots::instance().set(grpc::CreateChannel(fmt::format("{}:{}", "localhost", 50010), grpc::InsecureChannelCredentials())); + plugins::Slots::instance().set(createChannel(plugins_config)); auto simplify_plugin = plugins::Slots::instance().get(); Polygons poly{}; auto x = simplify_plugin(poly, 100, 100); spdlog::info("simplified poly received"); } -} // namespace cura \ No newline at end of file +} // namespace cura From 2226ca0215f57ef272294004d9a170b89ae06732 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 17:55:50 +0200 Subject: [PATCH 045/656] Document the classes [CURA-10475] --- include/plugins/converters.h | 93 +++++++++++++++++++++++++++++------ include/plugins/pluginproxy.h | 50 ++++++++++++++++--- include/plugins/slotproxy.h | 39 +++++++++++++++ include/plugins/slots.h | 48 +++++++++++++++--- include/plugins/validator.h | 47 +++++++++++++++--- 5 files changed, 244 insertions(+), 33 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 90b0015dd4..ffb73d9190 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -14,11 +14,23 @@ namespace cura::plugins { +/** + * @brief A converter struct for plugin requests. + * + * The `plugin_request` struct provides a conversion function that converts a native slot ID + * to a `proto::PluginRequest` message. + */ struct plugin_request { - using value_type = proto::PluginRequest; - using native_value_type = cura::plugins::SlotID; - + using value_type = proto::PluginRequest; ///< The protobuf message type. + using native_value_type = cura::plugins::SlotID; ///< The native value type. + + /** + * @brief Converts a native slot ID to a `proto::PluginRequest` message. + * + * @param slot_id The native slot ID. + * @return The converted `proto::PluginRequest` message. + */ value_type operator()(const native_value_type& slot_id) const { value_type message{}; @@ -27,22 +39,49 @@ struct plugin_request } }; +/** + * @brief A converter struct for plugin responses. + * + * The `plugin_response` struct provides a conversion function that converts a `proto::PluginResponse` + * message to a native value type. + */ struct plugin_response { - using value_type = proto::PluginResponse; - using native_value_type = std::pair; - + using value_type = proto::PluginResponse; ///< The protobuf message type. + using native_value_type = std::pair; ///< The native value type. + + /** + * @brief Converts a `proto::PluginResponse` message to a native value type. + * + * @param message The `proto::PluginResponse` message. + * @return The converted native value. + */ native_value_type operator()(const value_type& message) const { return std::make_pair(message.version(), message.plugin_hash()); } }; +/** + * @brief A converter struct for simplify requests. + * + * The `simplify_request` struct provides a conversion function that converts native data for + * simplification (polygons and simplification parameters) to a `proto::SimplifyRequest` message. + */ struct simplify_request { - using value_type = proto::SimplifyRequest; - using native_value_type = Polygons; - + using value_type = proto::SimplifyRequest; ///< The protobuf message type. + using native_value_type = Polygons; ///< The native value type. + + /** + * @brief Converts native data for simplification to a `proto::SimplifyRequest` message. + * + * @param polygons The polygons to be simplified. + * @param max_resolution The maximum resolution for the simplified polygons. + * @param max_deviation The maximum deviation for the simplified polygons. + * @param max_area_deviation The maximum area deviation for the simplified polygons. + * @return The converted `proto::SimplifyRequest` message. + */ value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const { value_type message{}; @@ -68,11 +107,23 @@ struct simplify_request } }; +/** + * @brief A converter struct for simplify responses. + * + * The `simplify_response` struct provides a conversion function that converts a `proto::SimplifyResponse` + * message to a native value type. + */ struct simplify_response { - using value_type = proto::SimplifyResponse; - using native_value_type = Polygons; - + using value_type = proto::SimplifyResponse; ///< The protobuf message type. + using native_value_type = Polygons; ///< The native value type. + + /** + * @brief Converts a `proto::SimplifyResponse` message to a native value type. + * + * @param message The `proto::SimplifyResponse` message. + * @return The converted native value. + */ native_value_type operator()(const value_type& message) const { native_value_type poly{}; @@ -89,11 +140,23 @@ struct simplify_response } }; +/** + * @brief A converter struct for postprocess requests. + * + * The `postprocess_request` struct provides a conversion function that converts a native G-code string + * to a `proto::PostprocessRequest` message. + */ struct postprocess_request { - using value_type = proto::PostprocessRequest; - using native_value_type = std::string; - + using value_type = proto::PostprocessRequest; ///< The protobuf message type. + using native_value_type = std::string; ///< The native value type. + + /** + * @brief Converts a native G-code string to a `proto::PostprocessRequest` message. + * + * @param gcode The native G-code string. + * @return The converted `proto::PostprocessRequest` message. + */ value_type operator()(const native_value_type& gcode) const { value_type message{}; diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index d45c35835a..4241b7baca 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,6 +19,19 @@ namespace cura::plugins { +/** + * @brief A class template representing a proxy for a plugin. + * + * The PluginProxy class template facilitates communication with plugins by providing + * an interface for sending requests and receiving responses. It uses gRPC for communication. + * + * @tparam Slot The plugin slot ID. + * @tparam Validator The type used for validating the plugin. + * @tparam Stub The process stub type. + * @tparam Prepare The prepare type. + * @tparam Request The gRPC convertible request type. + * @tparam Response The gRPC convertible response type. + */ template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response> class PluginProxy { @@ -41,16 +54,28 @@ class PluginProxy static inline constexpr plugins::SlotID slot_id{ Slot }; private: - validator_t valid_{}; - request_converter_t request_converter_{}; - response_converter_t response_converter_{}; + validator_t valid_; ///< The validator object for plugin validation. + request_converter_t request_converter_; ///< The request converter object. + response_converter_t response_converter_; ///< The response converter object. - grpc::Status status_; + grpc::Status status_; ///< The gRPC status object. + + proto::Plugin::Stub plugin_stub_; ///< The gRPC stub for plugin communication. + process_stub_t process_stub_; ///< The gRPC stub for process communication. - proto::Plugin::Stub plugin_stub_; - process_stub_t process_stub_; public: + /** + * @brief Constructs a PluginProxy object. + * + * This constructor initializes the PluginProxy object by establishing communication + * channels with the plugin identified by the given slot ID. It performs plugin validation + * and checks for communication errors. + * + * @param channel A shared pointer to the gRPC channel for communication with the plugin. + * + * @throws std::runtime_error if the plugin fails validation or communication errors occur. + */ PluginProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) { agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? @@ -85,6 +110,19 @@ class PluginProxy } } + /** + * @brief Executes the plugin operation. + * + * This operator allows the PluginProxy object to be invoked as a callable, which sends + * a request to the plugin and waits for the response. The response is converted using + * the response_converter_ object, and the converted value is returned. + * + * @tparam Args The argument types for the plugin request. + * @param args The arguments for the plugin request. + * @return The converted response value. + * + * @throws std::runtime_error if communication with the plugin fails. + */ value_type operator()(auto&&... args) { value_type ret_value{}; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 58aab07099..0c375a150e 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -19,6 +19,21 @@ namespace cura::plugins { +/** + * @brief A class template representing a proxy for a plugin slot. + * + * The SlotProxy class template acts as a proxy for a plugin slot and provides an interface + * for communication with plugins assigned to the slot. It delegates plugin requests to the + * corresponding PluginProxy object and provides a default behavior when no plugin is available. + * + * @tparam Slot The plugin slot ID. + * @tparam Validator The type used for validating the plugin. + * @tparam Stub The process stub type. + * @tparam Prepare The prepare type. + * @tparam Request The gRPC convertible request type. + * @tparam Response The gRPC convertible response type. + * @tparam Default The default behavior when no plugin is available. + */ template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response, auto Default> class SlotProxy { @@ -27,9 +42,33 @@ class SlotProxy public: static inline constexpr plugins::SlotID slot_id{ Slot }; + /** + * @brief Default constructor. + * + * Constructs a SlotProxy object without initializing the plugin. + */ constexpr SlotProxy() noexcept = default; + + /** + * @brief Constructs a SlotProxy object with a plugin. + * + * Constructs a SlotProxy object and initializes the plugin using the provided gRPC channel. + * + * @param channel A shared pointer to the gRPC channel for communication with the plugin. + */ SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; + /** + * @brief Executes the plugin operation. + * + * This operator allows the SlotProxy object to be invoked as a callable, which delegates the + * plugin request to the corresponding PluginProxy object if available. If no plugin is available, + * it invokes the default behavior provided by the `Default` callable object. + * + * @tparam Args The argument types for the plugin request. + * @param args The arguments for the plugin request. + * @return The result of the plugin request or the default behavior. + */ auto operator()(auto&&... args) { if (plugin_.has_value()) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 0aab9ec114..25548c6151 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -17,11 +17,16 @@ #include "postprocess.grpc.pb.h" #include "simplify.grpc.pb.h" -#include "utils/Simplify.h" // TODO: remove need for including implementation headers - namespace cura::plugins { +/** + * @brief Alias for the Simplify slot. + * + * This alias represents the Simplify slot, which is used for simplifying polygons. + * + * @tparam Default The default behavior when no plugin is registered. + */ template using simplify_slot = SlotProxy, @@ -31,6 +36,13 @@ using simplify_slot = SlotProxy; +/** + * @brief Alias for the Postprocess slot. + * + * This alias represents the Postprocess slot, which is used for post-processing G-code. + * + * @tparam Default The default behavior when no plugin is registered. + */ template using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, @@ -40,29 +52,53 @@ using postprocess_slot = SlotProxy; -template +/** + * @brief Class for managing plugin slots. + * + * The `Slots` class provides functionality to manage plugin slots. It allows registering and retrieving plugins + * for specific slots. + * + * @tparam Simplify The Simplify slot type. + * @tparam Postprocess The Postprocess slot type. + */ +template // TODO: use variadic template args class Slots { - using slots_t = std::variant; //, Postprocess>; + using slots_t = std::variant; ///< The variant representing available slots. + std::unordered_map slots_{}; ///< The map storing registered slots. constexpr Slots() noexcept = default; - std::unordered_map slots_{}; - public: Slots(const Slots&) = delete; Slots(Slots&&) = delete; + /** + * @brief Returns the instance of the Slots class. + * + * @return The instance of the Slots class. + */ static Slots& instance() noexcept { static Slots instance{}; return instance; } + /** + * @brief Registers a plugin for the specified slot. + * + * @param plugin The plugin to register. + */ constexpr void set(auto&& plugin) { slots_.emplace(plugin.slot_id, std::forward(plugin)); } + /** + * @brief Retrieves the plugin for the specified slot. + * + * @tparam SlotID The ID of the slot. + * @return The plugin for the specified slot. + */ template constexpr auto get() const { diff --git a/include/plugins/validator.h b/include/plugins/validator.h index c074ddbda0..88c9fefcfb 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -11,35 +11,70 @@ namespace cura::plugins { -// TODO: Implement hash and other checks +/** + * @brief A class for validating plugin versions and hashes. + * + * The `Validator` class provides functionality to validate plugin versions and hashes against + * specified version ranges and plugin hashes. + * + * @tparam VersionRange The version range specified as a character range literal. + * @tparam PluginHash The plugin hash specified as a character range literal. + */ template class Validator { - semver::range::detail::range semver_range_{ VersionRange.value }; - semver::version version_{ "1.0.0" }; - std::string_view plugin_hash_{}; - bool include_prerelease_{ false }; - bool valid_{ false }; + semver::range::detail::range semver_range_{ VersionRange.value }; ///< The semver range object. + semver::version version_{ "1.0.0" }; ///< The version to validate. + std::string_view plugin_hash_{}; ///< The plugin hash to validate. TODO: implement and use + bool include_prerelease_{ false }; ///< Flag indicating whether to include prerelease versions. + bool valid_{ false }; ///< Flag indicating the validity of the version. public: + /** + * @brief Default constructor. + */ constexpr Validator() noexcept = default; + + /** + * @brief Constructor that sets the version to validate. + * + * @param version The version to validate. + */ constexpr explicit Validator(std::string_view version) : version_{ version }, valid_{ valid_version() } {}; + /** + * @brief Conversion operator to bool. + * + * @return True if the version is valid, false otherwise. + */ constexpr operator bool() const noexcept { return valid_; } + /** + * @brief Checks if the version is valid according to the specified version range. + * + * @return True if the version is valid, false otherwise. + */ [[nodiscard]] constexpr bool valid_version() const { return semver_range_.satisfies(version_, include_prerelease_); } + /** + * @brief Returns the version string. + * + * @return The version string. + */ std::string getVersion() const { return version_.to_string(); } + /** + * @brief The version range specified as a string view. + */ static inline constexpr std::string_view version_range{ VersionRange.value }; }; From 8a2f74062c55b625a220e654a92cc48489b97060 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 17 May 2023 18:00:50 +0200 Subject: [PATCH 046/656] Build: Make passing of .proto-filenames work on Windows. done as part of CURA-10475 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 81d90e752c..b11a6f4d7c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -108,7 +108,7 @@ def generate(self): tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info - tc.variables["GRPC_PROTOS"] = ";".join([str(p) for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) + tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\","/") for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) tc.generate() From 470ea3bc60ee86ba031c3198cd041378b979af51 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 17 May 2023 18:01:38 +0200 Subject: [PATCH 047/656] Use variadic SlotTypes in registry [CURA-10475] --- include/plugins/slots.h | 7 +++---- src/Application.cpp | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 25548c6151..3fc0a1fe87 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -58,13 +58,12 @@ using postprocess_slot = SlotProxy // TODO: use variadic template args +template class Slots { - using slots_t = std::variant; ///< The variant representing available slots. + using slots_t = std::variant; ///< The variant representing available slots. std::unordered_map slots_{}; ///< The map storing registered slots. constexpr Slots() noexcept = default; diff --git a/src/Application.cpp b/src/Application.cpp index 81bc3fb078..14bcb7827c 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -25,6 +25,7 @@ #include "utils/string.h" //For stringcasecompare. #include "plugins/slots.h" +#include "utils/Simplify.h" // TODO: remove when we're properly setting the plugins in the process namespace cura { From a80b699c586631ff93322b7244a002fb4b4c0a1f Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 17 May 2023 19:20:24 +0200 Subject: [PATCH 048/656] Load plugin validation certificate and 'chain'. part of CURA-10475 --- src/Application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index 52a95ac2a6..56eac34f7f 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -30,7 +30,8 @@ #include "plugins/slots.h" #ifdef ENTERPRISE_BUILD -#include "../secrets/private.pem.h" +#include "../secrets/plugins.crt.h" +#include "../secrets/plugins.key.h" #endif namespace cura @@ -274,7 +275,8 @@ auto createChannel(const PluginSetupConfiguration& plugins_config) { #ifdef ENTERPRISE_BUILD auto creds_config = grpc::SslCredentialsOptions(); - creds_config.pem_cert_chain = "./plugins.crt"; // TODO: Release this next to the engine. (It's ok, the private one can still be inside.) + creds_config.pem_root_certs = secrets::certificate; + creds_config.pem_cert_chain = secrets::certificate; creds_config.pem_private_key = secrets::private_key; auto channel_creds = grpc::SslCredentials(creds_config); #else // NOT ENTERPRISE_BUILD From b5531733bd6dc4de87d3e4fa1c0f414df59ed749 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 08:54:19 +0200 Subject: [PATCH 049/656] Moved default behaviour to slots.h [CURA-10475] --- include/plugins/slots.h | 23 +++++++++++++++++++++-- src/Application.cpp | 22 ++++------------------ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 3fc0a1fe87..d082238807 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -12,6 +12,7 @@ #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" +#include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed #include "plugin.grpc.pb.h" #include "postprocess.grpc.pb.h" @@ -19,6 +20,15 @@ namespace cura::plugins { +namespace details +{ +constexpr auto default_process = [](auto&& arg, auto&&...) { return std::forward(arg); }; + +constexpr auto simplify_default = [](const Polygons& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) +{ + const Simplify simplify{ max_resolution, max_deviation, max_area_deviation }; + return simplify.polygon(polygons); +}; /** * @brief Alias for the Simplify slot. @@ -27,7 +37,7 @@ namespace cura::plugins * * @tparam Default The default behavior when no plugin is registered. */ -template +template using simplify_slot = SlotProxy, proto::Simplify::Stub, @@ -43,7 +53,7 @@ using simplify_slot = SlotProxy +template using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, @@ -52,6 +62,8 @@ using postprocess_slot = SlotProxy; +} // namespace details + /** * @brief Class for managing plugin slots. * @@ -104,6 +116,13 @@ class Slots return std::get(slots_.at(SlotID)); } }; + +using simplify_t = details::simplify_slot; +using postprocess_t = details::postprocess_slot<>; + +// The Template arguments should be ordered in the same ordering as the SlotID enum +using slot_registry = plugins::Slots; + } // namespace cura::plugins #endif // PLUGINS_SLOTS_H diff --git a/src/Application.cpp b/src/Application.cpp index 14bcb7827c..2ae76d8964 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -25,7 +25,6 @@ #include "utils/string.h" //For stringcasecompare. #include "plugins/slots.h" -#include "utils/Simplify.h" // TODO: remove when we're properly setting the plugins in the process namespace cura { @@ -258,32 +257,19 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { - // TODO: remove this - + using plugins::slot_registry; auto host = "localhost"; auto port = 50010; - constexpr auto simplify_default = [](const Polygons& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) - { - const Simplify simplify{ max_resolution, max_deviation, max_area_deviation }; - return simplify.polygon(polygons); - }; - using simplify_t = plugins::simplify_slot; - - constexpr auto postprocess_default = [](std::string word){ return word; }; - using postprocess_t = plugins::postprocess_slot; - - using slot_registry = plugins::Slots; - if (true) // determine wat to register depending if front-end starts a plugin { - slot_registry::instance().set(simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); } else { - slot_registry::instance().set(simplify_t{}); + slot_registry::instance().set(plugins::simplify_t{}); } - slot_registry::instance().set(postprocess_t{}); + slot_registry::instance().set(plugins::postprocess_t{}); auto simplify_plugin = slot_registry::instance().get(); Polygons poly{}; From 1757694957ebcaeb14e28ef019b107f97e5947c2 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 16:18:53 +0200 Subject: [PATCH 050/656] Fixed anonymous namespaces in multiple translation units - Add default_process struct to slots.h - Change Default template parameter from auto to class in slots.h and slotproxy.h - Add default_process member to SlotProxy - Remove unused code in Application.cpp Parsing anonymous lambda's in the header, results in different slots registry types in different translation units. Which is kinda bad if it is a Singleton ;-) [CURA-10475] --- include/plugins/slotproxy.h | 5 +++-- include/plugins/slots.h | 21 +++++++++++++++------ src/Application.cpp | 17 +++++------------ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 0c375a150e..018af6292d 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -34,9 +34,10 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response, auto Default> +template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response, class Default> class SlotProxy { + Default default_process{}; std::optional> plugin_{ std::nullopt }; public: @@ -75,7 +76,7 @@ class SlotProxy { return std::invoke(plugin_.value(), std::forward(args)...); } - return std::invoke(Default, std::forward(args)...); + return std::invoke(default_process, std::forward(args)...); } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index d082238807..7c7ae0972e 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -8,11 +8,14 @@ #include #include +#include + #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed +#include "utils/NoCopy.h" #include "plugin.grpc.pb.h" #include "postprocess.grpc.pb.h" @@ -22,12 +25,18 @@ namespace cura::plugins { namespace details { -constexpr auto default_process = [](auto&& arg, auto&&...) { return std::forward(arg); }; +struct default_process +{ + constexpr auto operator()(auto&& arg, auto&&...){ return std::forward(arg); }; +}; -constexpr auto simplify_default = [](const Polygons& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) +struct simplify_default { - const Simplify simplify{ max_resolution, max_deviation, max_area_deviation }; - return simplify.polygon(polygons); + auto operator()(auto&& arg, auto&&... args) + { + const Simplify simplify{ std::forward(args)... }; + return simplify.polygon(std::forward(arg)); + } }; /** @@ -37,7 +46,7 @@ constexpr auto simplify_default = [](const Polygons& polygons, const coord_t max * * @tparam Default The default behavior when no plugin is registered. */ -template +template using simplify_slot = SlotProxy, proto::Simplify::Stub, @@ -53,7 +62,7 @@ using simplify_slot = SlotProxy +template using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, proto::Postprocess::Stub, diff --git a/src/Application.cpp b/src/Application.cpp index 2ae76d8964..a9f53ae6a5 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -257,27 +257,20 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { - using plugins::slot_registry; auto host = "localhost"; auto port = 50010; if (true) // determine wat to register depending if front-end starts a plugin { - slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); } else { - slot_registry::instance().set(plugins::simplify_t{}); + plugins::slot_registry::instance().set(plugins::simplify_t{}); } - slot_registry::instance().set(plugins::postprocess_t{}); - - auto simplify_plugin = slot_registry::instance().get(); - Polygons poly{}; - Polygon p{}; - p.poly = {{0,1}, {2,3}, {4,5}}; - poly.add(p); - auto x = simplify_plugin(poly, 100, 200, 300); - spdlog::info("simplified poly received"); + plugins::slot_registry::instance().set(plugins::postprocess_t{}); + + } } // namespace cura \ No newline at end of file From b633f6e9907323022f5e1bace2ff0f93e138a86a Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 16:20:00 +0200 Subject: [PATCH 051/656] Use the simplify plugin to simplify the layer polygons [CURA-10475] --- src/slicer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slicer.cpp b/src/slicer.cpp index 889429ceb0..4ae513a162 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -20,6 +20,8 @@ #include "utils/gettime.h" #include "utils/section_type.h" +#include "plugins/slots.h" + namespace cura { @@ -771,7 +773,9 @@ void SlicerLayer::makePolygons(const Mesh* mesh) polygons.erase(it, polygons.end()); // Finally optimize all the polygons. Every point removed saves time in the long run. - polygons = Simplify(mesh->settings).polygon(polygons); +// polygons = Simplify(mesh->settings).polygon(polygons); + auto simplify = plugins::slot_registry::instance().get(); + polygons = simplify(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), mesh->settings.get("meshfix_maximum_extrusion_area_deviation")); polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments From 05780ed35b59e34f8569830df6e1f045f8b8c96e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 16:37:12 +0200 Subject: [PATCH 052/656] Added fmt formatter for slot_id parsing the SlotID in `fmt::format` and/or `spdlog` will give a humanreadable name, should handy for logging and communication with front-end [CURA-10475] --- include/plugins/pluginproxy.h | 1 - include/plugins/types.h | 42 ++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 4241b7baca..0030343426 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -138,7 +138,6 @@ class PluginProxy request_process_t request{ request_converter_(std::forward(args)...) }; response_process_t response{}; status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); - spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); ret_value = response_converter_(response); }, boost::asio::detached); diff --git a/include/plugins/types.h b/include/plugins/types.h index 3c3c1b9f02..308b6c6563 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef CURAENGINE_INCLUDE_PLUGINS_TYPES_H -#define CURAENGINE_INCLUDE_PLUGINS_TYPES_H +#ifndef PLUGINS_TYPES_H +#define PLUGINS_TYPES_H #include #include @@ -35,4 +35,40 @@ struct CharRangeLiteral } // namespace cura::plugins -#endif // CURAENGINE_INCLUDE_PLUGINS_TYPES_H + +// Custom formatter for humanreadable slot_id's +template<> +struct fmt::formatter +{ + // The formatting function + template + auto format(cura::plugins::SlotID slot_id, FormatContext& ctx) + { + std::string slot_name; + + switch (slot_id) + { + case cura::plugins::SlotID::SIMPLIFY: + slot_name = "Simplify"; + break; + case cura::plugins::SlotID::POSTPROCESS: + slot_name = "Postprocess"; + break; + default: + slot_name = "Unknown"; + break; + } + + return fmt::format_to(ctx.out(), "{}", slot_name); + } + + // The parsing function + template + auto parse(ParseContext& ctx) + { + // Not implemented for simplicity in this example + return ctx.begin(); + } +}; + +#endif // PLUGINS_TYPES_H From 6e1c95da88619cac928482d55a5865cebdc78cdf Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 19:13:54 +0200 Subject: [PATCH 053/656] Added some benchmarking for the plugins and slots I ported the simplification algorithm to the plugin https://github.com/Ultimaker/curaengine_example_plugin with some small modifications to make it work with vectors of containers instead of our Polygons. and I wrote three different Benchmarks, guess which scenario was the best: 1st scenario = is our current simplify logic 2nd scenario = a plugin slot, which will default to the current simplify logic (scenario 1), because there is no plugin loaded. 3th scenario = a plugin slot, with the example plugin loaded. All scenarios would result in the same simplification, but it is a good indication that the slots/plugin logic as it is now seems to have minimum overhead. It is even quicker by a whooping 1.5 nanosecond :wink: ----------------------------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------------------------- SimplifyTestFixture/simplify_local 20.4 ns 20.4 ns 34223484 SimplifyTestFixture/simplify_slot_noplugin 20.2 ns 20.2 ns 34762914 SimplifyTestFixture/simplify_slot_localplugin 18.9 ns 18.9 ns 36288158 [CURA-10475] --- benchmark/CMakeLists.txt | 5 +- benchmark/main.cpp | 3 +- benchmark/simplify_benchmark.h | 86 ++++++++++++++++++++++++++++++++++ include/plugins/types.h | 3 +- src/SkeletalTrapezoidation.cpp | 2 +- 5 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 benchmark/simplify_benchmark.h diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 289b634dd7..4cb27361e2 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -5,6 +5,7 @@ message(STATUS "Building benchmarks...") find_package(benchmark REQUIRED) + add_executable(benchmarks main.cpp) -target_link_libraries(benchmarks PRIVATE _CuraEngine benchmark::benchmark) -target_include_directories(benchmarks PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file +target_link_libraries(benchmarks PRIVATE _CuraEngine benchmark::benchmark test_helpers) +target_include_directories(benchmarks PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/generated) \ No newline at end of file diff --git a/benchmark/main.cpp b/benchmark/main.cpp index 6d519d5de7..cdea597f35 100644 --- a/benchmark/main.cpp +++ b/benchmark/main.cpp @@ -1,8 +1,9 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include "infill_benchmark.h" #include "wall_benchmark.h" +#include "simplify_benchmark.h" #include // Run the benchmark diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h new file mode 100644 index 0000000000..7f71c5c153 --- /dev/null +++ b/benchmark/simplify_benchmark.h @@ -0,0 +1,86 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H +#define CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H + +#include + +#include +#include +#include + +#include "../tests/ReadTestPolygons.h" +#include "utils/Simplify.h" +#include "plugins/slots.h" + +namespace cura +{ +class SimplifyTestFixture : public benchmark::Fixture +{ +public: + const std::vector POLYGON_FILENAMES = { + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave_hole.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square_hole.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_triangle.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_two_squares.txt").string() + }; + + std::vector shapes; + + void SetUp(const ::benchmark::State& state) + { + readTestPolygons(POLYGON_FILENAMES, shapes); + } + + void TearDown(const ::benchmark::State& state) + { + } +}; + +BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_local)(benchmark::State& st) +{ + Simplify simplify(MM2INT(0.25), MM2INT(0.025), 50000); + for (auto _ : st) + { + for (const auto& polys : shapes) + { + auto simplified = simplify.polygon(polys); + } + } +} + +BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_local); + +BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State& st) +{ + plugins::slot_registry::instance().set(plugins::simplify_t{}); + auto simplify = plugins::slot_registry::instance().get(); + for (auto _ : st) + { + for (const auto& polys : shapes) + { + auto simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000); + } + } +} + +BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_noplugin); + +BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::State& st) +{ + auto host = "localhost"; + auto port = 50010; + plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + auto simplify = plugins::slot_registry::instance().get(); + for (auto _ : st) + { + for (const auto& polys : shapes) + { + auto simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000); + } + } +} + +BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_localplugin); +} // namespace cura +#endif // CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H diff --git a/include/plugins/types.h b/include/plugins/types.h index 308b6c6563..050a032d6e 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -4,10 +4,11 @@ #ifndef PLUGINS_TYPES_H #define PLUGINS_TYPES_H -#include #include #include +#include + #include "utils/IntPoint.h" #include "utils/concepts/generic.h" #include "utils/polygon.h" diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index 580c5781e3..48479df859 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -1091,7 +1091,7 @@ void SkeletalTrapezoidation::generateTransitionEnds(edge_t& edge, coord_t mid_po #ifdef DEBUG if (! generateTransitionEnd(edge, start_pos, end_pos, transition_half_length, mid_rest, end_rest, lower_bead_count, edge_transition_ends)) { - spdlog::warn("There must have been at least one direction in which the bead count is increasing enough for the transition to happen!"); + spdlog::debug("There must have been at least one direction in which the bead count is increasing enough for the transition to happen!"); } #else generateTransitionEnd(edge, start_pos, end_pos, transition_half_length, mid_rest, end_rest, lower_bead_count, edge_transition_ends); From b0826abcae8a0ccc827d9bc73d976ed570d28736 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 19:21:47 +0200 Subject: [PATCH 054/656] Skip with error for benchmark [CURA-10475] --- include/utils/views/densify.h | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 include/utils/views/densify.h diff --git a/include/utils/views/densify.h b/include/utils/views/densify.h new file mode 100644 index 0000000000..1301adfe5e --- /dev/null +++ b/include/utils/views/densify.h @@ -0,0 +1,89 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_INCLUDE_UTILS_VIEWS_DENSIFY_H +#define CURAENGINE_INCLUDE_UTILS_VIEWS_DENSIFY_H + +#include +#include +#include + +#include "utils/concepts/geometry.h" + +namespace cura::views +{ +namespace details +{ +template +struct PointsOnLineGenerator { + struct promise_type { + std::vector points; + std::size_t index = 0; + + PointsOnLineGenerator get_return_object() { return {this}; } + std::suspend_always initial_suspend() { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + void unhandled_exception() {} + + bool done() const { return index == points.size(); } + + T current_value() const { return points[index]; } + + void advance() { ++index; } + + std::suspend_always yield_value(T value) { + points.push_back(value); + return {}; + } + + void return_void() {} + }; + + using Handle = std::coroutine_handle; + + Handle handle; + + PointsOnLineGenerator(promise_type* p) + : handle(Handle::from_promise(*p)) {} + + ~PointsOnLineGenerator() { + if (handle) handle.destroy(); + } + + bool done() const { return handle.done(); } + + T operator()() { + T value = handle.promise().current_value(); + handle.promise().advance(); + if (!handle.done()) handle.resume(); + return value; + } +}; + +template +PointsOnLineGenerator generatePointsOnLine(const T& point_start, const T& point_end, size_t n) { + co_yield point_start; + + double dx = (point_end.X - point_start.X) / (n + 1); + double dy = (point_end.Y - point_start.Y) / (n + 1); + + for (int i = 1; i <= n; ++i) { + co_yield T{point_start.X + i * dx, point_end.Y + i * dy}; + } + + co_yield point_end; +} + +struct densify_view_fn +{ + template + constexpr auto operator()(Rng&& rng, size_t n) const + { + auto generator = generatePointsOnLine(rng, n); + } +}; + +} // namespace details +} // namespace cura::views + +#endif // CURAENGINE_INCLUDE_UTILS_VIEWS_DENSIFY_H From cc24c6ad40dbec6b0d46dda22616dc7828367865 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 18 May 2023 19:22:45 +0200 Subject: [PATCH 055/656] fix benchmark and unit test on runners When plugin isn't loaded [CURA-10475] --- benchmark/simplify_benchmark.h | 10 +++++++++- src/Application.cpp | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 7f71c5c153..608243fb3a 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -70,7 +70,15 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St { auto host = "localhost"; auto port = 50010; - plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + + try + { + plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + } + catch (std::runtime_error e) + { + st.SkipWithError(e.what()); + } auto simplify = plugins::slot_registry::instance().get(); for (auto _ : st) { diff --git a/src/Application.cpp b/src/Application.cpp index a9f53ae6a5..4efa87ff12 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -260,7 +260,7 @@ void Application::registerPlugins() auto host = "localhost"; auto port = 50010; - if (true) // determine wat to register depending if front-end starts a plugin + if (false) // determine wat to register depending if front-end starts a plugin { plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); } From e699aea13c31b30bf21d99075a6fad7a7a0d8404 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 09:24:34 +0200 Subject: [PATCH 056/656] Enable secure (no plugins) compilation This should be the default for Enterprise. [CURA-10475] --- CMakeLists.txt | 2 ++ conanfile.py | 11 +++++++++-- include/plugins/slots.h | 7 ++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a006328c9..eb3cd65770 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ AssureOutOfSourceBuilds() option(ENABLE_ARCUS "Enable support for ARCUS" ON) option(ENABLE_TESTING "Build with unit tests" OFF) option(EXTENSIVE_WARNINGS "Build with all warnings" ON) +option(ENABLE_PLUGINS "Build with all warnings" ON) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) #set(GRPC_PROTOS "List of all gRPC definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") @@ -161,6 +162,7 @@ target_include_directories(_CuraEngine target_compile_definitions(_CuraEngine PUBLIC $<$:ARCUS> + $<$:PLUGINS> CURA_ENGINE_VERSION=\"${CURA_ENGINE_VERSION}\" $<$:BUILD_TESTS> PRIVATE diff --git a/conanfile.py b/conanfile.py index 926154d084..30b5b55c12 100644 --- a/conanfile.py +++ b/conanfile.py @@ -28,15 +28,21 @@ class CuraEngineConan(ConanFile): "enable_arcus": [True, False], "enable_testing": [True, False], "enable_benchmarks": [True, False], - "enable_extensive_warnings": [True, False] + "enable_extensive_warnings": [True, False], + "enable_plugins": [True, False], } default_options = { "enable_arcus": True, "enable_testing": False, "enable_benchmarks": False, "enable_extensive_warnings": False, + "enable_plugins": True, } + def set_version(self): + if not self.version: + self.version = "5.4.0-alpha.1" + def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) copy(self, "Cura.proto", self.recipe_folder, self.export_sources_folder) @@ -80,7 +86,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/cura_10475") + self.requires("arcus/(latest)@ultimaker/cura_10475") # TODO: point to `testing` once the CURA-10475 from libArcus is main self.requires("clipper/6.4.2") self.requires("boost/1.81.0") self.requires("rapidjson/1.1.0") @@ -106,6 +112,7 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings + tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info tc.variables["GRPC_PROTOS"] = ";".join([str(p) for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 7c7ae0972e..15163cd098 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -110,7 +110,12 @@ class Slots */ constexpr void set(auto&& plugin) { - slots_.emplace(plugin.slot_id, std::forward(plugin)); + using plugin_t = decltype(plugin); +#ifdef PLUGINS + slots_.emplace(plugin.slot_id, std::forward(plugin)); +#else + slots_.emplace(plugin.slot_id, std::forward(plugin_t{})); // Allways create a default (not connected) plugin +#endif } /** From 559656754d02d4fcef6c23abfc1208e09371b395 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 09:25:16 +0200 Subject: [PATCH 057/656] Revert "Skip with error for benchmark" This reverts commit b0826abcae8a0ccc827d9bc73d976ed570d28736. --- include/utils/views/densify.h | 89 ----------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 include/utils/views/densify.h diff --git a/include/utils/views/densify.h b/include/utils/views/densify.h deleted file mode 100644 index 1301adfe5e..0000000000 --- a/include/utils/views/densify.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2023 UltiMaker -// CuraEngine is released under the terms of the AGPLv3 or higher - -#ifndef CURAENGINE_INCLUDE_UTILS_VIEWS_DENSIFY_H -#define CURAENGINE_INCLUDE_UTILS_VIEWS_DENSIFY_H - -#include -#include -#include - -#include "utils/concepts/geometry.h" - -namespace cura::views -{ -namespace details -{ -template -struct PointsOnLineGenerator { - struct promise_type { - std::vector points; - std::size_t index = 0; - - PointsOnLineGenerator get_return_object() { return {this}; } - std::suspend_always initial_suspend() { return {}; } - std::suspend_always final_suspend() noexcept { return {}; } - void unhandled_exception() {} - - bool done() const { return index == points.size(); } - - T current_value() const { return points[index]; } - - void advance() { ++index; } - - std::suspend_always yield_value(T value) { - points.push_back(value); - return {}; - } - - void return_void() {} - }; - - using Handle = std::coroutine_handle; - - Handle handle; - - PointsOnLineGenerator(promise_type* p) - : handle(Handle::from_promise(*p)) {} - - ~PointsOnLineGenerator() { - if (handle) handle.destroy(); - } - - bool done() const { return handle.done(); } - - T operator()() { - T value = handle.promise().current_value(); - handle.promise().advance(); - if (!handle.done()) handle.resume(); - return value; - } -}; - -template -PointsOnLineGenerator generatePointsOnLine(const T& point_start, const T& point_end, size_t n) { - co_yield point_start; - - double dx = (point_end.X - point_start.X) / (n + 1); - double dy = (point_end.Y - point_start.Y) / (n + 1); - - for (int i = 1; i <= n; ++i) { - co_yield T{point_start.X + i * dx, point_end.Y + i * dy}; - } - - co_yield point_end; -} - -struct densify_view_fn -{ - template - constexpr auto operator()(Rng&& rng, size_t n) const - { - auto generator = generatePointsOnLine(rng, n); - } -}; - -} // namespace details -} // namespace cura::views - -#endif // CURAENGINE_INCLUDE_UTILS_VIEWS_DENSIFY_H From bb31da329a1d22d7e6c6463954f5f13d769fa174 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 10:38:20 +0200 Subject: [PATCH 058/656] Fix failing integration tests The Slots need to be registered here as well, since we're skipping the normal registration step in this test [CURA-10475] --- tests/CMakeLists.txt | 2 +- tests/integration/SlicePhaseTest.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 357ee011a6..8e718d50b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,7 +51,7 @@ endif () add_library(test_helpers ${TESTS_HELPERS_SRC}) target_compile_definitions(test_helpers PUBLIC $<$:BUILD_TESTS> $<$:ARCUS>) -target_include_directories(test_helpers PUBLIC "../include") +target_include_directories(test_helpers PUBLIC "../include" ${CMAKE_BINARY_DIR}/generated) target_link_libraries(test_helpers PRIVATE _CuraEngine GTest::gtest GTest::gmock clipper::clipper) if (ENABLE_ARCUS) target_link_libraries(test_helpers PUBLIC arcus::arcus protobuf::libprotobuf) diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index c90a96fabe..8a9b4cd9c6 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -8,6 +8,7 @@ #include "utils/FMatrix4x3.h" // To load STL files. #include "utils/polygon.h" // Creating polygons to compare to sliced layers. #include "utils/polygonUtils.h" // Comparing similarity of polygons. +#include "plugins/slots.h" #include #include @@ -26,6 +27,9 @@ class SlicePhaseTest : public testing::Test { // Start the thread pool Application::getInstance().startThreadPool(); + plugins::slot_registry::instance().set(plugins::simplify_t{}); + plugins::slot_registry::instance().set(plugins::postprocess_t{}); + // Set up a scene so that we may request settings. Application::getInstance().current_slice = new Slice(1); From 2802bec1bd533be1bdccac2f51971ca3acb09d03 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 10:54:29 +0200 Subject: [PATCH 059/656] Use complex polygons to test simplification [CURA-10475] --- benchmark/simplify_benchmark.h | 13 +- tests/resources/slice_polygon_1.txt | 47 + tests/resources/slice_polygon_2.txt | 117 + tests/resources/slice_polygon_3.txt | 233 ++ tests/resources/slice_polygon_4.txt | 5045 +++++++++++++++++++++++++++ 5 files changed, 5451 insertions(+), 4 deletions(-) create mode 100644 tests/resources/slice_polygon_1.txt create mode 100644 tests/resources/slice_polygon_2.txt create mode 100644 tests/resources/slice_polygon_3.txt create mode 100644 tests/resources/slice_polygon_4.txt diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 608243fb3a..35c9a3b158 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -22,7 +22,9 @@ class SimplifyTestFixture : public benchmark::Fixture const std::vector POLYGON_FILENAMES = { std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave_hole.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square_hole.txt").string(), - std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_triangle.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_two_squares.txt").string() + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_triangle.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_two_squares.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_1.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_2.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_3.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_4.txt").string() }; std::vector shapes; @@ -42,9 +44,10 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_local)(benchmark::State& st) Simplify simplify(MM2INT(0.25), MM2INT(0.025), 50000); for (auto _ : st) { + Polygons simplified; for (const auto& polys : shapes) { - auto simplified = simplify.polygon(polys); + benchmark::DoNotOptimize(simplified = simplify.polygon(polys)); } } } @@ -57,9 +60,10 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State auto simplify = plugins::slot_registry::instance().get(); for (auto _ : st) { + Polygons simplified; for (const auto& polys : shapes) { - auto simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000); + benchmark::DoNotOptimize(simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } @@ -82,9 +86,10 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St auto simplify = plugins::slot_registry::instance().get(); for (auto _ : st) { + Polygons simplified; for (const auto& polys : shapes) { - auto simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000); + benchmark::DoNotOptimize(simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } diff --git a/tests/resources/slice_polygon_1.txt b/tests/resources/slice_polygon_1.txt new file mode 100644 index 0000000000..d07ee348a1 --- /dev/null +++ b/tests/resources/slice_polygon_1.txt @@ -0,0 +1,47 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +# + +A polygon from a real world example. This polygon is generated from tree support diff --git a/tests/resources/slice_polygon_2.txt b/tests/resources/slice_polygon_2.txt new file mode 100644 index 0000000000..a36c67170c --- /dev/null +++ b/tests/resources/slice_polygon_2.txt @@ -0,0 +1,117 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +# + +A polygon from a real world example. This polygon is generated from tree support diff --git a/tests/resources/slice_polygon_3.txt b/tests/resources/slice_polygon_3.txt new file mode 100644 index 0000000000..18d86abf5e --- /dev/null +++ b/tests/resources/slice_polygon_3.txt @@ -0,0 +1,233 @@ +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 diff --git a/tests/resources/slice_polygon_4.txt b/tests/resources/slice_polygon_4.txt new file mode 100644 index 0000000000..a6ce356288 --- /dev/null +++ b/tests/resources/slice_polygon_4.txt @@ -0,0 +1,5045 @@ +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 +x +v 127470 164447 +v 127806 164494 +v 128024 164722 +v 128075 165037 +v 127933 165382 +v 127468 165831 +v 127067 165975 +v 126530 165890 +v 126607 166299 +v 126462 166638 +v 126341 166754 +v 126682 167110 +v 126822 167452 +v 126776 167780 +v 126558 168001 +v 126227 168048 +v 125897 167909 +v 125594 167606 +v 125323 167879 +v 124886 168043 +v 124559 167994 +v 124316 167776 +v 124258 167468 +v 124401 167144 +v 124774 166786 +v 124586 166599 +v 124446 166266 +v 124497 165952 +v 124714 165719 +v 125045 165671 +v 125396 165812 +v 125532 165952 +v 125628 165856 +v 125980 165724 +v 126520 165797 +v 126442 165399 +v 126576 165081 +v 126726 164935 +v 126756 164822 +v 126986 164538 +v 127314 164412 +x +v 117979 155034 +v 118691 155162 +v 119350 155356 +v 119957 155618 +v 120564 155959 +v 121103 156355 +v 121609 156815 +v 122090 157380 +v 122445 157897 +v 122782 158541 +v 123021 159165 +v 123183 159797 +v 123284 160451 +v 123305 161000 +v 123305 161822 +v 123344 162220 +v 123457 162604 +v 123645 162964 +v 123891 163269 +v 124194 163522 +v 124540 163710 +v 124915 163825 +v 125305 163865 +v 147845 163861 +v 147845 166410 +v 79916 166411 +v 79356 166378 +v 78804 166282 +v 78265 166123 +v 77747 165904 +v 77256 165628 +v 76799 165297 +v 76381 164917 +v 72155 160597 +v 72155 156966 +v 79718 161223 +v 82189 162602 +v 82938 162982 +v 83715 163295 +v 84515 163541 +v 85333 163717 +v 86162 163825 +v 86997 163860 +v 109305 163865 +v 109695 163825 +v 110070 163710 +v 110416 163522 +v 110719 163269 +v 110970 162956 +v 111153 162604 +v 111267 162220 +v 111305 161823 +v 111305 161000 +v 111322 160543 +v 111385 160031 +v 111440 159736 +v 111578 159211 +v 111824 158543 +v 112171 157895 +v 112530 157368 +v 112970 156847 +v 113466 156389 +v 114045 155959 +v 114683 155600 +v 115327 155336 +v 115909 155165 +v 116608 155040 +v 117301 155000 +x +v 116798 157534 +v 116267 157654 +v 115775 157853 +v 115362 158089 +v 114935 158425 +v 114551 158841 +v 114282 159238 +v 114041 159725 +v 113883 160269 +v 113815 160744 +v 113815 161266 +v 113882 161746 +v 114048 162283 +v 114250 162707 +v 114554 163171 +v 114940 163584 +v 115371 163920 +v 115776 164148 +v 116295 164358 +v 116837 164472 +v 117305 164500 +v 117829 164466 +v 118339 164348 +v 118834 164149 +v 119279 163890 +v 119671 163585 +v 120057 163170 +v 120359 162708 +v 120561 162284 +v 120727 161747 +v 120794 161267 +v 120796 160745 +v 120721 160219 +v 120567 159719 +v 120329 159239 +v 120037 158807 +v 119677 158426 +v 119249 158090 +v 118837 157854 +v 118342 157654 +v 117813 157535 +v 117305 157500 +x +v 87502 90642 +v 87560 90841 +v 87699 90816 +v 88009 90915 +v 88376 90819 +v 88584 90852 +v 88648 90631 +v 88682 90595 +v 89550 90595 +v 89704 90718 +v 89827 90968 +v 89816 90968 +v 89758 91188 +v 89553 91335 +v 89251 91372 +v 89117 91332 +v 88963 91691 +v 88757 91854 +v 88794 92050 +v 88997 92113 +v 89094 92091 +v 89413 92113 +v 89986 92258 +v 90409 92644 +v 90519 92963 +v 90768 92950 +v 90866 92526 +v 91135 92253 +v 91195 91929 +v 90920 91688 +v 90770 91319 +v 90635 91357 +v 90352 91318 +v 90127 91161 +v 90065 90962 +v 90041 90963 +v 90169 90704 +v 90309 90595 +v 91204 90595 +v 91242 90634 +v 91302 90842 +v 91449 90817 +v 91756 90914 +v 92123 90820 +v 92332 90854 +v 92395 90631 +v 92430 90595 +v 93524 90595 +v 93576 90759 +v 93583 90759 +v 93461 91010 +v 93229 91184 +v 92889 91274 +v 92711 91690 +v 92409 91930 +v 92513 92055 +v 92652 92391 +v 93224 92227 +v 93787 92346 +v 94184 92718 +v 94327 93215 +v 94143 93731 +v 93894 93953 +v 94057 94459 +v 95000 93476 +v 94786 93272 +v 94615 92820 +v 94717 92381 +v 94893 92203 +v 94937 91969 +v 94623 91651 +v 94566 91294 +v 94204 91159 +v 93956 90960 +v 93834 90720 +v 93862 90595 +v 94938 90595 +v 94996 90663 +v 95062 90595 +v 96022 90595 +v 96126 90689 +v 96200 90595 +v 97045 90595 +v 97199 90718 +v 97322 90968 +v 97311 90968 +v 97253 91188 +v 97048 91335 +v 96746 91372 +v 96560 91318 +v 96511 91662 +v 96196 91979 +v 96308 92109 +v 96443 92526 +v 97010 92365 +v 97578 92484 +v 97939 92820 +v 98093 92422 +v 98172 92350 +v 98241 92157 +v 98558 91811 +v 98415 91688 +v 98266 91319 +v 98130 91357 +v 97847 91318 +v 97622 91161 +v 97560 90962 +v 97536 90963 +v 97664 90704 +v 97804 90595 +v 98699 90595 +v 98737 90634 +v 98797 90842 +v 98944 90817 +v 99252 90915 +v 99619 90819 +v 99827 90852 +v 99891 90631 +v 99926 90595 +v 101020 90595 +v 101071 90757 +v 101079 90757 +v 100958 91010 +v 100719 91187 +v 100384 91275 +v 100206 91692 +v 99851 91972 +v 100093 92200 +v 100207 92631 +v 100180 92706 +v 100329 92854 +v 100987 92614 +v 101511 92737 +v 101866 93141 +v 101978 93669 +v 101792 94240 +v 101545 94541 +v 101630 94942 +v 101563 95229 +v 101475 95547 +v 101634 96158 +v 101229 97596 +v 100444 98425 +v 99369 98762 +v 98344 98463 +v 97545 97615 +v 97257 96503 +v 97072 96453 +v 96681 96842 +v 95693 97154 +v 94771 96920 +v 94068 96221 +v 93794 95154 +v 93273 95601 +v 93187 95623 +v 93005 95943 +v 92269 96703 +v 91311 97012 +v 90412 96772 +v 89715 96051 +v 89482 95123 +v 89570 94841 +v 89005 94952 +v 88957 94936 +v 88540 95062 +v 87813 94885 +v 87441 94530 +v 87016 94665 +v 86658 94585 +v 86538 94799 +v 87488 95788 +v 87812 96787 +v 87559 97809 +v 86839 98526 +v 85816 98780 +v 84782 98446 +v 84039 98685 +v 83252 98491 +v 82654 97897 +v 82459 97114 +v 82726 96318 +v 82902 96134 +v 82922 96080 +v 83274 95733 +v 83316 95549 +v 83684 95128 +v 83347 95048 +v 83010 94773 +v 82958 94629 +v 82896 94578 +v 82739 94148 +v 82874 93728 +v 83234 93405 +v 83739 93294 +v 83981 93340 +v 84235 93364 +v 84245 93089 +v 84336 92724 +v 84104 92655 +v 83751 92327 +v 84069 92036 +v 84183 91898 +v 84584 91530 +v 84674 91555 +v 84820 91708 +v 85168 91841 +v 85391 92017 +v 85638 92261 +v 85749 92561 +v 85676 92869 +v 85883 93095 +v 86499 92486 +v 86587 92458 +v 86657 92382 +v 87407 92158 +v 87454 91935 +v 87173 91688 +v 87059 91413 +v 86896 91480 +v 86634 91452 +v 86444 91284 +v 86408 91042 +v 86531 90786 +v 86693 90633 +v 86716 90630 +v 87020 90595 +v 87457 90595 +x +v 85773 94292 +v 85528 94548 +v 86309 94777 +v 86438 94550 +v 85894 94043 + +# Big polygon \ No newline at end of file From 5b4793279dd583a71ab493c9da58b78bae6d78eb Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 13:32:19 +0200 Subject: [PATCH 060/656] Allow Cura front-end to set plugins (port & address) This can currently be done by setting the environment variables: - SIMPLIFY_ENABLE: default disabled, setting this to any value will enable the plugin - SIMPLIFY_ADDRESS: defaults to localhost - SIMPLIFY_PORT: defaults to 33700 - POSTPROCESS_ENABLE: default disabled, setting this to any value will enable the plugin - POSTPROCESS_ADDRESS: defaults to localhost - POSTPROCESS_PORT: defaults to 33701 [CURA-10475] --- Cura.proto | 13 +++++++++ src/Application.cpp | 27 ++++++++---------- src/communication/ArcusCommunication.cpp | 36 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/Cura.proto b/Cura.proto index 284b4eb9d5..5c82d7c112 100644 --- a/Cura.proto +++ b/Cura.proto @@ -8,12 +8,25 @@ message ObjectList repeated Setting settings = 2; // meshgroup settings (for one-at-a-time printing) } +enum SlotID { + SIMPLIFY = 0; + POSTPROCESS = 1; +} + +message EnginePlugin +{ + SlotID id = 1; + optional string address = 2; + optional uint32 port = 3; +} + message Slice { repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another SettingList global_settings = 2; // The global settings used for the whole print job repeated Extruder extruders = 3; // The settings sent to each extruder object repeated SettingExtruder limit_to_extruder = 4; // From which stack the setting would inherit if not defined per object + repeated EnginePlugin engine_plugins = 5; } message Extruder diff --git a/src/Application.cpp b/src/Application.cpp index 4efa87ff12..43d7c0e711 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -24,8 +23,6 @@ #include "utils/ThreadPool.h" #include "utils/string.h" //For stringcasecompare. -#include "plugins/slots.h" - namespace cura { @@ -257,18 +254,18 @@ void Application::startThreadPool(int nworkers) void Application::registerPlugins() { - auto host = "localhost"; - auto port = 50010; - - if (false) // determine wat to register depending if front-end starts a plugin - { - plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); - } - else - { - plugins::slot_registry::instance().set(plugins::simplify_t{}); - } - plugins::slot_registry::instance().set(plugins::postprocess_t{}); +// auto host = "localhost"; +// auto port = 50010; +// +// if (false) // determine wat to register depending if front-end starts a plugin +// { +// plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); +// } +// else +// { +// plugins::slot_registry::instance().set(plugins::simplify_t{}); +// } +// plugins::slot_registry::instance().set(plugins::postprocess_t{}); } diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index c1686d34e0..bc30f98d16 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -7,6 +7,8 @@ #include //To sleep while waiting for the connection. #include //To map settings to their extruder numbers for limit_to_extruder. +#include +#include #include #include "Application.h" //To get and set the current slice command. @@ -22,6 +24,8 @@ #include "settings/types/Velocity.h" //To send to layer view how fast stuff is printing. #include "utils/polygon.h" +#include "plugins/slots.h" + namespace cura { @@ -504,6 +508,38 @@ void ArcusCommunication::sliceNext() } spdlog::debug("Received a Slice message."); + // TODO: Use typemap + constexpr auto create_channel = [&](auto&&... args){ return grpc::CreateChannel(fmt::format("{}:{}", std::forward(args)...), grpc::InsecureChannelCredentials()); }; + for (const auto& plugin : slice_message->engine_plugins()) + { + if (plugin.has_address() && plugin.has_port()) + { + switch (plugin.id()) + { + case cura::proto::SlotID::SIMPLIFY: + plugins::slot_registry::instance().set(plugins::simplify_t{ create_channel(plugin.address(), plugin.port()) }); + break; + case cura::proto::SlotID::POSTPROCESS: + plugins::slot_registry::instance().set(plugins::postprocess_t{ create_channel(plugin.address(), plugin.port()) }); + break; + default: break; + } + } + else + { + switch (plugin.id()) + { + case cura::proto::SlotID::SIMPLIFY: + plugins::slot_registry::instance().set(plugins::simplify_t{ }); + break; + case cura::proto::SlotID::POSTPROCESS: + plugins::slot_registry::instance().set(plugins::postprocess_t{ }); + break; + default: break; + } + } + } + Slice slice(slice_message->object_lists().size()); Application::getInstance().current_slice = &slice; From 780364c6508006f970054bc0033a418b22526838 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 14:27:03 +0200 Subject: [PATCH 061/656] Removed register plugins from Application This is now done in the communication class Only implemented this for Arcus communication, not yet commandline [CURA-10475] --- include/Application.h | 2 -- src/Application.cpp | 23 ----------------------- 2 files changed, 25 deletions(-) diff --git a/include/Application.h b/include/Application.h index eaedbc79e5..6f67b799d0 100644 --- a/include/Application.h +++ b/include/Application.h @@ -134,8 +134,6 @@ class Application : NoCopy * This destroys the Communication instance along with it. */ ~Application(); - - void registerPlugins(); }; } //Cura namespace. diff --git a/src/Application.cpp b/src/Application.cpp index 43d7c0e711..0d152408da 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -186,8 +186,6 @@ void Application::run(const size_t argc, char** argv) exit(1); } - registerPlugins(); - #ifdef ARCUS if (stringcasecompare(argv[1], "connect") == 0) { @@ -249,25 +247,4 @@ void Application::startThreadPool(int nworkers) thread_pool = new ThreadPool(nthreads); } - - - -void Application::registerPlugins() -{ -// auto host = "localhost"; -// auto port = 50010; -// -// if (false) // determine wat to register depending if front-end starts a plugin -// { -// plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); -// } -// else -// { -// plugins::slot_registry::instance().set(plugins::simplify_t{}); -// } -// plugins::slot_registry::instance().set(plugins::postprocess_t{}); - - -} - } // namespace cura \ No newline at end of file From 1a4016c7459e850ef51c615e787730db0b5ff1b6 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 17:48:14 +0200 Subject: [PATCH 062/656] Fixing new polygons proto types WIP [CURA-10475] --- include/plugins/converters.h | 49 ++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index ffb73d9190..b8e9b8bd7b 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -5,6 +5,7 @@ #define PLUGINS_CONVERTERS_H #include +#include #include "plugins/types.h" @@ -88,19 +89,25 @@ struct simplify_request message.set_max_resolution(max_resolution); message.set_max_deviation(max_resolution); message.set_max_area_deviation(max_resolution); - for (const auto& polygon : polygons.paths) + + auto* msg_polygons = message.mutable_polygons(); + auto* msg_polygon = msg_polygons->add_polygons(); + auto* msg_outline_path = msg_polygon->mutable_outline()->add_path(); + + for (const auto& point : polygons.front()) { - auto* poly = message.mutable_polygons(); - for (const auto& path : polygons.paths) - { - auto p = poly->add_paths(); + msg_outline_path->set_x(point.X); + msg_outline_path->set_y(point.Y); + } - for (const auto& point : path) - { - auto* pt = p->add_path(); - pt->set_x(point.X); - pt->set_y(point.Y); - } + auto* msg_holes = msg_polygon->mutable_holes(); + for (const auto& polygon : polygons.paths | ranges::views::drop(1)) + { + auto* msg_path = msg_holes->Add()->add_path(); + for (const auto& point : polygon) + { + msg_path->set_x(point.X); + msg_path->set_y(point.Y); } } return message; @@ -127,14 +134,24 @@ struct simplify_response native_value_type operator()(const value_type& message) const { native_value_type poly{}; - for (const auto& paths : message.polygons().paths()) + for (const auto& paths : message.polygons().polygons()) { - Polygon p{}; - for (const auto& point : paths.path()) + Polygon o{}; + for (const auto& point : paths.outline().path()) + { + o.add(Point{ point.x(), point.y() }); + } + poly.add(o); + + for (const auto& hole : paths.holes()) { - p.add(Point{ point.x(), point.y() }); + Polygon h{}; + for (const auto& point : hole.path()) + { + h.add(Point{ point.x(), point.y() }); + } + poly.add(h); } - poly.add(p); } return poly; } From 6e15ca8521cdb6c7f0aaa037adce5157f0b32dd2 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 20:05:55 +0200 Subject: [PATCH 063/656] Fixed new polygons proto types [CURA-10475] --- include/plugins/converters.h | 14 +++++++++++--- include/plugins/pluginproxy.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index b8e9b8bd7b..ab30e61132 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -5,6 +5,7 @@ #define PLUGINS_CONVERTERS_H #include +#include #include #include "plugins/types.h" @@ -90,12 +91,18 @@ struct simplify_request message.set_max_deviation(max_resolution); message.set_max_area_deviation(max_resolution); + if (polygons.empty()) + { + return message; + } + auto* msg_polygons = message.mutable_polygons(); auto* msg_polygon = msg_polygons->add_polygons(); - auto* msg_outline_path = msg_polygon->mutable_outline()->add_path(); + auto* msg_outline = msg_polygon->mutable_outline(); - for (const auto& point : polygons.front()) + for (const auto& point : ranges::front(polygons.paths)) { + auto* msg_outline_path = msg_outline->add_path(); msg_outline_path->set_x(point.X); msg_outline_path->set_y(point.Y); } @@ -103,9 +110,10 @@ struct simplify_request auto* msg_holes = msg_polygon->mutable_holes(); for (const auto& polygon : polygons.paths | ranges::views::drop(1)) { - auto* msg_path = msg_holes->Add()->add_path(); + auto* msg_hole = msg_holes->Add(); for (const auto& point : polygon) { + auto* msg_path = msg_hole->add_path(); msg_path->set_x(point.X); msg_path->set_y(point.Y); } diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 0030343426..588cfecfa5 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -136,9 +136,11 @@ class PluginProxy { grpc::ClientContext client_context{}; request_process_t request{ request_converter_(std::forward(args)...) }; +// spdlog::debug("Request: {}", request.DebugString()); response_process_t response{}; status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); ret_value = response_converter_(response); +// spdlog::debug("Response: {}", response.DebugString()); }, boost::asio::detached); grpc_context.run(); From 0f8974abd9107cb04a0133675ead2f5be74cdc1e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 19 May 2023 20:11:04 +0200 Subject: [PATCH 064/656] Use port 33700 [CURA-10475] --- benchmark/simplify_benchmark.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 35c9a3b158..71cb18ee8d 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -73,7 +73,7 @@ BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_noplugin); BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::State& st) { auto host = "localhost"; - auto port = 50010; + auto port = 33700; try { From 9c79d48626450e8aef8185d1ea53819d25d73b32 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 20 May 2023 12:26:08 +0200 Subject: [PATCH 065/656] Update protobuf and grpc files This commit updates the protobuf and grpc files to reflect the changes made on the arcus_replacement branch of the CuraEngine_grpc_definitions repository. The changes were necessary to ensure compatibility and consistency with the updated definitions. This branch now uses the by @casperlamboo proposed new Protobuf types, but modified, such that we can start pulling out libArcus and pyArcus. Codebase consistency: The modifications made in this commit ensure that the protobuf and grpc files used in the current branch align with the definitions present in the arcus_replacement branch of the CuraEngine_grpc_definitions repository. [CURA-10475] --- conanfile.py | 2 +- include/plugins/converters.h | 33 ++++++++++++++++++---------- include/plugins/pluginproxy.h | 41 +++++++++++++++++------------------ include/plugins/slotproxy.h | 4 ++-- include/plugins/slots.h | 6 +++-- include/plugins/validator.h | 3 +-- 6 files changed, 50 insertions(+), 39 deletions(-) diff --git a/conanfile.py b/conanfile.py index 30b5b55c12..a335e21923 100644 --- a/conanfile.py +++ b/conanfile.py @@ -100,7 +100,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/arcus_replacement") def generate(self): deps = CMakeDeps(self) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index ab30e61132..8aca64fe87 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -4,7 +4,9 @@ #ifndef PLUGINS_CONVERTERS_H #define PLUGINS_CONVERTERS_H -#include +#include +#include + #include #include @@ -22,10 +24,12 @@ namespace cura::plugins * The `plugin_request` struct provides a conversion function that converts a native slot ID * to a `proto::PluginRequest` message. */ +template struct plugin_request { - using value_type = proto::PluginRequest; ///< The protobuf message type. + using value_type = proto::PluginRequest; ///< The protobuf message type. using native_value_type = cura::plugins::SlotID; ///< The native value type. + const std::string slot_version_range{ SlotVersionRng.value }; /** * @brief Converts a native slot ID to a `proto::PluginRequest` message. @@ -36,7 +40,8 @@ struct plugin_request value_type operator()(const native_value_type& slot_id) const { value_type message{}; - message.set_id(slot_id); + message.set_slot_version_range(slot_version_range); + message.set_slot_id(slot_id); return message; } }; @@ -50,7 +55,7 @@ struct plugin_request struct plugin_response { using value_type = proto::PluginResponse; ///< The protobuf message type. - using native_value_type = std::pair; ///< The native value type. + using native_value_type = std::tuple; ///< The native value type. /** * @brief Converts a `proto::PluginResponse` message to a native value type. @@ -60,7 +65,7 @@ struct plugin_response */ native_value_type operator()(const value_type& message) const { - return std::make_pair(message.version(), message.plugin_hash()); + return { message.slot_id(), message.plugin_name(), message.slot_version(), message.plugin_version() }; } }; @@ -70,10 +75,12 @@ struct plugin_response * The `simplify_request` struct provides a conversion function that converts native data for * simplification (polygons and simplification parameters) to a `proto::SimplifyRequest` message. */ +template struct simplify_request { - using value_type = proto::SimplifyRequest; ///< The protobuf message type. + using value_type = proto::SimplifyRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. + const std::string slot_version_range{ SlotVersionRng.value }; /** * @brief Converts native data for simplification to a `proto::SimplifyRequest` message. @@ -87,10 +94,7 @@ struct simplify_request value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const { value_type message{}; - message.set_max_resolution(max_resolution); - message.set_max_deviation(max_resolution); - message.set_max_area_deviation(max_resolution); - + message.set_slot_version_range(slot_version_range); if (polygons.empty()) { return message; @@ -108,7 +112,7 @@ struct simplify_request } auto* msg_holes = msg_polygon->mutable_holes(); - for (const auto& polygon : polygons.paths | ranges::views::drop(1)) + for (const auto& polygon : polygons.paths | ranges::views::drop(1)) { auto* msg_hole = msg_holes->Add(); for (const auto& point : polygon) @@ -118,6 +122,10 @@ struct simplify_request msg_path->set_y(point.Y); } } + + message.set_max_resolution(max_resolution); + message.set_max_deviation(max_resolution); + message.set_max_area_deviation(max_resolution); return message; } }; @@ -171,10 +179,12 @@ struct simplify_response * The `postprocess_request` struct provides a conversion function that converts a native G-code string * to a `proto::PostprocessRequest` message. */ +template struct postprocess_request { using value_type = proto::PostprocessRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. + const std::string slot_version_range{ SlotVersionRng.value }; /** * @brief Converts a native G-code string to a `proto::PostprocessRequest` message. @@ -185,6 +195,7 @@ struct postprocess_request value_type operator()(const native_value_type& gcode) const { value_type message{}; + message.set_slot_version_range(slot_version_range); message.set_gcode_word(gcode); return message; } diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 588cfecfa5..a46e2cf958 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -32,15 +32,17 @@ namespace cura::plugins * @tparam Request The gRPC convertible request type. * @tparam Response The gRPC convertible response type. */ -template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response> +template class PluginProxy { public: // type aliases for easy use using value_type = typename Response::native_value_type; - using request_plugin_t = typename plugin_request::value_type; - using response_plugin_t = typename plugin_response::value_type; + using validator_t = Validator; + + using request_plugin_t = typename proto::PluginRequest; + using response_plugin_t = typename proto::PluginResponse; using request_process_t = typename Request::value_type; using response_process_t = typename Response::value_type; @@ -48,20 +50,17 @@ class PluginProxy using request_converter_t = Request; using response_converter_t = Response; - using validator_t = Validator; - using process_stub_t = Stub; + using stub_t = Stub; static inline constexpr plugins::SlotID slot_id{ Slot }; private: - validator_t valid_; ///< The validator object for plugin validation. - request_converter_t request_converter_; ///< The request converter object. + validator_t valid_; ///< The validator object for plugin validation. + request_converter_t request_converter_; ///< The request converter object. response_converter_t response_converter_; ///< The response converter object. - grpc::Status status_; ///< The gRPC status object. - - proto::Plugin::Stub plugin_stub_; ///< The gRPC stub for plugin communication. - process_stub_t process_stub_; ///< The gRPC stub for process communication. + grpc::Status status_; ///< The gRPC status object. + stub_t stub_; ///< The gRPC stub for communication. public: @@ -76,7 +75,7 @@ class PluginProxy * * @throws std::runtime_error if the plugin fails validation or communication errors occur. */ - PluginProxy(std::shared_ptr channel) : plugin_stub_(channel), process_stub_(channel) + PluginProxy(std::shared_ptr channel) : stub_(channel) { agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? @@ -84,17 +83,17 @@ class PluginProxy grpc_context, [&]() -> boost::asio::awaitable { - using RPC = agrpc::RPC<&proto::Plugin::Stub::PrepareAsyncIdentify>; + using RPC = agrpc::RPC<&stub_t::PrepareAsyncIdentify>; grpc::ClientContext client_context{}; - plugin_request plugin_request_conv{}; + plugin_request plugin_request_conv{}; request_plugin_t request{ plugin_request_conv(slot_id) }; response_plugin_t response{}; - status_ = co_await RPC::request(grpc_context, plugin_stub_, client_context, request, response, boost::asio::use_awaitable); + status_ = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); plugin_response plugin_response_conv{}; - auto [version, _] = plugin_response_conv(response); + const auto& [slot_id, plugin_name, slot_version, plugin_version] = plugin_response_conv(response); spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); - valid_ = Validator{ version }; - spdlog::info("Plugin: {} validated: {}", slot_id, static_cast(valid_)); + valid_ = Validator{ slot_version }; + spdlog::info("Slot: '{}' with plugin: '{}' version: '{}' is validated: {}", slot_id, plugin_name, plugin_version, static_cast(valid_)); }, boost::asio::detached); grpc_context.run(); @@ -136,11 +135,11 @@ class PluginProxy { grpc::ClientContext client_context{}; request_process_t request{ request_converter_(std::forward(args)...) }; -// spdlog::debug("Request: {}", request.DebugString()); + // spdlog::debug("Request: {}", request.DebugString()); response_process_t response{}; - status_ = co_await Prepare::request(grpc_context, process_stub_, client_context, request, response, boost::asio::use_awaitable); + status_ = co_await Prepare::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); ret_value = response_converter_(response); -// spdlog::debug("Response: {}", response.DebugString()); + // spdlog::debug("Response: {}", response.DebugString()); }, boost::asio::detached); grpc_context.run(); diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 018af6292d..65f70fd291 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -34,11 +34,11 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template Validator, class Stub, class Prepare, grpc_convertable Request, grpc_convertable Response, class Default> +template class Validator, class Stub, class Prepare, template class Request, class Response, class Default> class SlotProxy { Default default_process{}; - std::optional> plugin_{ std::nullopt }; + std::optional, Stub, Prepare, Request, Response>> plugin_{ std::nullopt }; public: static inline constexpr plugins::SlotID slot_id{ Slot }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 15163cd098..8efdfa4fe2 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -48,7 +48,8 @@ struct simplify_default */ template using simplify_slot = SlotProxy, + "<=0.0.1", + Validator, proto::Simplify::Stub, agrpc::RPC<&proto::Simplify::Stub::PrepareAsyncSimplify>, simplify_request, @@ -64,7 +65,8 @@ using simplify_slot = SlotProxy using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", "qwerty-azerty-temp-hash">, + ">=1.0.0 <2.0.0 || >3.2.1", + Validator, proto::Postprocess::Stub, agrpc::RPC<&proto::Postprocess::Stub::PrepareAsyncPostprocess>, postprocess_request, diff --git a/include/plugins/validator.h b/include/plugins/validator.h index 88c9fefcfb..6a2eb42b4b 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -20,12 +20,11 @@ namespace cura::plugins * @tparam VersionRange The version range specified as a character range literal. * @tparam PluginHash The plugin hash specified as a character range literal. */ -template +template class Validator { semver::range::detail::range semver_range_{ VersionRange.value }; ///< The semver range object. semver::version version_{ "1.0.0" }; ///< The version to validate. - std::string_view plugin_hash_{}; ///< The plugin hash to validate. TODO: implement and use bool include_prerelease_{ false }; ///< Flag indicating whether to include prerelease versions. bool valid_{ false }; ///< Flag indicating the validity of the version. From 3ac6a0dade5433606ed7050fc69eacf8cb2cef46 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 21 May 2023 18:48:29 +0200 Subject: [PATCH 066/656] Added ValidatorException [CURA-10475] --- include/plugins/exception.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 include/plugins/exception.h diff --git a/include/plugins/exception.h b/include/plugins/exception.h new file mode 100644 index 0000000000..4aa601298e --- /dev/null +++ b/include/plugins/exception.h @@ -0,0 +1,36 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_CONCEPTS_GRAPH_H +#define UTILS_CONCEPTS_GRAPH_H + +#include + +#include +#include +#include + +#include "plugins/types.h" + +namespace cura::plugins::exceptions +{ + +class ValidatorException : public std::exception +{ + std::string msg_; + +public: + ValidatorException(auto validator, std::string plugin_name, const std::string& plugin_version, const std::string& plugin_target) noexcept + : msg_(fmt::format("Plugin {} '{}' at {} failed validation: {}", plugin_name, plugin_version, plugin_target, validator.what())) + { + } + + virtual const char* what() const noexcept override + { + return msg_.c_str(); + } +}; + +} // namespace cura::plugins::exceptions + +#endif // UTILS_CONCEPTS_GRAPH_H \ No newline at end of file From 476d81b964bb53fdbadd3a5d4f77cdf99e68b27f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 21 May 2023 18:52:18 +0200 Subject: [PATCH 067/656] Fix variant storage in array, using typelist This commit updates the include/plugins/slots.h file, making several changes and improvements. Added #include to include the necessary header. Removed the unnecessary utils/Simplify.h inclusion, with a comment to remove it once the simplify slot has been removed. Added utils/IntPoint.h inclusion for future use. Refactored the default_process struct's operator() for better code readability. Updated the simplify_default struct's definition. Refactored the simplify_slot and postprocess_slot aliases for better readability. Added the Typelist and Registry template classes for managing plugin slots. Added the SingletonRegistry template struct for ensuring a singleton instance of the registry. Updated the simplify_t and postprocess_t aliases to use the new registry classes. These changes improve the code structure, organization, and maintainability of the plugin slot management system in the Cura software." [CURA-10475] --- include/plugins/slots.h | 143 +++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 8efdfa4fe2..c7fa093333 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -4,6 +4,7 @@ #ifndef PLUGINS_SLOTS_H #define PLUGINS_SLOTS_H +#include #include #include #include @@ -14,8 +15,9 @@ #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" -#include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed +#include "utils/IntPoint.h" #include "utils/NoCopy.h" +#include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed #include "plugin.grpc.pb.h" #include "postprocess.grpc.pb.h" @@ -27,7 +29,10 @@ namespace details { struct default_process { - constexpr auto operator()(auto&& arg, auto&&...){ return std::forward(arg); }; + constexpr auto operator()(auto&& arg, auto&&...) + { + return std::forward(arg); + }; }; struct simplify_default @@ -47,14 +52,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using simplify_slot = SlotProxy, - simplify_request, - simplify_response, - Default>; +using simplify_slot = SlotProxy, simplify_request, simplify_response, Default>; /** * @brief Alias for the Postprocess slot. @@ -64,80 +62,93 @@ using simplify_slot = SlotProxy -using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", - Validator, - proto::Postprocess::Stub, - agrpc::RPC<&proto::Postprocess::Stub::PrepareAsyncPostprocess>, - postprocess_request, - postprocess_response, - Default>; +using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", Validator, proto::Postprocess::Stub, agrpc::RPC<&proto::Postprocess::Stub::PrepareAsyncPostprocess>, postprocess_request, postprocess_response, Default>; -} // namespace details +template +struct Typelist +{ +}; -/** - * @brief Class for managing plugin slots. - * - * The `Slots` class provides functionality to manage plugin slots. It allows registering and retrieving plugins - * for specific slots. - * - * @tparams SlotTypes The different slot types. - */ -template -class Slots +template class Unit> +class Registry; + +template class Unit> +class Registry, Unit> { - using slots_t = std::variant; ///< The variant representing available slots. - std::unordered_map slots_{}; ///< The map storing registered slots. +}; - constexpr Slots() noexcept = default; +template class Unit> +class Registry, Unit> : public Registry, Unit> +{ public: - Slots(const Slots&) = delete; - Slots(Slots&&) = delete; - - /** - * @brief Returns the instance of the Slots class. - * - * @return The instance of the Slots class. - */ - static Slots& instance() noexcept + using ValueType = T; + using Base = Registry, Unit>; + + template + Tp& get() { - static Slots instance{}; - return instance; + return get_type().value; } - /** - * @brief Registers a plugin for the specified slot. - * - * @param plugin The plugin to register. - */ - constexpr void set(auto&& plugin) + template + auto call(auto&&... args) { - using plugin_t = decltype(plugin); -#ifdef PLUGINS - slots_.emplace(plugin.slot_id, std::forward(plugin)); -#else - slots_.emplace(plugin.slot_id, std::forward(plugin_t{})); // Allways create a default (not connected) plugin -#endif + auto holder = get_type(); + return std::invoke(holder.value, std::forward(args)...); } - /** - * @brief Retrieves the plugin for the specified slot. - * - * @tparam SlotID The ID of the slot. - * @return The plugin for the specified slot. - */ - template - constexpr auto get() const +private: + template + Unit& get_type() { - return std::get(slots_.at(SlotID)); + return get_helper(std::is_same{}); } + + template + Unit& get_helper(std::true_type) + { + return value_; + } + + template + Unit& get_helper(std::false_type) + { + return Base::template get_type(); + } + + Unit value_; }; +template class Unit> +class SingletonRegistry +{ +public: + static Registry& instance() + { + static Registry instance; + return instance; + } + +private: + SingletonRegistry() + { + } +}; + +template +struct Holder +{ + T value; + // agrpc::GrpcContext context; +}; + +} // namespace details + using simplify_t = details::simplify_slot; using postprocess_t = details::postprocess_slot<>; -// The Template arguments should be ordered in the same ordering as the SlotID enum -using slot_registry = plugins::Slots; +using SlotTypes = details::Typelist; +using slot_registry = details::SingletonRegistry; } // namespace cura::plugins From aceef5066f86b3ebd5dcc22e022602df5097eb65 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 21 May 2023 18:53:23 +0200 Subject: [PATCH 068/656] Update the validator This commit modifies the include/plugins/validator.h file, introducing improvements and enhancements to the validator class used in plugin validation. Added the header for improved formatting capabilities. Rearranged the header includes for better organization. Modified the Validator class constructor to handle invalid formatted versions gracefully, providing an informative error message. Updated the valid_version() function to provide a more detailed error message when the version is not within the required range. Added a what() member function to retrieve the error message associated with an invalid version. Improved code readability and added appropriate comments. [CURA-10475] --- include/plugins/validator.h | 51 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/include/plugins/validator.h b/include/plugins/validator.h index 6a2eb42b4b..d6e791fcfa 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -4,10 +4,11 @@ #ifndef PLUGINS_VALIDATOR_H #define PLUGINS_VALIDATOR_H -#include "plugins/types.h" - +#include #include +#include "plugins/types.h" + namespace cura::plugins { @@ -25,8 +26,9 @@ class Validator { semver::range::detail::range semver_range_{ VersionRange.value }; ///< The semver range object. semver::version version_{ "1.0.0" }; ///< The version to validate. - bool include_prerelease_{ false }; ///< Flag indicating whether to include prerelease versions. + bool include_prerelease_{ true }; ///< Flag indicating whether to include prerelease versions. bool valid_{ false }; ///< Flag indicating the validity of the version. + std::string what_{}; public: /** @@ -39,7 +41,20 @@ class Validator * * @param version The version to validate. */ - constexpr explicit Validator(std::string_view version) : version_{ version }, valid_{ valid_version() } {}; + constexpr explicit Validator(std::string_view version) noexcept + { + auto semver_version = semver::from_string_noexcept(version); + if (semver_version.has_value()) + { + version_ = semver_version.value(); + valid_ = valid_version(); + } + else + { + valid_ = false; + what_ = fmt::format("Received invalid formatted version {} from plugin, expected version according to semver.", version); + } + }; /** * @brief Conversion operator to bool. @@ -51,30 +66,26 @@ class Validator return valid_; } - /** - * @brief Checks if the version is valid according to the specified version range. - * - * @return True if the version is valid, false otherwise. - */ - [[nodiscard]] constexpr bool valid_version() const + constexpr std::string_view what() const noexcept { - return semver_range_.satisfies(version_, include_prerelease_); + return what_; } + /** - * @brief Returns the version string. + * @brief Checks if the version is valid according to the specified version range. * - * @return The version string. + * @return True if the version is valid, false otherwise. */ - std::string getVersion() const + [[nodiscard]] constexpr bool valid_version() noexcept { - return version_.to_string(); + auto valid_version{ semver_range_.satisfies(version_, include_prerelease_) }; + if (! valid_version) + { + what_ = fmt::format("CuraEngine requires a 'slot version' within the range of '{}', while the plugin reports to have version: '{}'", VersionRange.value, version_.to_string()); + } + return valid_version; } - - /** - * @brief The version range specified as a string view. - */ - static inline constexpr std::string_view version_range{ VersionRange.value }; }; } // namespace cura::plugins From a1842e78551be3ea3d48cb118c25ace8653855d1 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 21 May 2023 18:55:22 +0200 Subject: [PATCH 069/656] Update Slot/Plugin proxies to work with new exceptions This commit updates the pluginproxy.h and slotproxy.h header files with the following changes: In pluginproxy.h: Added necessary #include statements and dependencies. Introduced member variables to store plugin information. Modified the constructor to include plugin initialization and validation logic. Modified the operator() function to include plugin execution and error handling. In slotproxy.h: Added a required #include statement. Modified the constructor and operator() function to handle plugin availability and error handling. These changes enhance the functionality and error handling of the proxy classes for plugins. [CURA-10475] --- include/plugins/pluginproxy.h | 137 ++++++++++++++++++++-------------- include/plugins/slotproxy.h | 21 +++++- 2 files changed, 100 insertions(+), 58 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index a46e2cf958..57657f27cc 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -4,16 +4,18 @@ #ifndef PLUGINS_PLUGINPROXY_H #define PLUGINS_PLUGINPROXY_H -#include - #include #include #include #include #include +#include +#include #include #include +#include "plugins/exception.h" + #include "plugin.grpc.pb.h" namespace cura::plugins @@ -55,13 +57,16 @@ class PluginProxy static inline constexpr plugins::SlotID slot_id{ Slot }; private: - validator_t valid_; ///< The validator object for plugin validation. - request_converter_t request_converter_; ///< The request converter object. - response_converter_t response_converter_; ///< The response converter object. + validator_t valid_{}; ///< The validator object for plugin validation. + request_converter_t request_converter_{}; ///< The request converter object. + response_converter_t response_converter_{}; ///< The response converter object. grpc::Status status_; ///< The gRPC status object. stub_t stub_; ///< The gRPC stub for communication. - + std::string plugin_name_{}; ///< The name of the plugin. + std::string plugin_version_{}; ///< The version of the plugin. + std::string plugin_peer_{}; ///< The peer of the plugin. + bool plugin_ready_{ false }; ///< Whether the plugin is ready for communication. public: /** @@ -75,40 +80,22 @@ class PluginProxy * * @throws std::runtime_error if the plugin fails validation or communication errors occur. */ + constexpr PluginProxy() = default; + PluginProxy(std::shared_ptr channel) : stub_(channel) { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - boost::asio::co_spawn( - grpc_context, - [&]() -> boost::asio::awaitable - { - using RPC = agrpc::RPC<&stub_t::PrepareAsyncIdentify>; - grpc::ClientContext client_context{}; - plugin_request plugin_request_conv{}; - request_plugin_t request{ plugin_request_conv(slot_id) }; - response_plugin_t response{}; - status_ = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); - plugin_response plugin_response_conv{}; - const auto& [slot_id, plugin_name, slot_version, plugin_version] = plugin_response_conv(response); - spdlog::debug("Received response from plugin '{}': {}", slot_id, response.DebugString()); - valid_ = Validator{ slot_version }; - spdlog::info("Slot: '{}' with plugin: '{}' version: '{}' is validated: {}", slot_id, plugin_name, plugin_version, static_cast(valid_)); - }, - boost::asio::detached); + // Give the plugin some time to start up and initiate handshake + agrpc::GrpcContext grpc_context; + boost::asio::co_spawn(grpc_context, wait_for_plugin_ready(grpc_context, channel), boost::asio::detached); grpc_context.run(); - - if (! valid_) - { - throw std::runtime_error(fmt::format("Could not validate plugin '{}'", slot_id)); - } - - if (! status_.ok()) - { - throw std::runtime_error(fmt::format("Communication with plugin '{}' {}", slot_id, status_.error_message())); - } } + constexpr PluginProxy(const PluginProxy&) = default; + constexpr PluginProxy(PluginProxy&&) = default; + constexpr PluginProxy& operator=(const PluginProxy&) = default; + constexpr PluginProxy& operator=(PluginProxy&&) = default; + ~PluginProxy() = default; + /** * @brief Executes the plugin operation. * @@ -124,32 +111,72 @@ class PluginProxy */ value_type operator()(auto&&... args) { + agrpc::GrpcContext grpc_context; value_type ret_value{}; - if (valid_) + boost::asio::co_spawn( + grpc_context, + [&]() -> boost::asio::awaitable + { + const auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10); // TODO use deadline + grpc::ClientContext client_context{}; + request_process_t request{ request_converter_(std::forward(args)...) }; + response_process_t response{}; + status_ = co_await Prepare::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = response_converter_(response); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status_.ok()) { - agrpc::GrpcContext grpc_context; // TODO: figure out how the reuse the grpc_context, it is recommended to use 1 per thread. Maybe move this to the lot registry?? - - boost::asio::co_spawn( - grpc_context, - [&]() -> boost::asio::awaitable - { - grpc::ClientContext client_context{}; - request_process_t request{ request_converter_(std::forward(args)...) }; - // spdlog::debug("Request: {}", request.DebugString()); - response_process_t response{}; - status_ = co_await Prepare::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = response_converter_(response); - // spdlog::debug("Response: {}", response.DebugString()); - }, - boost::asio::detached); - grpc_context.run(); + throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_id, plugin_name_, plugin_version_, plugin_name_, status_.error_message())); } + return ret_value; + } - if (! status_.ok()) +private: + boost::asio::awaitable wait_for_plugin_ready(agrpc::GrpcContext& grpc_context, std::shared_ptr channel, const std::chrono::seconds& timeout = std::chrono::seconds(5)) + { + const auto deadline = std::chrono::system_clock::now() + timeout; + + const auto state = channel->GetState(true); + bool has_state_changed = co_await agrpc::notify_on_state_change(grpc_context, *channel, state, deadline); + if (has_state_changed) + { + auto plugin_connection_state = channel->GetState(true); + if (plugin_connection_state != grpc_connectivity_state::GRPC_CHANNEL_READY && plugin_connection_state != grpc_connectivity_state::GRPC_CHANNEL_CONNECTING) + { + throw std::runtime_error(fmt::format("Plugin for slot {} is not ready", slot_id)); + } + co_await handshake(grpc_context, timeout); + if (! valid_) + { + throw exceptions::ValidatorException(valid_, plugin_name_, plugin_version_, plugin_peer_); + } + plugin_ready_ = true; + } + } + + boost::asio::awaitable handshake(agrpc::GrpcContext& grpc_context, const std::chrono::seconds& timeout = std::chrono::seconds(5)) + { + const auto deadline = std::chrono::system_clock::now() + timeout; // TODO use deadline + using RPC = agrpc::RPC<&stub_t::PrepareAsyncIdentify>; + grpc::ClientContext client_context{}; + plugin_request plugin_request_conv{}; + request_plugin_t request{ plugin_request_conv(slot_id) }; + response_plugin_t response{}; + auto status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); + if (! status.ok()) { - throw std::runtime_error(fmt::format("Communication with plugin '{}' failed, due: {}", slot_id, status_.error_message())); + throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_id, plugin_name_, plugin_version_, plugin_name_, status_.error_message())); } - return ret_value; // FIXME: handle plugin not connected + spdlog::debug("Plugin responded with: {}", response.DebugString()); + plugin_response plugin_response_conv{}; + const auto& [rsp_slot_id, rsp_plugin_name, rsp_slot_version, rsp_plugin_version] = plugin_response_conv(response); + plugin_name_ = rsp_plugin_name; + plugin_version_ = rsp_plugin_version; + plugin_peer_ = client_context.peer(); + valid_ = Validator{ rsp_slot_version }; } }; } // namespace cura::plugins diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 65f70fd291..cac16792cb 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,6 +4,7 @@ #ifndef PLUGINS_SLOTPROXY_H #define PLUGINS_SLOTPROXY_H +#include #include #include #include @@ -48,7 +49,7 @@ class SlotProxy * * Constructs a SlotProxy object without initializing the plugin. */ - constexpr SlotProxy() noexcept = default; + SlotProxy() noexcept = default; /** * @brief Constructs a SlotProxy object with a plugin. @@ -59,6 +60,12 @@ class SlotProxy */ SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; + constexpr SlotProxy(const SlotProxy&) noexcept = default; + constexpr SlotProxy(SlotProxy&&) noexcept = default; + constexpr SlotProxy& operator=(const SlotProxy&) noexcept = default; + constexpr SlotProxy& operator=(SlotProxy&&) noexcept = default; + ~SlotProxy() = default; + /** * @brief Executes the plugin operation. * @@ -70,11 +77,19 @@ class SlotProxy * @param args The arguments for the plugin request. * @return The result of the plugin request or the default behavior. */ - auto operator()(auto&&... args) + auto operator()(auto&&... args) -> std::invoke_result_t { if (plugin_.has_value()) { - return std::invoke(plugin_.value(), std::forward(args)...); + try + { + return std::invoke(plugin_.value(), std::forward(args)...); + } + catch (const std::exception& e) + { + spdlog::error("Plugin error: {}", e.what()); + exit(1); + } } return std::invoke(default_process, std::forward(args)...); } From a1bc71315f9ff88d0602dc24f52202bbe2e1f95c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 03:25:54 +0200 Subject: [PATCH 070/656] Use invoke to forward call Sill need to figure out how to connect [CURA-10475] --- include/plugins/slots.h | 18 ++++++++---------- src/communication/ArcusCommunication.cpp | 19 +++---------------- src/slicer.cpp | 11 +++++++++-- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index c7fa093333..45a33802ae 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -6,24 +6,21 @@ #include #include -#include -#include - -#include #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" #include "utils/IntPoint.h" -#include "utils/NoCopy.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed #include "plugin.grpc.pb.h" #include "postprocess.grpc.pb.h" #include "simplify.grpc.pb.h" -namespace cura::plugins +namespace cura +{ +namespace plugins { namespace details { @@ -91,10 +88,10 @@ class Registry, Unit> : public Registry } template - auto call(auto&&... args) + auto invoke(auto&&... args) { auto holder = get_type(); - return std::invoke(holder.value, std::forward(args)...); + return std::invoke(holder.proxy, std::forward(args)...); } private: @@ -138,7 +135,7 @@ class SingletonRegistry template struct Holder { - T value; + T proxy; // agrpc::GrpcContext context; }; @@ -148,7 +145,8 @@ using simplify_t = details::simplify_slot; using postprocess_t = details::postprocess_slot<>; using SlotTypes = details::Typelist; -using slot_registry = details::SingletonRegistry; +} // namespace plugins +using slots = plugins::details::SingletonRegistry; } // namespace cura::plugins diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index bc30f98d16..29857b4d72 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -7,8 +7,8 @@ #include //To sleep while waiting for the connection. #include //To map settings to their extruder numbers for limit_to_extruder. -#include #include +#include #include #include "Application.h" //To get and set the current slice command. @@ -517,23 +517,10 @@ void ArcusCommunication::sliceNext() switch (plugin.id()) { case cura::proto::SlotID::SIMPLIFY: - plugins::slot_registry::instance().set(plugins::simplify_t{ create_channel(plugin.address(), plugin.port()) }); - break; - case cura::proto::SlotID::POSTPROCESS: - plugins::slot_registry::instance().set(plugins::postprocess_t{ create_channel(plugin.address(), plugin.port()) }); - break; - default: break; - } - } - else - { - switch (plugin.id()) - { - case cura::proto::SlotID::SIMPLIFY: - plugins::slot_registry::instance().set(plugins::simplify_t{ }); + //slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); break; case cura::proto::SlotID::POSTPROCESS: - plugins::slot_registry::instance().set(plugins::postprocess_t{ }); + //slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); break; default: break; } diff --git a/src/slicer.cpp b/src/slicer.cpp index 4ae513a162..c82c3e82a5 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -774,8 +774,15 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. // polygons = Simplify(mesh->settings).polygon(polygons); - auto simplify = plugins::slot_registry::instance().get(); - polygons = simplify(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), mesh->settings.get("meshfix_maximum_extrusion_area_deviation")); + // TODO: Clean up + auto x = slots::instance().invoke(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); +// auto y = x(); +// auto z = plugins::slot_registry::instance().call(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); + + + //(polygons, mesh->settings.get("meshfix_maxconnectimum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), mesh->settings.get("meshfix_maximum_extrusion_area_deviation")); +// auto simplify = plugins::slot_registry::instance().get(); +// polygons = simplify(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), mesh->settings.get("meshfix_maximum_extrusion_area_deviation")); polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments From 2160158541741cc14a017f45e917e6b9cb7830cd Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 10:00:35 +0200 Subject: [PATCH 071/656] Fixed the connect functionality In the file include/plugins/pluginproxy.h: Added the ranges::semiregular_box member variable stub_ to the PluginProxy class. Removed the plugin_ready_ member variable. In the file src/communication/ArcusCommunication.cpp: Uncommented the lines that connect the simplify and postprocess plugins. In the file include/plugins/slotproxy.h: Replaced the std::optional> member variable plugin_ with value_type. Updated the constructors and assignment operators accordingly. In the file src/slicer.cpp: Removed the TODO comment and unnecessary code related to invoking the simplify_slot plugin. In the file include/plugins/slots.h: Updated the version requirements in the simplify_slot and postprocess_slot aliases. In the SingletonRegistry class: Added a connect member function to connect a plugin to a slot. [CURA-10475] --- include/plugins/pluginproxy.h | 38 +++++++++++++++++++----- include/plugins/slotproxy.h | 19 ++---------- include/plugins/slots.h | 32 ++++++++++---------- src/communication/ArcusCommunication.cpp | 4 +-- src/slicer.cpp | 11 +------ 5 files changed, 54 insertions(+), 50 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 57657f27cc..18601b9d47 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -62,11 +63,10 @@ class PluginProxy response_converter_t response_converter_{}; ///< The response converter object. grpc::Status status_; ///< The gRPC status object. - stub_t stub_; ///< The gRPC stub for communication. + ranges::semiregular_box stub_; ///< The gRPC stub for communication. std::string plugin_name_{}; ///< The name of the plugin. std::string plugin_version_{}; ///< The version of the plugin. std::string plugin_peer_{}; ///< The peer of the plugin. - bool plugin_ready_{ false }; ///< Whether the plugin is ready for communication. public: /** @@ -82,7 +82,7 @@ class PluginProxy */ constexpr PluginProxy() = default; - PluginProxy(std::shared_ptr channel) : stub_(channel) + explicit PluginProxy(std::shared_ptr channel) : stub_(channel) { // Give the plugin some time to start up and initiate handshake agrpc::GrpcContext grpc_context; @@ -91,9 +91,34 @@ class PluginProxy } constexpr PluginProxy(const PluginProxy&) = default; - constexpr PluginProxy(PluginProxy&&) = default; - constexpr PluginProxy& operator=(const PluginProxy&) = default; - constexpr PluginProxy& operator=(PluginProxy&&) = default; + constexpr PluginProxy(PluginProxy&&) noexcept = default; + constexpr PluginProxy& operator=(const PluginProxy& other) + { + if (this != &other) + { + valid_ = other.valid_; + status_ = other.status_; + stub_ = other.stub_; + plugin_name_ = other.plugin_name_; + plugin_version_ = other.plugin_version_; + plugin_peer_ = other.plugin_peer_; + } + return *this; + } + + constexpr PluginProxy& operator=(PluginProxy&& other) + { + if (this != &other) + { + valid_ = std::move(other.valid_); + status_ = std::move(other.status_); + stub_ = std::move(other.stub_); // FIXME: the stub should be moved + plugin_name_ = std::move(other.plugin_name_); + plugin_version_ = std::move(other.plugin_version_); + plugin_peer_ = std::move(other.plugin_peer_); + } + return *this; + } ~PluginProxy() = default; /** @@ -153,7 +178,6 @@ class PluginProxy { throw exceptions::ValidatorException(valid_, plugin_name_, plugin_version_, plugin_peer_); } - plugin_ready_ = true; } } diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index cac16792cb..58f6b5766e 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -39,7 +39,8 @@ template, Stub, Prepare, Request, Response>> plugin_{ std::nullopt }; + using value_type = PluginProxy, Stub, Prepare, Request, Response>; + std::optional plugin_{ std::nullopt }; public: static inline constexpr plugins::SlotID slot_id{ Slot }; @@ -60,12 +61,6 @@ class SlotProxy */ SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; - constexpr SlotProxy(const SlotProxy&) noexcept = default; - constexpr SlotProxy(SlotProxy&&) noexcept = default; - constexpr SlotProxy& operator=(const SlotProxy&) noexcept = default; - constexpr SlotProxy& operator=(SlotProxy&&) noexcept = default; - ~SlotProxy() = default; - /** * @brief Executes the plugin operation. * @@ -81,15 +76,7 @@ class SlotProxy { if (plugin_.has_value()) { - try - { - return std::invoke(plugin_.value(), std::forward(args)...); - } - catch (const std::exception& e) - { - spdlog::error("Plugin error: {}", e.what()); - exit(1); - } + return std::invoke(plugin_.value(), std::forward(args)...); } return std::invoke(default_process, std::forward(args)...); } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 45a33802ae..6abd20935a 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -49,7 +49,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using simplify_slot = SlotProxy, simplify_request, simplify_response, Default>; +using simplify_slot = SlotProxy, simplify_request, simplify_response, Default>; /** * @brief Alias for the Postprocess slot. @@ -59,7 +59,7 @@ using simplify_slot = SlotProxy -using postprocess_slot = SlotProxy=1.0.0 <2.0.0 || >3.2.1", Validator, proto::Postprocess::Stub, agrpc::RPC<&proto::Postprocess::Stub::PrepareAsyncPostprocess>, postprocess_request, postprocess_response, Default>; +using postprocess_slot = SlotProxy, postprocess_request, postprocess_response, Default>; template struct Typelist @@ -82,33 +82,38 @@ class Registry, Unit> : public Registry using Base = Registry, Unit>; template - Tp& get() + constexpr Tp& get() { - return get_type().value; + return get_type().proxy; } template - auto invoke(auto&&... args) + constexpr auto invoke(auto&&... args) { - auto holder = get_type(); - return std::invoke(holder.proxy, std::forward(args)...); + return std::invoke(get(), std::forward(args)...); + } + + template + void connect(auto&& plugin) + { + get_type().proxy = Tp { std::forward( std::move(plugin) ) }; } private: template - Unit& get_type() + constexpr Unit& get_type() { return get_helper(std::is_same{}); } template - Unit& get_helper(std::true_type) + constexpr Unit& get_helper(std::true_type) { return value_; } template - Unit& get_helper(std::false_type) + constexpr Unit& get_helper(std::false_type) { return Base::template get_type(); } @@ -127,16 +132,13 @@ class SingletonRegistry } private: - SingletonRegistry() - { - } + constexpr SingletonRegistry() = default; }; template struct Holder { T proxy; - // agrpc::GrpcContext context; }; } // namespace details @@ -148,6 +150,6 @@ using SlotTypes = details::Typelist; } // namespace plugins using slots = plugins::details::SingletonRegistry; -} // namespace cura::plugins +} // namespace cura #endif // PLUGINS_SLOTS_H diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 29857b4d72..cee329dd9f 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -517,10 +517,10 @@ void ArcusCommunication::sliceNext() switch (plugin.id()) { case cura::proto::SlotID::SIMPLIFY: - //slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); + slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); break; case cura::proto::SlotID::POSTPROCESS: - //slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); + slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); break; default: break; } diff --git a/src/slicer.cpp b/src/slicer.cpp index c82c3e82a5..de11c5f4dd 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -774,16 +774,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. // polygons = Simplify(mesh->settings).polygon(polygons); - // TODO: Clean up - auto x = slots::instance().invoke(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); -// auto y = x(); -// auto z = plugins::slot_registry::instance().call(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); - - - //(polygons, mesh->settings.get("meshfix_maxconnectimum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), mesh->settings.get("meshfix_maximum_extrusion_area_deviation")); -// auto simplify = plugins::slot_registry::instance().get(); -// polygons = simplify(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), mesh->settings.get("meshfix_maximum_extrusion_area_deviation")); - + polygons = slots::instance().invoke(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments // Clean up polylines for Surface Mode printing From 37695c3436e72e521816e7f8062fce5dd09c605f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 10:28:11 +0200 Subject: [PATCH 072/656] Made Base friend for CRTP of slots [CURA-10475] --- include/plugins/slots.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 6abd20935a..e51325f74a 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -80,6 +80,7 @@ class Registry, Unit> : public Registry public: using ValueType = T; using Base = Registry, Unit>; + friend Base; template constexpr Tp& get() @@ -99,7 +100,7 @@ class Registry, Unit> : public Registry get_type().proxy = Tp { std::forward( std::move(plugin) ) }; } -private: +protected: template constexpr Unit& get_type() { From 7f1f8299cbbf752985595e60572af53ff412b5e9 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 10:47:42 +0200 Subject: [PATCH 073/656] Setting the plugin is no longer needed. Since this is done when the registry instance [CURA-10475] --- tests/integration/SlicePhaseTest.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index 8a9b4cd9c6..23913d95f9 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -8,7 +8,6 @@ #include "utils/FMatrix4x3.h" // To load STL files. #include "utils/polygon.h" // Creating polygons to compare to sliced layers. #include "utils/polygonUtils.h" // Comparing similarity of polygons. -#include "plugins/slots.h" #include #include @@ -27,8 +26,6 @@ class SlicePhaseTest : public testing::Test { // Start the thread pool Application::getInstance().startThreadPool(); - plugins::slot_registry::instance().set(plugins::simplify_t{}); - plugins::slot_registry::instance().set(plugins::postprocess_t{}); // Set up a scene so that we may request settings. Application::getInstance().current_slice = new Slice(1); From 8719e64df4f780d00bfa8a8917e5877690e9f712 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 10:50:01 +0200 Subject: [PATCH 074/656] Fixed simplify benchmarks It now uses the new logic [CURA-10475] --- benchmark/simplify_benchmark.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 71cb18ee8d..9a5d715dda 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -77,19 +77,18 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St try { - plugins::slot_registry::instance().set(plugins::simplify_t{ grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())}); + slots::instance().connect( grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials()))); } catch (std::runtime_error e) { st.SkipWithError(e.what()); } - auto simplify = plugins::slot_registry::instance().get(); for (auto _ : st) { Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } From 5f4e7833f3cd7cf6641977c4a019e3ccf340a0b7 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 10:50:01 +0200 Subject: [PATCH 075/656] Fixed simplify benchmarks It now uses the new logic [CURA-10475] --- benchmark/simplify_benchmark.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 9a5d715dda..4d9862e0ce 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -56,14 +56,12 @@ BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_local); BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State& st) { - plugins::slot_registry::instance().set(plugins::simplify_t{}); - auto simplify = plugins::slot_registry::instance().get(); for (auto _ : st) { Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = simplify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } @@ -77,7 +75,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St try { - slots::instance().connect( grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials()))); + slots::instance().connect( grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())); } catch (std::runtime_error e) { From 4ba0d8f7faee24f382c81d619f9d8c78d688092b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 13:59:57 +0200 Subject: [PATCH 076/656] Fix types to work with updated proto types Handshake doesn't work yet. We are gonna use metadata for this [CURA-10475] --- CMakeLists.txt | 7 +-- conanfile.py | 2 +- include/plugins/converters.h | 114 +++++++++++++++++----------------- include/plugins/pluginproxy.h | 49 +++++++-------- include/plugins/slots.h | 5 +- include/plugins/types.h | 4 +- 6 files changed, 86 insertions(+), 95 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb3cd65770..32782b2e94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,18 +14,15 @@ option(ENABLE_PLUGINS "Build with all warnings" ON) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) #set(GRPC_PROTOS "List of all gRPC definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") -message(STATUS ${GRPC_PROTOS}) # Generate the plugin types find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) -asio_grpc_protobuf_generate( - GENERATE_GRPC GENERATE_MOCK_CODE +asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" - PROTOS "${GRPC_PROTOS}" -) + GENERATE_GRPC GENERATE_MOCK_CODE GENERATE_DESCRIPTORS) if (ENABLE_ARCUS) message(STATUS "Building with Arcus") diff --git a/conanfile.py b/conanfile.py index a335e21923..90aab8ad90 100644 --- a/conanfile.py +++ b/conanfile.py @@ -114,7 +114,7 @@ def generate(self): tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info - tc.variables["GRPC_PROTOS"] = ";".join([str(p) for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) + tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) tc.generate() diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 8aca64fe87..2874dd718b 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -13,61 +13,63 @@ #include "plugins/types.h" #include "postprocess.grpc.pb.h" +#include "postprocess.pb.h" #include "simplify.grpc.pb.h" +#include "simplify.pb.h" namespace cura::plugins { - -/** - * @brief A converter struct for plugin requests. - * - * The `plugin_request` struct provides a conversion function that converts a native slot ID - * to a `proto::PluginRequest` message. - */ -template -struct plugin_request -{ - using value_type = proto::PluginRequest; ///< The protobuf message type. - using native_value_type = cura::plugins::SlotID; ///< The native value type. - const std::string slot_version_range{ SlotVersionRng.value }; - - /** - * @brief Converts a native slot ID to a `proto::PluginRequest` message. - * - * @param slot_id The native slot ID. - * @return The converted `proto::PluginRequest` message. - */ - value_type operator()(const native_value_type& slot_id) const - { - value_type message{}; - message.set_slot_version_range(slot_version_range); - message.set_slot_id(slot_id); - return message; - } -}; - -/** - * @brief A converter struct for plugin responses. - * - * The `plugin_response` struct provides a conversion function that converts a `proto::PluginResponse` - * message to a native value type. - */ -struct plugin_response -{ - using value_type = proto::PluginResponse; ///< The protobuf message type. - using native_value_type = std::tuple; ///< The native value type. - - /** - * @brief Converts a `proto::PluginResponse` message to a native value type. - * - * @param message The `proto::PluginResponse` message. - * @return The converted native value. - */ - native_value_type operator()(const value_type& message) const - { - return { message.slot_id(), message.plugin_name(), message.slot_version(), message.plugin_version() }; - } -}; +// +///** +// * @brief A converter struct for plugin requests. +// * +// * The `plugin_request` struct provides a conversion function that converts a native slot ID +// * to a `proto::PluginRequest` message. +// */ +//template +//struct plugin_request +//{ +// using value_type = proto::PluginRequest; ///< The protobuf message type. +// using native_value_type = cura::plugins::SlotID; ///< The native value type. +// const std::string slot_version_range{ SlotVersionRng.value }; +// +// /** +// * @brief Converts a native slot ID to a `proto::PluginRequest` message. +// * +// * @param slot_id The native slot ID. +// * @return The converted `proto::PluginRequest` message. +// */ +// value_type operator()(const native_value_type& slot_id) const +// { +// value_type message{}; +// message.set_slot_version_range(slot_version_range); +// message.set_slot_id(slot_id); +// return message; +// } +//}; +// +///** +// * @brief A converter struct for plugin responses. +// * +// * The `plugin_response` struct provides a conversion function that converts a `proto::PluginResponse` +// * message to a native value type. +// */ +//struct plugin_response +//{ +// using value_type = proto::PluginResponse; ///< The protobuf message type. +// using native_value_type = std::tuple; ///< The native value type. +// +// /** +// * @brief Converts a `proto::PluginResponse` message to a native value type. +// * +// * @param message The `proto::PluginResponse` message. +// * @return The converted native value. +// */ +// native_value_type operator()(const value_type& message) const +// { +// return { message.slot_id(), message.plugin_name(), message.slot_version(), message.plugin_version() }; +// } +//}; /** * @brief A converter struct for simplify requests. @@ -78,7 +80,7 @@ struct plugin_response template struct simplify_request { - using value_type = proto::SimplifyRequest; ///< The protobuf message type. + using value_type = plugins::v1::SimplifyServiceModifyRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. const std::string slot_version_range{ SlotVersionRng.value }; @@ -94,7 +96,6 @@ struct simplify_request value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const { value_type message{}; - message.set_slot_version_range(slot_version_range); if (polygons.empty()) { return message; @@ -138,7 +139,7 @@ struct simplify_request */ struct simplify_response { - using value_type = proto::SimplifyResponse; ///< The protobuf message type. + using value_type = plugins::v1::SimplifyServiceModifyResponse; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -182,7 +183,7 @@ struct simplify_response template struct postprocess_request { - using value_type = proto::PostprocessRequest; ///< The protobuf message type. + using value_type = plugins::v1::PostprocessServiceModifyRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. const std::string slot_version_range{ SlotVersionRng.value }; @@ -195,7 +196,6 @@ struct postprocess_request value_type operator()(const native_value_type& gcode) const { value_type message{}; - message.set_slot_version_range(slot_version_range); message.set_gcode_word(gcode); return message; } @@ -203,7 +203,7 @@ struct postprocess_request struct postprocess_response { - using value_type = proto::PostprocessResponse; + using value_type = plugins::v1::PostprocessServiceModifyResponse; using native_value_type = std::string; native_value_type operator()(const value_type& message) const diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 18601b9d47..0d67a85a47 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -17,8 +17,6 @@ #include "plugins/exception.h" -#include "plugin.grpc.pb.h" - namespace cura::plugins { @@ -44,9 +42,6 @@ class PluginProxy using validator_t = Validator; - using request_plugin_t = typename proto::PluginRequest; - using response_plugin_t = typename proto::PluginResponse; - using request_process_t = typename Request::value_type; using response_process_t = typename Response::value_type; @@ -173,7 +168,7 @@ class PluginProxy { throw std::runtime_error(fmt::format("Plugin for slot {} is not ready", slot_id)); } - co_await handshake(grpc_context, timeout); + //co_await handshake(grpc_context, timeout); if (! valid_) { throw exceptions::ValidatorException(valid_, plugin_name_, plugin_version_, plugin_peer_); @@ -181,27 +176,27 @@ class PluginProxy } } - boost::asio::awaitable handshake(agrpc::GrpcContext& grpc_context, const std::chrono::seconds& timeout = std::chrono::seconds(5)) - { - const auto deadline = std::chrono::system_clock::now() + timeout; // TODO use deadline - using RPC = agrpc::RPC<&stub_t::PrepareAsyncIdentify>; - grpc::ClientContext client_context{}; - plugin_request plugin_request_conv{}; - request_plugin_t request{ plugin_request_conv(slot_id) }; - response_plugin_t response{}; - auto status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); - if (! status.ok()) - { - throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_id, plugin_name_, plugin_version_, plugin_name_, status_.error_message())); - } - spdlog::debug("Plugin responded with: {}", response.DebugString()); - plugin_response plugin_response_conv{}; - const auto& [rsp_slot_id, rsp_plugin_name, rsp_slot_version, rsp_plugin_version] = plugin_response_conv(response); - plugin_name_ = rsp_plugin_name; - plugin_version_ = rsp_plugin_version; - plugin_peer_ = client_context.peer(); - valid_ = Validator{ rsp_slot_version }; - } +// boost::asio::awaitable handshake(agrpc::GrpcContext& grpc_context, const std::chrono::seconds& timeout = std::chrono::seconds(5)) +// { +// const auto deadline = std::chrono::system_clock::now() + timeout; // TODO use deadline +// using RPC = agrpc::RPC<&stub_t::PrepareAsyncIdentify>; +// grpc::ClientContext client_context{}; +// plugin_request plugin_request_conv{}; +// request_plugin_t request{ plugin_request_conv(slot_id) }; +// response_plugin_t response{}; +// auto status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); +// if (! status.ok()) +// { +// throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_id, plugin_name_, plugin_version_, plugin_name_, status_.error_message())); +// } +// spdlog::debug("Plugin responded with: {}", response.DebugString()); +// plugin_response plugin_response_conv{}; +// const auto& [rsp_slot_id, rsp_plugin_name, rsp_slot_version, rsp_plugin_version] = plugin_response_conv(response); +// plugin_name_ = rsp_plugin_name; +// plugin_version_ = rsp_plugin_version; +// plugin_peer_ = client_context.peer(); +// valid_ = Validator{ rsp_slot_version }; +// } }; } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index e51325f74a..2b63c2825f 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -14,7 +14,6 @@ #include "utils/IntPoint.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed -#include "plugin.grpc.pb.h" #include "postprocess.grpc.pb.h" #include "simplify.grpc.pb.h" @@ -49,7 +48,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using simplify_slot = SlotProxy, simplify_request, simplify_response, Default>; +using simplify_slot = SlotProxy, simplify_request, simplify_response, Default>; /** * @brief Alias for the Postprocess slot. @@ -59,7 +58,7 @@ using simplify_slot = SlotProxy -using postprocess_slot = SlotProxy, postprocess_request, postprocess_response, Default>; +using postprocess_slot = SlotProxy, postprocess_request, postprocess_response, Default>; template struct Typelist diff --git a/include/plugins/types.h b/include/plugins/types.h index 050a032d6e..af629b262b 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -13,11 +13,11 @@ #include "utils/concepts/generic.h" #include "utils/polygon.h" -#include "plugin.grpc.pb.h" +#include "slot_id.pb.h" namespace cura::plugins { -using SlotID = proto::SlotID; +using SlotID = plugins::v1::SlotID; namespace details { From 8315a03b12bd8fe9ae7381ef97b36507886e5e31 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 22 May 2023 14:57:02 +0200 Subject: [PATCH 077/656] Don't generate descriptors We don't use them [CURA-10475] --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32782b2e94..8964268165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ find_package(asio-grpc REQUIRED) asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" - GENERATE_GRPC GENERATE_MOCK_CODE GENERATE_DESCRIPTORS) + GENERATE_GRPC GENERATE_MOCK_CODE) if (ENABLE_ARCUS) message(STATUS "Building with Arcus") From c273526f140f306f9837db4ac69d68bf6acaf423 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 10:54:03 +0200 Subject: [PATCH 078/656] Refactored plugins and slots This commit refactors the PluginProxy class and converters.h in the include/plugins directory. It introduces validation for plugin requests and responses and handles communication failures. The changes include: Refactored PluginProxy class: The PluginProxy class has been modified to use template parameters for RPC_ID, SlotVersionRng, Stub, ValidatorTp, RequestTp, and ResponseTp. It also uses aliases for easy use of types. Updated request and response converters: The request and response converters have been updated to use the new types. Added validation and communication failure handling: The PluginProxy class now performs validation on plugin requests and responses. It also handles communication failures and throws appropriate exceptions. These changes improve the functionality and reliability of the PluginProxy class and ensure proper validation and error handling during communication with plugins. [CURA-10475] --- include/plugins/converters.h | 68 +-------- include/plugins/metadata.h | 28 ++++ include/plugins/pluginproxy.h | 175 ++++++++++++----------- include/plugins/slotproxy.h | 6 +- include/plugins/slots.h | 12 +- src/communication/ArcusCommunication.cpp | 4 +- src/slicer.cpp | 2 +- 7 files changed, 131 insertions(+), 164 deletions(-) create mode 100644 include/plugins/metadata.h diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 2874dd718b..dc7c77d48d 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -19,70 +19,11 @@ namespace cura::plugins { -// -///** -// * @brief A converter struct for plugin requests. -// * -// * The `plugin_request` struct provides a conversion function that converts a native slot ID -// * to a `proto::PluginRequest` message. -// */ -//template -//struct plugin_request -//{ -// using value_type = proto::PluginRequest; ///< The protobuf message type. -// using native_value_type = cura::plugins::SlotID; ///< The native value type. -// const std::string slot_version_range{ SlotVersionRng.value }; -// -// /** -// * @brief Converts a native slot ID to a `proto::PluginRequest` message. -// * -// * @param slot_id The native slot ID. -// * @return The converted `proto::PluginRequest` message. -// */ -// value_type operator()(const native_value_type& slot_id) const -// { -// value_type message{}; -// message.set_slot_version_range(slot_version_range); -// message.set_slot_id(slot_id); -// return message; -// } -//}; -// -///** -// * @brief A converter struct for plugin responses. -// * -// * The `plugin_response` struct provides a conversion function that converts a `proto::PluginResponse` -// * message to a native value type. -// */ -//struct plugin_response -//{ -// using value_type = proto::PluginResponse; ///< The protobuf message type. -// using native_value_type = std::tuple; ///< The native value type. -// -// /** -// * @brief Converts a `proto::PluginResponse` message to a native value type. -// * -// * @param message The `proto::PluginResponse` message. -// * @return The converted native value. -// */ -// native_value_type operator()(const value_type& message) const -// { -// return { message.slot_id(), message.plugin_name(), message.slot_version(), message.plugin_version() }; -// } -//}; -/** - * @brief A converter struct for simplify requests. - * - * The `simplify_request` struct provides a conversion function that converts native data for - * simplification (polygons and simplification parameters) to a `proto::SimplifyRequest` message. - */ -template struct simplify_request { using value_type = plugins::v1::SimplifyServiceModifyRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. - const std::string slot_version_range{ SlotVersionRng.value }; /** * @brief Converts native data for simplification to a `proto::SimplifyRequest` message. @@ -174,18 +115,11 @@ struct simplify_response } }; -/** - * @brief A converter struct for postprocess requests. - * - * The `postprocess_request` struct provides a conversion function that converts a native G-code string - * to a `proto::PostprocessRequest` message. - */ -template + struct postprocess_request { using value_type = plugins::v1::PostprocessServiceModifyRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. - const std::string slot_version_range{ SlotVersionRng.value }; /** * @brief Converts a native G-code string to a `proto::PostprocessRequest` message. diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h new file mode 100644 index 0000000000..7f9ee17342 --- /dev/null +++ b/include/plugins/metadata.h @@ -0,0 +1,28 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_INCLUDE_PLUGINS_METADATA_H +#define CURAENGINE_INCLUDE_PLUGINS_METADATA_H + +#include + +namespace cura::plugins +{ +struct plugin_metadata +{ + std::string name; + std::string version; + std::string peer; + + std::string slot_version; +}; + +struct slot_metadata +{ + std::string_view rpc_id; + std::string_view version_range; +}; + +} // namespace cura::plugins + +#endif // CURAENGINE_INCLUDE_PLUGINS_METADATA_H diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 0d67a85a47..dc80259453 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -6,16 +6,27 @@ #include #include +#include #include #include #include +#include #include -#include +#include +#include +#include #include #include +#include +#include #include +#include #include "plugins/exception.h" +#include "plugins/metadata.h" + +#include "slot_id.pb.h" +#include "utils/concepts/generic.h" namespace cura::plugins { @@ -33,35 +44,32 @@ namespace cura::plugins * @tparam Request The gRPC convertible request type. * @tparam Response The gRPC convertible response type. */ -template +template class PluginProxy { public: // type aliases for easy use - using value_type = typename Response::native_value_type; - - using validator_t = Validator; + using value_type = typename ResponseTp::native_value_type; + using validator_type = ValidatorTp; - using request_process_t = typename Request::value_type; - using response_process_t = typename Response::value_type; + using req_msg_type = typename RequestTp::value_type; + using rsp_msg_type = typename ResponseTp::value_type; - using request_converter_t = Request; - using response_converter_t = Response; + using req_converter_type = RequestTp; + using rsp_converter_type = ResponseTp; using stub_t = Stub; - - static inline constexpr plugins::SlotID slot_id{ Slot }; +// using stub_t = grpc::GenericStub; private: - validator_t valid_{}; ///< The validator object for plugin validation. - request_converter_t request_converter_{}; ///< The request converter object. - response_converter_t response_converter_{}; ///< The response converter object. + validator_type valid_{}; ///< The validator object for plugin validation. + req_converter_type req_{}; ///< The request converter object. + rsp_converter_type rsp_{}; ///< The response converter object. - grpc::Status status_; ///< The gRPC status object. ranges::semiregular_box stub_; ///< The gRPC stub for communication. - std::string plugin_name_{}; ///< The name of the plugin. - std::string plugin_version_{}; ///< The version of the plugin. - std::string plugin_peer_{}; ///< The peer of the plugin. + + constexpr static slot_metadata slot_info_{ .rpc_id = RPC_ID.value, .version_range = SlotVersionRng.value }; + std::optional plugin_info_{ std::nullopt }; ///< The plugin info object. public: /** @@ -76,46 +84,33 @@ class PluginProxy * @throws std::runtime_error if the plugin fails validation or communication errors occur. */ constexpr PluginProxy() = default; - - explicit PluginProxy(std::shared_ptr channel) : stub_(channel) - { - // Give the plugin some time to start up and initiate handshake - agrpc::GrpcContext grpc_context; - boost::asio::co_spawn(grpc_context, wait_for_plugin_ready(grpc_context, channel), boost::asio::detached); - grpc_context.run(); - } - + explicit PluginProxy(std::shared_ptr channel) : stub_(channel){}; constexpr PluginProxy(const PluginProxy&) = default; - constexpr PluginProxy(PluginProxy&&) noexcept = default; + constexpr PluginProxy(PluginProxy&&) noexcept = default; constexpr PluginProxy& operator=(const PluginProxy& other) { if (this != &other) { valid_ = other.valid_; - status_ = other.status_; stub_ = other.stub_; - plugin_name_ = other.plugin_name_; - plugin_version_ = other.plugin_version_; - plugin_peer_ = other.plugin_peer_; + plugin_info_ = other.plugin_info_; } return *this; } - constexpr PluginProxy& operator=(PluginProxy&& other) { if (this != &other) { valid_ = std::move(other.valid_); - status_ = std::move(other.status_); - stub_ = std::move(other.stub_); // FIXME: the stub should be moved - plugin_name_ = std::move(other.plugin_name_); - plugin_version_ = std::move(other.plugin_version_); - plugin_peer_ = std::move(other.plugin_peer_); + stub_ = std::move(other.stub_); + plugin_info_ = std::move(other.plugin_info_); } return *this; } ~PluginProxy() = default; +#pragma clang diagnostic push +#pragma ide diagnostic ignored "cppcoreguidelines-avoid-capture-default-when-capturing-this" /** * @brief Executes the plugin operation. * @@ -133,70 +128,82 @@ class PluginProxy { agrpc::GrpcContext grpc_context; value_type ret_value{}; + grpc::Status status; + boost::asio::co_spawn( grpc_context, [&]() -> boost::asio::awaitable { - const auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10); // TODO use deadline + using RPC = agrpc::RPC<&stub_t::PrepareAsyncModify>; grpc::ClientContext client_context{}; - request_process_t request{ request_converter_(std::forward(args)...) }; - response_process_t response{}; - status_ = co_await Prepare::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = response_converter_(response); + + // Set time-out + client_context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(500)); // TODO: don't use magic number and make it realistic + + // Metadata + client_context.AddMetadata("cura-slot-service-name", slot_info_.rpc_id.data()); + client_context.AddMetadata("cura-slot-version-range", slot_info_.version_range.data()); + + // Construct request + auto request{ req_(std::forward(args)...) }; + + // Make unary request + rsp_msg_type response; + status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = rsp_(response); + + if (! plugin_info_.has_value()) + { + const auto& metadata_rsp = client_context.GetServerInitialMetadata(); + plugin_info_ = plugin_metadata{ + .name = metadata_rsp.find("cura-plugin-name")->second.data(), + .version = metadata_rsp.find("cura-plugin-version")->second.data(), + .peer = client_context.peer(), + .slot_version = metadata_rsp.find("cura-slot-version")->second.data(), + }; + valid_ = validator_type{ plugin_info_->slot_version }; + } }, boost::asio::detached); grpc_context.run(); - if (! status_.ok()) + if (! status.ok()) // TODO: handle different kind of status codes { - throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_id, plugin_name_, plugin_version_, plugin_name_, status_.error_message())); + if (plugin_info_.has_value()) + { + throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_info_.rpc_id, plugin_info_->name, plugin_info_->version, plugin_info_->peer, status.error_message())); + } + throw std::runtime_error(fmt::format("Slot {} had a communication failure: {}", slot_info_.rpc_id, status.error_message())); } - return ret_value; - } - -private: - boost::asio::awaitable wait_for_plugin_ready(agrpc::GrpcContext& grpc_context, std::shared_ptr channel, const std::chrono::seconds& timeout = std::chrono::seconds(5)) - { - const auto deadline = std::chrono::system_clock::now() + timeout; - const auto state = channel->GetState(true); - bool has_state_changed = co_await agrpc::notify_on_state_change(grpc_context, *channel, state, deadline); - if (has_state_changed) + if (! valid_ ) { - auto plugin_connection_state = channel->GetState(true); - if (plugin_connection_state != grpc_connectivity_state::GRPC_CHANNEL_READY && plugin_connection_state != grpc_connectivity_state::GRPC_CHANNEL_CONNECTING) - { - throw std::runtime_error(fmt::format("Plugin for slot {} is not ready", slot_id)); - } - //co_await handshake(grpc_context, timeout); - if (! valid_) + if (plugin_info_.has_value()) { - throw exceptions::ValidatorException(valid_, plugin_name_, plugin_version_, plugin_peer_); + throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} failed validation: {}", slot_info_.rpc_id, plugin_info_->name, plugin_info_->version, plugin_info_->peer, valid_.what())); } + throw std::runtime_error(fmt::format("Slot {} failed validation: {}", slot_info_.rpc_id, valid_.what())); } + + return ret_value; // TODO: check if ret_value is always filled or if we need a solution like: https://stackoverflow.com/questions/67908591/how-to-convert-boostasioawaitable-to-stdfuture } +#pragma clang diagnostic pop -// boost::asio::awaitable handshake(agrpc::GrpcContext& grpc_context, const std::chrono::seconds& timeout = std::chrono::seconds(5)) -// { -// const auto deadline = std::chrono::system_clock::now() + timeout; // TODO use deadline -// using RPC = agrpc::RPC<&stub_t::PrepareAsyncIdentify>; -// grpc::ClientContext client_context{}; -// plugin_request plugin_request_conv{}; -// request_plugin_t request{ plugin_request_conv(slot_id) }; -// response_plugin_t response{}; -// auto status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); -// if (! status.ok()) -// { -// throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_id, plugin_name_, plugin_version_, plugin_name_, status_.error_message())); -// } -// spdlog::debug("Plugin responded with: {}", response.DebugString()); -// plugin_response plugin_response_conv{}; -// const auto& [rsp_slot_id, rsp_plugin_name, rsp_slot_version, rsp_plugin_version] = plugin_response_conv(response); -// plugin_name_ = rsp_plugin_name; -// plugin_version_ = rsp_plugin_version; -// plugin_peer_ = client_context.peer(); -// valid_ = Validator{ rsp_slot_version }; -// } +private: + template + auto serialize(const Message& message) + { + grpc::ByteBuffer buffer; + bool own_buffer; + grpc::GenericSerialize(message, &buffer, &own_buffer); + return buffer; + } + + template + bool deserialize(grpc::ByteBuffer& buffer, Message& message) + { + return grpc::GenericDeserialize(&buffer, &message).ok(); + } }; } // namespace cura::plugins diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 58f6b5766e..ed04db9bf6 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -35,16 +35,14 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template class Validator, class Stub, class Prepare, template class Request, class Response, class Default> +template class ValidatorTp, class RequestTp, class ResponseTp, class Default> class SlotProxy { Default default_process{}; - using value_type = PluginProxy, Stub, Prepare, Request, Response>; + using value_type = PluginProxy, RequestTp, ResponseTp>; std::optional plugin_{ std::nullopt }; public: - static inline constexpr plugins::SlotID slot_id{ Slot }; - /** * @brief Default constructor. * diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 2b63c2825f..6373190302 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -48,7 +48,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using simplify_slot = SlotProxy, simplify_request, simplify_response, Default>; +using slot_simplify_ = SlotProxy<"SimplifyService", "<=1.0.0", plugins::v1::SimplifyService::Stub, Validator, simplify_request, simplify_response, Default>; /** * @brief Alias for the Postprocess slot. @@ -58,7 +58,7 @@ using simplify_slot = SlotProxy -using postprocess_slot = SlotProxy, postprocess_request, postprocess_response, Default>; +using slot_postprocess_ = SlotProxy<"PostprocessService", "<=1.0.0", plugins::v1::PostprocessService::Stub, Validator, postprocess_request, postprocess_response, Default>; template struct Typelist @@ -96,7 +96,7 @@ class Registry, Unit> : public Registry template void connect(auto&& plugin) { - get_type().proxy = Tp { std::forward( std::move(plugin) ) }; + get_type().proxy = Tp{ std::forward(std::move(plugin)) }; } protected: @@ -143,10 +143,10 @@ struct Holder } // namespace details -using simplify_t = details::simplify_slot; -using postprocess_t = details::postprocess_slot<>; +using slot_simplify = details::slot_simplify_; +using slot_postprocess = details::slot_postprocess_<>; -using SlotTypes = details::Typelist; +using SlotTypes = details::Typelist; } // namespace plugins using slots = plugins::details::SingletonRegistry; diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index cee329dd9f..e975df92dd 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -517,10 +517,10 @@ void ArcusCommunication::sliceNext() switch (plugin.id()) { case cura::proto::SlotID::SIMPLIFY: - slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); + slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); break; case cura::proto::SlotID::POSTPROCESS: - slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); + slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); break; default: break; } diff --git a/src/slicer.cpp b/src/slicer.cpp index de11c5f4dd..582182da9c 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -774,7 +774,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. // polygons = Simplify(mesh->settings).polygon(polygons); - polygons = slots::instance().invoke(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); + polygons = slots::instance().invoke(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments // Clean up polylines for Surface Mode printing From 744087431b08e8f950464fb9a6e42caecc9df056 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 11:38:00 +0200 Subject: [PATCH 079/656] removed trailing return type deduction MSVC crashed on this Contributes to CURA-10475 --- include/plugins/slotproxy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index ed04db9bf6..cdc2e75865 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -70,7 +70,7 @@ class SlotProxy * @param args The arguments for the plugin request. * @return The result of the plugin request or the default behavior. */ - auto operator()(auto&&... args) -> std::invoke_result_t + auto operator()(auto&&... args) { if (plugin_.has_value()) { From 878dcf14b3d3daf15b3bc24d058ad0ac461a5dbf Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 11:40:24 +0200 Subject: [PATCH 080/656] removed redundant code Contributes to CURA-10475 --- include/plugins/pluginproxy.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index dc80259453..77c7e8aaa3 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -59,7 +58,6 @@ class PluginProxy using rsp_converter_type = ResponseTp; using stub_t = Stub; -// using stub_t = grpc::GenericStub; private: validator_type valid_{}; ///< The validator object for plugin validation. @@ -109,8 +107,6 @@ class PluginProxy } ~PluginProxy() = default; -#pragma clang diagnostic push -#pragma ide diagnostic ignored "cppcoreguidelines-avoid-capture-default-when-capturing-this" /** * @brief Executes the plugin operation. * @@ -187,23 +183,6 @@ class PluginProxy return ret_value; // TODO: check if ret_value is always filled or if we need a solution like: https://stackoverflow.com/questions/67908591/how-to-convert-boostasioawaitable-to-stdfuture } -#pragma clang diagnostic pop - -private: - template - auto serialize(const Message& message) - { - grpc::ByteBuffer buffer; - bool own_buffer; - grpc::GenericSerialize(message, &buffer, &own_buffer); - return buffer; - } - - template - bool deserialize(grpc::ByteBuffer& buffer, Message& message) - { - return grpc::GenericDeserialize(&buffer, &message).ok(); - } }; } // namespace cura::plugins From 779d15e48f17e22df2eacb5b08d74acd0bbd8bb7 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 11:50:36 +0200 Subject: [PATCH 081/656] Fixed needing to linking to boost context lib Wasn't needed Contributes to CURA-10475 --- include/plugins/pluginproxy.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 77c7e8aaa3..be4adde78c 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -6,17 +6,12 @@ #include #include -#include #include #include #include -#include #include -#include -#include #include #include -#include #include #include #include From b4eff117454905e536f4ab7b626aab23f6d19fdb Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 12:10:10 +0200 Subject: [PATCH 082/656] Use SlotID Contributes to CURA-10475 --- include/plugins/exception.h | 7 +++++-- include/plugins/metadata.h | 2 +- include/plugins/pluginproxy.h | 14 +++++++------- include/plugins/slotproxy.h | 4 ++-- include/plugins/slots.h | 5 +++-- include/plugins/types.h | 4 ++-- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/plugins/exception.h b/include/plugins/exception.h index 4aa601298e..0cc74fb579 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -10,6 +10,7 @@ #include #include +#include "plugins/metadata.h" #include "plugins/types.h" namespace cura::plugins::exceptions @@ -20,8 +21,10 @@ class ValidatorException : public std::exception std::string msg_; public: - ValidatorException(auto validator, std::string plugin_name, const std::string& plugin_version, const std::string& plugin_target) noexcept - : msg_(fmt::format("Plugin {} '{}' at {} failed validation: {}", plugin_name, plugin_version, plugin_target, validator.what())) + ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept : msg_(fmt::format("Failed to validation plugin on Slot '{}': {}", slot_info.slot_id, validator.what())){}; + + ValidatorException(const auto& validator, const slot_metadata& slot_info, const plugin_metadata& plugin_info) noexcept + : msg_(fmt::format("Failed to validate plugin {} '{}' at {} for slot '{}': {}", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, validator.what())) { } diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 7f9ee17342..bd6ca9bde4 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -19,7 +19,7 @@ struct plugin_metadata struct slot_metadata { - std::string_view rpc_id; + plugins::v1::SlotID slot_id; std::string_view version_range; }; diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index be4adde78c..4ddf6453f8 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -38,7 +38,7 @@ namespace cura::plugins * @tparam Request The gRPC convertible request type. * @tparam Response The gRPC convertible response type. */ -template +template class PluginProxy { public: @@ -61,7 +61,7 @@ class PluginProxy ranges::semiregular_box stub_; ///< The gRPC stub for communication. - constexpr static slot_metadata slot_info_{ .rpc_id = RPC_ID.value, .version_range = SlotVersionRng.value }; + constexpr static slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value }; std::optional plugin_info_{ std::nullopt }; ///< The plugin info object. public: @@ -132,7 +132,7 @@ class PluginProxy client_context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(500)); // TODO: don't use magic number and make it realistic // Metadata - client_context.AddMetadata("cura-slot-service-name", slot_info_.rpc_id.data()); + client_context.AddMetadata("cura-slot-service-name", fmt::format("{}", slot_info_.slot_id)); client_context.AddMetadata("cura-slot-version-range", slot_info_.version_range.data()); // Construct request @@ -162,18 +162,18 @@ class PluginProxy { if (plugin_info_.has_value()) { - throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_info_.rpc_id, plugin_info_->name, plugin_info_->version, plugin_info_->peer, status.error_message())); + throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_info_.slot_id, plugin_info_->name, plugin_info_->version, plugin_info_->peer, status.error_message())); } - throw std::runtime_error(fmt::format("Slot {} had a communication failure: {}", slot_info_.rpc_id, status.error_message())); + throw std::runtime_error(fmt::format("Slot {} had a communication failure: {}", slot_info_.slot_id, status.error_message())); } if (! valid_ ) { if (plugin_info_.has_value()) { - throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} failed validation: {}", slot_info_.rpc_id, plugin_info_->name, plugin_info_->version, plugin_info_->peer, valid_.what())); + throw exceptions::ValidatorException(valid_, slot_info_, plugin_info_.value()); } - throw std::runtime_error(fmt::format("Slot {} failed validation: {}", slot_info_.rpc_id, valid_.what())); + throw exceptions::ValidatorException(valid_, slot_info_); } return ret_value; // TODO: check if ret_value is always filled or if we need a solution like: https://stackoverflow.com/questions/67908591/how-to-convert-boostasioawaitable-to-stdfuture diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index cdc2e75865..9bf2c85693 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -35,11 +35,11 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template class ValidatorTp, class RequestTp, class ResponseTp, class Default> +template class ValidatorTp, class RequestTp, class ResponseTp, class Default> class SlotProxy { Default default_process{}; - using value_type = PluginProxy, RequestTp, ResponseTp>; + using value_type = PluginProxy, RequestTp, ResponseTp>; std::optional plugin_{ std::nullopt }; public: diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 6373190302..656d0841a6 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -16,6 +16,7 @@ #include "postprocess.grpc.pb.h" #include "simplify.grpc.pb.h" +#include "slot_id.pb.h" namespace cura { @@ -48,7 +49,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy<"SimplifyService", "<=1.0.0", plugins::v1::SimplifyService::Stub, Validator, simplify_request, simplify_response, Default>; +using slot_simplify_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -58,7 +59,7 @@ using slot_simplify_ = SlotProxy<"SimplifyService", "<=1.0.0", plugins::v1::Simp * @tparam Default The default behavior when no plugin is registered. */ template -using slot_postprocess_ = SlotProxy<"PostprocessService", "<=1.0.0", plugins::v1::PostprocessService::Stub, Validator, postprocess_request, postprocess_response, Default>; +using slot_postprocess_ = SlotProxy; template struct Typelist diff --git a/include/plugins/types.h b/include/plugins/types.h index af629b262b..23546f89d0 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -50,10 +50,10 @@ struct fmt::formatter switch (slot_id) { case cura::plugins::SlotID::SIMPLIFY: - slot_name = "Simplify"; + slot_name = "SimplifyService"; break; case cura::plugins::SlotID::POSTPROCESS: - slot_name = "Postprocess"; + slot_name = "PostprocessService"; break; default: slot_name = "Unknown"; From 7216246d547b3bad4e1fb40e1a0544341eb0cde5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 13:07:43 +0200 Subject: [PATCH 083/656] Set plugin_metadata from client_context Throws or warns when metadata is missing [CURA-10475] --- include/plugins/metadata.h | 44 +++++++++++++++++++++++++++++++---- include/plugins/pluginproxy.h | 8 +------ include/plugins/types.h | 24 +++++++++++++++---- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index bd6ca9bde4..41d83290ed 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -4,17 +4,53 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_METADATA_H #define CURAENGINE_INCLUDE_PLUGINS_METADATA_H +#include #include +#include +#include + +#include "plugins/types.h" + namespace cura::plugins { struct plugin_metadata { - std::string name; - std::string version; - std::string peer; + std::string_view name; + std::string_view version; + std::string_view peer; + std::string_view slot_version; - std::string slot_version; + explicit plugin_metadata(const grpc::ClientContext& client_context) + { + const auto& metadata = client_context.GetServerInitialMetadata(); + if (auto it = metadata.find("cura-slot-version"); it != metadata.end()) + { + slot_version = std::string{ it->second.data(), it->second.size() }; + } + else + { + spdlog::error("'cura-slot-version' RPC metadata not set"); + throw std::runtime_error("'cura-slot-version' RPC metadata not set"); + } + if (auto it = metadata.find("cura-plugin-name"); it != metadata.end()) + { + name = std::string{ it->second.data(), it->second.size() }; + } + else + { + spdlog::warn("'cura-plugin-name' RPC metadata not set"); + } + if (auto it = metadata.find("cura-plugin-version"); it != metadata.end()) + { + version = std::string{ it->second.data(), it->second.size() }; + } + else + { + spdlog::warn("'cura-plugin-version' RPC metadata not set"); + } + peer = client_context.peer(); + } }; struct slot_metadata diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 4ddf6453f8..70f4d0da26 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -145,13 +145,7 @@ class PluginProxy if (! plugin_info_.has_value()) { - const auto& metadata_rsp = client_context.GetServerInitialMetadata(); - plugin_info_ = plugin_metadata{ - .name = metadata_rsp.find("cura-plugin-name")->second.data(), - .version = metadata_rsp.find("cura-plugin-version")->second.data(), - .peer = client_context.peer(), - .slot_version = metadata_rsp.find("cura-slot-version")->second.data(), - }; + plugin_info_ = plugin_metadata{ client_context }; valid_ = validator_type{ plugin_info_->slot_version }; } }, diff --git a/include/plugins/types.h b/include/plugins/types.h index 23546f89d0..b4820dcfb0 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -8,6 +8,7 @@ #include #include +#include #include "utils/IntPoint.h" #include "utils/concepts/generic.h" @@ -37,11 +38,12 @@ struct CharRangeLiteral } // namespace cura::plugins +namespace fmt +{ // Custom formatter for humanreadable slot_id's template<> -struct fmt::formatter +struct formatter { - // The formatting function template auto format(cura::plugins::SlotID slot_id, FormatContext& ctx) { @@ -63,13 +65,27 @@ struct fmt::formatter return fmt::format_to(ctx.out(), "{}", slot_name); } - // The parsing function template auto parse(ParseContext& ctx) { - // Not implemented for simplicity in this example return ctx.begin(); } }; +template<> +struct formatter +{ + constexpr auto parse(format_parse_context& ctx) + { + return ctx.end(); + } + + template + auto format(const grpc::string_ref& str, FormatContext& ctx) + { + return format_to(ctx.out(), "{}", std::string_view{ str.data(), str.size() }); + } +}; + +} // namespace fmt #endif // PLUGINS_TYPES_H From c0b54ba12f4cadf2c86c398c593c38cc8f51263f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 13:34:15 +0200 Subject: [PATCH 084/656] Refactor Validator class and update its usage in pluginproxy.h and slotproxy.h - Remove unused code and comments in validator.h - Update Validator class constructor to take slot_info and plugin_info as arguments - Update usage of Validator class in pluginproxy.h and slotproxy.h - Update ValidatorException messages in exception.h [CURA-10475] --- include/plugins/exception.h | 4 +-- include/plugins/pluginproxy.h | 4 +-- include/plugins/slotproxy.h | 4 +-- include/plugins/validator.h | 50 +++++------------------------------ 4 files changed, 12 insertions(+), 50 deletions(-) diff --git a/include/plugins/exception.h b/include/plugins/exception.h index 0cc74fb579..d3d50af969 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -21,10 +21,10 @@ class ValidatorException : public std::exception std::string msg_; public: - ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept : msg_(fmt::format("Failed to validation plugin on Slot '{}': {}", slot_info.slot_id, validator.what())){}; + ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept : msg_(fmt::format("Failed to validation plugin on Slot '{}'", slot_info.slot_id)){}; ValidatorException(const auto& validator, const slot_metadata& slot_info, const plugin_metadata& plugin_info) noexcept - : msg_(fmt::format("Failed to validate plugin {} '{}' at {} for slot '{}': {}", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, validator.what())) + : msg_(fmt::format("Failed to validate plugin {} '{}' at {} for slot '{}'", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id)) { } diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 70f4d0da26..20dccd582e 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -123,7 +123,7 @@ class PluginProxy boost::asio::co_spawn( grpc_context, - [&]() -> boost::asio::awaitable + [this, &status, &grpc_context, &ret_value, &args...]() -> boost::asio::awaitable { using RPC = agrpc::RPC<&stub_t::PrepareAsyncModify>; grpc::ClientContext client_context{}; @@ -146,7 +146,7 @@ class PluginProxy if (! plugin_info_.has_value()) { plugin_info_ = plugin_metadata{ client_context }; - valid_ = validator_type{ plugin_info_->slot_version }; + valid_ = validator_type{ slot_info_, plugin_info_.value() }; } }, boost::asio::detached); diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 9bf2c85693..3360cf4887 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -35,11 +35,11 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template class ValidatorTp, class RequestTp, class ResponseTp, class Default> +template class SlotProxy { Default default_process{}; - using value_type = PluginProxy, RequestTp, ResponseTp>; + using value_type = PluginProxy; std::optional plugin_{ std::nullopt }; public: diff --git a/include/plugins/validator.h b/include/plugins/validator.h index d6e791fcfa..08956cd23e 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -7,28 +7,16 @@ #include #include +#include "plugins/metadata.h" #include "plugins/types.h" namespace cura::plugins { -/** - * @brief A class for validating plugin versions and hashes. - * - * The `Validator` class provides functionality to validate plugin versions and hashes against - * specified version ranges and plugin hashes. - * - * @tparam VersionRange The version range specified as a character range literal. - * @tparam PluginHash The plugin hash specified as a character range literal. - */ -template class Validator { - semver::range::detail::range semver_range_{ VersionRange.value }; ///< The semver range object. - semver::version version_{ "1.0.0" }; ///< The version to validate. bool include_prerelease_{ true }; ///< Flag indicating whether to include prerelease versions. bool valid_{ false }; ///< Flag indicating the validity of the version. - std::string what_{}; public: /** @@ -41,18 +29,13 @@ class Validator * * @param version The version to validate. */ - constexpr explicit Validator(std::string_view version) noexcept + Validator(const slot_metadata& slot_info, const plugin_metadata& plugin_info) { - auto semver_version = semver::from_string_noexcept(version); - if (semver_version.has_value()) + auto slot_range = semver::range::detail::range(slot_info.version_range); + auto slot_version = semver::from_string(plugin_info.slot_version); + if (slot_range.satisfies(slot_version, include_prerelease_)) { - version_ = semver_version.value(); - valid_ = valid_version(); - } - else - { - valid_ = false; - what_ = fmt::format("Received invalid formatted version {} from plugin, expected version according to semver.", version); + valid_ = true; } }; @@ -65,27 +48,6 @@ class Validator { return valid_; } - - constexpr std::string_view what() const noexcept - { - return what_; - } - - - /** - * @brief Checks if the version is valid according to the specified version range. - * - * @return True if the version is valid, false otherwise. - */ - [[nodiscard]] constexpr bool valid_version() noexcept - { - auto valid_version{ semver_range_.satisfies(version_, include_prerelease_) }; - if (! valid_version) - { - what_ = fmt::format("CuraEngine requires a 'slot version' within the range of '{}', while the plugin reports to have version: '{}'", VersionRange.value, version_.to_string()); - } - return valid_version; - } }; } // namespace cura::plugins From 6c574caf9a88bd98720ae567d89c07f8c6df22cc Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 14:17:47 +0200 Subject: [PATCH 085/656] Add RemoteException for propagation of error's that occur in the plugin service [CURA-10475] --- include/plugins/exception.h | 19 +++++++++++++++++++ include/plugins/pluginproxy.h | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/plugins/exception.h b/include/plugins/exception.h index d3d50af969..d9147d14af 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -8,6 +8,7 @@ #include #include +#include #include #include "plugins/metadata.h" @@ -34,6 +35,24 @@ class ValidatorException : public std::exception } }; +class RemoteException : public std::exception +{ + std::string msg_; + +public: + RemoteException(const slot_metadata& slot_info, std::string_view error_msg) noexcept : msg_(fmt::format("Remote exception on Slot '{}': {}", slot_info.slot_id, error_msg)){}; + + RemoteException(const slot_metadata& slot_info, const plugin_metadata& plugin_info, std::string_view error_msg) noexcept + : msg_(fmt::format("Remote exception for plugin {} '{}' at {} for slot '{}': {}", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, error_msg)) + { + } + + virtual const char* what() const noexcept override + { + return msg_.c_str(); + } +}; + } // namespace cura::plugins::exceptions #endif // UTILS_CONCEPTS_GRAPH_H \ No newline at end of file diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 20dccd582e..78bbabc227 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -156,9 +156,9 @@ class PluginProxy { if (plugin_info_.has_value()) { - throw std::runtime_error(fmt::format("Slot {} with plugin {} '{}' at {} had a communication failure: {}", slot_info_.slot_id, plugin_info_->name, plugin_info_->version, plugin_info_->peer, status.error_message())); + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); } - throw std::runtime_error(fmt::format("Slot {} had a communication failure: {}", slot_info_.slot_id, status.error_message())); + throw exceptions::RemoteException(slot_info_, status.error_message()); } if (! valid_ ) From 11ea610d8cff06db9dd8f5e47ad884ed5827e820 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 14:20:08 +0200 Subject: [PATCH 086/656] Remove big 6 No longer needed [CURA-10475] --- include/plugins/pluginproxy.h | 23 ----------------------- include/plugins/slotproxy.h | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 78bbabc227..f6163a0ac2 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -78,29 +78,6 @@ class PluginProxy */ constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) : stub_(channel){}; - constexpr PluginProxy(const PluginProxy&) = default; - constexpr PluginProxy(PluginProxy&&) noexcept = default; - constexpr PluginProxy& operator=(const PluginProxy& other) - { - if (this != &other) - { - valid_ = other.valid_; - stub_ = other.stub_; - plugin_info_ = other.plugin_info_; - } - return *this; - } - constexpr PluginProxy& operator=(PluginProxy&& other) - { - if (this != &other) - { - valid_ = std::move(other.valid_); - stub_ = std::move(other.stub_); - plugin_info_ = std::move(other.plugin_info_); - } - return *this; - } - ~PluginProxy() = default; /** * @brief Executes the plugin operation. diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 3360cf4887..66e5db5d9d 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -57,7 +57,7 @@ class SlotProxy * * @param channel A shared pointer to the gRPC channel for communication with the plugin. */ - SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; + explicit SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; /** * @brief Executes the plugin operation. From 96f8f62a9cc89f22d57bc830ee27d8699242f3fb Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 14:37:53 +0200 Subject: [PATCH 087/656] Build with new proto hierachy [CURA-10475] --- CMakeLists.txt | 2 +- conanfile.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8964268165..87dce7685f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) -asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" +asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" IMPORT_DIRS ${GRPC_IMPORT_DIRS} OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" GENERATE_GRPC GENERATE_MOCK_CODE) diff --git a/conanfile.py b/conanfile.py index 90aab8ad90..43126b1581 100644 --- a/conanfile.py +++ b/conanfile.py @@ -114,7 +114,8 @@ def generate(self): tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info - tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).glob("*.proto")]) + tc.variables["GRPC_IMPORT_DIRS"] = cpp_info.resdirs[0] + tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).rglob("*.proto")]) tc.generate() From 4362291188d765fbdb620edbfc15cf1865c54641 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 14:53:54 +0200 Subject: [PATCH 088/656] Use curaengine_grpc_definitions from main [CURA-10475] --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 43126b1581..53a9318c95 100644 --- a/conanfile.py +++ b/conanfile.py @@ -100,7 +100,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/arcus_replacement") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") def generate(self): deps = CMakeDeps(self) From 09affeaea43ad5a68a658fd1dd700bdc11fb5752 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 15:11:10 +0200 Subject: [PATCH 089/656] Fixed new proto hierarchy [CURA-10475] --- include/plugins/converters.h | 16 ++++++++-------- include/plugins/pluginproxy.h | 2 +- include/plugins/slots.h | 10 +++++----- include/plugins/types.h | 4 +--- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index dc7c77d48d..58e5276ac9 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -12,17 +12,17 @@ #include "plugins/types.h" -#include "postprocess.grpc.pb.h" -#include "postprocess.pb.h" -#include "simplify.grpc.pb.h" -#include "simplify.pb.h" +#include "cura/plugins/slots/postprocess/v1/postprocess.grpc.pb.h" +#include "cura/plugins/slots/postprocess/v1/postprocess.pb.h" +#include "cura/plugins/slots/simplify/v1/simplify.grpc.pb.h" +#include "cura/plugins/slots/simplify/v1/simplify.pb.h" namespace cura::plugins { struct simplify_request { - using value_type = plugins::v1::SimplifyServiceModifyRequest; ///< The protobuf message type. + using value_type = slots::simplify::v1::SimplifyServiceModifyRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -80,7 +80,7 @@ struct simplify_request */ struct simplify_response { - using value_type = plugins::v1::SimplifyServiceModifyResponse; ///< The protobuf message type. + using value_type = slots::simplify::v1::SimplifyServiceModifyResponse; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -118,7 +118,7 @@ struct simplify_response struct postprocess_request { - using value_type = plugins::v1::PostprocessServiceModifyRequest; ///< The protobuf message type. + using value_type = slots::postprocess::v1::PostprocessServiceModifyRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. /** @@ -137,7 +137,7 @@ struct postprocess_request struct postprocess_response { - using value_type = plugins::v1::PostprocessServiceModifyResponse; + using value_type = slots::postprocess::v1::PostprocessServiceModifyResponse; using native_value_type = std::string; native_value_type operator()(const value_type& message) const diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index f6163a0ac2..ef2f8529e2 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,7 +19,7 @@ #include "plugins/exception.h" #include "plugins/metadata.h" -#include "slot_id.pb.h" +#include "cura/plugins/v1/slot_id.pb.h" #include "utils/concepts/generic.h" namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 656d0841a6..afa93d9507 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -14,9 +14,9 @@ #include "utils/IntPoint.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed -#include "postprocess.grpc.pb.h" -#include "simplify.grpc.pb.h" -#include "slot_id.pb.h" +#include "cura/plugins/slots/postprocess/v1/postprocess.grpc.pb.h" +#include "cura/plugins/slots/simplify/v1/simplify.grpc.pb.h" +#include "cura/plugins/v1/slot_id.pb.h" namespace cura { @@ -49,7 +49,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy; +using slot_simplify_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -59,7 +59,7 @@ using slot_simplify_ = SlotProxy -using slot_postprocess_ = SlotProxy; +using slot_postprocess_ = SlotProxy; template struct Typelist diff --git a/include/plugins/types.h b/include/plugins/types.h index b4820dcfb0..ff63a23183 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -14,12 +14,10 @@ #include "utils/concepts/generic.h" #include "utils/polygon.h" -#include "slot_id.pb.h" +#include "cura/plugins/v1/slot_id.pb.h" namespace cura::plugins { -using SlotID = plugins::v1::SlotID; - namespace details { template From 8e9fd81a10f30e04c7f349b09eb2ad8cfa02a96f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 15:20:42 +0200 Subject: [PATCH 090/656] Use correct namespace for SlotID [CURA-10475] --- include/plugins/types.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/plugins/types.h b/include/plugins/types.h index ff63a23183..8c87f35358 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -40,19 +40,19 @@ namespace fmt { // Custom formatter for humanreadable slot_id's template<> -struct formatter +struct formatter { template - auto format(cura::plugins::SlotID slot_id, FormatContext& ctx) + auto format(cura::plugins::v1::SlotID slot_id, FormatContext& ctx) { std::string slot_name; switch (slot_id) { - case cura::plugins::SlotID::SIMPLIFY: + case cura::plugins::v1::SlotID::SIMPLIFY: slot_name = "SimplifyService"; break; - case cura::plugins::SlotID::POSTPROCESS: + case cura::plugins::v1::SlotID::POSTPROCESS: slot_name = "PostprocessService"; break; default: From a25efd123365e6f3a206c0a119f1f713be3b1ad5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 15:24:15 +0200 Subject: [PATCH 091/656] Revert "Remove big 6" This reverts commit 11ea610d8cff06db9dd8f5e47ad884ed5827e820. --- include/plugins/pluginproxy.h | 23 +++++++++++++++++++++++ include/plugins/slotproxy.h | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index ef2f8529e2..b3226aa3ab 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -78,6 +78,29 @@ class PluginProxy */ constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) : stub_(channel){}; + constexpr PluginProxy(const PluginProxy&) = default; + constexpr PluginProxy(PluginProxy&&) noexcept = default; + constexpr PluginProxy& operator=(const PluginProxy& other) + { + if (this != &other) + { + valid_ = other.valid_; + stub_ = other.stub_; + plugin_info_ = other.plugin_info_; + } + return *this; + } + constexpr PluginProxy& operator=(PluginProxy&& other) + { + if (this != &other) + { + valid_ = std::move(other.valid_); + stub_ = std::move(other.stub_); + plugin_info_ = std::move(other.plugin_info_); + } + return *this; + } + ~PluginProxy() = default; /** * @brief Executes the plugin operation. diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 66e5db5d9d..3360cf4887 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -57,7 +57,7 @@ class SlotProxy * * @param channel A shared pointer to the gRPC channel for communication with the plugin. */ - explicit SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; + SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; /** * @brief Executes the plugin operation. From 4ca9d6da5b21646c8a5a933ee40efe9fc282acaa Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 23 May 2023 16:01:24 +0200 Subject: [PATCH 092/656] Move 'secure-optional' create channel to own file and fix merge. part of CURA-10475 --- include/plugins/channel.h | 40 +++++++++++++ src/Application.cpp | 71 ------------------------ src/communication/ArcusCommunication.cpp | 6 +- 3 files changed, 43 insertions(+), 74 deletions(-) create mode 100644 include/plugins/channel.h diff --git a/include/plugins/channel.h b/include/plugins/channel.h new file mode 100644 index 0000000000..e580ef49bf --- /dev/null +++ b/include/plugins/channel.h @@ -0,0 +1,40 @@ +#ifndef CHANNEL_H +#define CHANNEL_H + +#define REMOTE_PLUGIN_BUILD +// ^^ TODO: remove later & move to build-system + +#include "plugins/slots.h" + +#ifdef REMOTE_PLUGIN_BUILD +#include "../secrets/plugins.crt.h" +#include "../secrets/plugins.key.h" +#endif + +namespace cura::plugins +{ + struct ChannelSetupConfiguration + { + public: + std::string host; + uint64_t port; + uint64_t shibolet = 0UL; //TODO: Either get from startup (plugin would receive this as well) or remove completely. + }; + + auto createChannel(const ChannelSetupConfiguration& plugins_config = {"localhost", 50010UL}) + { +#ifdef REMOTE_PLUGIN_BUILD + auto creds_config = grpc::SslCredentialsOptions(); + creds_config.pem_root_certs = secrets::certificate; + creds_config.pem_cert_chain = secrets::certificate; + creds_config.pem_private_key = secrets::private_key; + auto channel_creds = grpc::SslCredentials(creds_config); +#else // not REMOTE_PLUGIN_BUILD + auto channel_creds = grpc::InsecureChannelCredentials(); +#endif + return grpc::CreateChannel(fmt::format("{}:{}", plugins_config.host, plugins_config.port), channel_creds); + } + +} // namespace cura::plugins + +#endif // CHANNEL_H diff --git a/src/Application.cpp b/src/Application.cpp index c8d6c2f3b6..15454631d8 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -3,9 +3,6 @@ #include "Application.h" -// TODO: Make this (enterprise) happen from build-system. -#define ENTERPRISE_BUILD 1 - #include #include #include @@ -28,24 +25,9 @@ #include "plugins/slots.h" -#ifdef ENTERPRISE_BUILD -#include "../secrets/plugins.crt.h" -#include "../secrets/plugins.key.h" -#endif - namespace cura { -//TODO: Get these from the command-line and/or frontend. -struct PluginSetupConfiguration -{ -public: - std::string host = "localhost"; - uint64_t port = 50010UL; - - uint64_t shibolet = 56785678UL; // Assume both us and the plugin have been given this on start-up, in a scenario that needs more security. -} PLUGIN_CONFIG; - Application::Application() { auto dup_sink = std::make_shared(std::chrono::seconds{ 10 }); @@ -206,8 +188,6 @@ void Application::run(const size_t argc, char** argv) exit(1); } - registerPlugins(PLUGIN_CONFIG); - #ifdef ARCUS if (stringcasecompare(argv[1], "connect") == 0) { @@ -269,55 +249,4 @@ void Application::startThreadPool(int nworkers) thread_pool = new ThreadPool(nthreads); } -// TODO: Where to put this in the structure, does this belong to 'Application'? -auto createChannel(const PluginSetupConfiguration& plugins_config) -{ -#ifdef ENTERPRISE_BUILD - auto creds_config = grpc::SslCredentialsOptions(); - creds_config.pem_root_certs = secrets::certificate; - creds_config.pem_cert_chain = secrets::certificate; - creds_config.pem_private_key = secrets::private_key; - auto channel_creds = grpc::SslCredentials(creds_config); -#else // NOT ENTERPRISE_BUILD - // TODO: Do we want security to be on always? - auto channel_creds = grpc::InsecureChannelCredentials(); -#endif - return grpc::CreateChannel(fmt::format("{}:{}", plugins_config.host, plugins_config.port), channel_creds); -} - -void Application::registerPlugins(const PluginSetupConfiguration& plugins_config) -{ - // TODO: remove this - - constexpr auto simplify_default = [](const Polygons& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) - { - const Simplify simplify{ max_resolution, max_deviation, max_area_deviation }; - return simplify.polygon(polygons); - }; - using simplify_t = plugins::simplify_slot; - - constexpr auto postprocess_default = [](std::string word){ return word; }; - using postprocess_t = plugins::postprocess_slot; - - using slot_registry = plugins::Slots; - - if (true) // determine wat to register depending if front-end starts a plugin - { - slot_registry::instance().set(simplify_t{createChannel(plugins_config)}); - } - else - { - slot_registry::instance().set(simplify_t{}); - } - slot_registry::instance().set(postprocess_t{}); - - auto simplify_plugin = slot_registry::instance().get(); - Polygons poly{}; - Polygon p{}; - p.poly = {{0,1}, {2,3}, {4,5}}; - poly.add(p); - auto x = simplify_plugin(poly, 100, 200, 300); - spdlog::info("simplified poly received"); -} - } // namespace cura diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index e975df92dd..ccf8da5ed2 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -24,6 +24,7 @@ #include "settings/types/Velocity.h" //To send to layer view how fast stuff is printing. #include "utils/polygon.h" +#include "plugins/channel.h" #include "plugins/slots.h" namespace cura @@ -509,7 +510,6 @@ void ArcusCommunication::sliceNext() spdlog::debug("Received a Slice message."); // TODO: Use typemap - constexpr auto create_channel = [&](auto&&... args){ return grpc::CreateChannel(fmt::format("{}:{}", std::forward(args)...), grpc::InsecureChannelCredentials()); }; for (const auto& plugin : slice_message->engine_plugins()) { if (plugin.has_address() && plugin.has_port()) @@ -517,10 +517,10 @@ void ArcusCommunication::sliceNext() switch (plugin.id()) { case cura::proto::SlotID::SIMPLIFY: - slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); + slots::instance().connect(plugins::createChannel({ plugin.address(), plugin.port() })); break; case cura::proto::SlotID::POSTPROCESS: - slots::instance().connect( create_channel(plugin.address(), plugin.port()) ); + slots::instance().connect(plugins::createChannel({ plugin.address(), plugin.port() })); break; default: break; } From 11eb6d831d573d81b5255cdb47a3d25c581e52c8 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 16:40:19 +0200 Subject: [PATCH 093/656] Fixed the slot validation [CURA-10475] --- include/plugins/exception.h | 4 ++-- include/plugins/metadata.h | 8 ++++---- include/plugins/pluginproxy.h | 6 +++++- include/plugins/validator.h | 6 ++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/plugins/exception.h b/include/plugins/exception.h index d9147d14af..9ef2fdcf07 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -25,7 +25,7 @@ class ValidatorException : public std::exception ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept : msg_(fmt::format("Failed to validation plugin on Slot '{}'", slot_info.slot_id)){}; ValidatorException(const auto& validator, const slot_metadata& slot_info, const plugin_metadata& plugin_info) noexcept - : msg_(fmt::format("Failed to validate plugin {} '{}' at {} for slot '{}'", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id)) + : msg_(fmt::format("Failed to validate plugin '{}-{}' running at [{}] for slot '{}', slot range '{}' incompatible with plugin slot version '{}'", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, slot_info.version_range, plugin_info.slot_version)) { } @@ -43,7 +43,7 @@ class RemoteException : public std::exception RemoteException(const slot_metadata& slot_info, std::string_view error_msg) noexcept : msg_(fmt::format("Remote exception on Slot '{}': {}", slot_info.slot_id, error_msg)){}; RemoteException(const slot_metadata& slot_info, const plugin_metadata& plugin_info, std::string_view error_msg) noexcept - : msg_(fmt::format("Remote exception for plugin {} '{}' at {} for slot '{}': {}", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, error_msg)) + : msg_(fmt::format("Remote exception for plugin '{}-{}' running at [{}] for slot '{}': {}", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, error_msg)) { } diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 41d83290ed..b49c17beb5 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -16,10 +16,10 @@ namespace cura::plugins { struct plugin_metadata { - std::string_view name; - std::string_view version; - std::string_view peer; - std::string_view slot_version; + std::string name; // cura-plugin-name (optional) + std::string version; // cura-plugin-version (optional) + std::string peer; + std::string slot_version; // cura-slot-version (required) explicit plugin_metadata(const grpc::ClientContext& client_context) { diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index b3226aa3ab..dd05ddde28 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -147,6 +147,10 @@ class PluginProxy { plugin_info_ = plugin_metadata{ client_context }; valid_ = validator_type{ slot_info_, plugin_info_.value() }; + if (valid_) + { + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->name, plugin_info_->version, plugin_info_->peer, slot_info_.slot_id); + } } }, boost::asio::detached); @@ -170,7 +174,7 @@ class PluginProxy throw exceptions::ValidatorException(valid_, slot_info_); } - return ret_value; // TODO: check if ret_value is always filled or if we need a solution like: https://stackoverflow.com/questions/67908591/how-to-convert-boostasioawaitable-to-stdfuture + return ret_value; } }; } // namespace cura::plugins diff --git a/include/plugins/validator.h b/include/plugins/validator.h index 08956cd23e..fb61db1cf8 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -6,6 +6,7 @@ #include #include +#include #include "plugins/metadata.h" #include "plugins/types.h" @@ -33,10 +34,7 @@ class Validator { auto slot_range = semver::range::detail::range(slot_info.version_range); auto slot_version = semver::from_string(plugin_info.slot_version); - if (slot_range.satisfies(slot_version, include_prerelease_)) - { - valid_ = true; - } + valid_ = slot_range.satisfies(slot_version, include_prerelease_); }; /** From 24c6b0cdf6807c01c9d077394b6fc8b15094991e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 17:02:12 +0200 Subject: [PATCH 094/656] Use v0 namespace [CURA-10475] --- include/plugins/converters.h | 8 ++++---- include/plugins/metadata.h | 2 +- include/plugins/pluginproxy.h | 2 +- include/plugins/slotproxy.h | 2 +- include/plugins/slots.h | 4 ++-- include/plugins/types.h | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 58e5276ac9..03bcbcd69b 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -22,7 +22,7 @@ namespace cura::plugins struct simplify_request { - using value_type = slots::simplify::v1::SimplifyServiceModifyRequest; ///< The protobuf message type. + using value_type = slots::simplify::v0::SimplifyServiceModifyRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -80,7 +80,7 @@ struct simplify_request */ struct simplify_response { - using value_type = slots::simplify::v1::SimplifyServiceModifyResponse; ///< The protobuf message type. + using value_type = slots::simplify::v0::SimplifyServiceModifyResponse; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -118,7 +118,7 @@ struct simplify_response struct postprocess_request { - using value_type = slots::postprocess::v1::PostprocessServiceModifyRequest; ///< The protobuf message type. + using value_type = slots::postprocess::v0::PostprocessServiceModifyRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. /** @@ -137,7 +137,7 @@ struct postprocess_request struct postprocess_response { - using value_type = slots::postprocess::v1::PostprocessServiceModifyResponse; + using value_type = slots::postprocess::v0::PostprocessServiceModifyResponse; using native_value_type = std::string; native_value_type operator()(const value_type& message) const diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index b49c17beb5..db17d9a201 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -55,7 +55,7 @@ struct plugin_metadata struct slot_metadata { - plugins::v1::SlotID slot_id; + plugins::v0::SlotID slot_id; std::string_view version_range; }; diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index dd05ddde28..7446f71119 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -38,7 +38,7 @@ namespace cura::plugins * @tparam Request The gRPC convertible request type. * @tparam Response The gRPC convertible response type. */ -template +template class PluginProxy { public: diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 3360cf4887..d3ac43136d 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -35,7 +35,7 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template +template class SlotProxy { Default default_process{}; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index afa93d9507..05fb775533 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -49,7 +49,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy; +using slot_simplify_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -59,7 +59,7 @@ using slot_simplify_ = SlotProxy -using slot_postprocess_ = SlotProxy; +using slot_postprocess_ = SlotProxy; template struct Typelist diff --git a/include/plugins/types.h b/include/plugins/types.h index 8c87f35358..f3b19e3a7b 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -40,19 +40,19 @@ namespace fmt { // Custom formatter for humanreadable slot_id's template<> -struct formatter +struct formatter { template - auto format(cura::plugins::v1::SlotID slot_id, FormatContext& ctx) + auto format(cura::plugins::v0::SlotID slot_id, FormatContext& ctx) { std::string slot_name; switch (slot_id) { - case cura::plugins::v1::SlotID::SIMPLIFY: + case cura::plugins::v0::SlotID::SIMPLIFY: slot_name = "SimplifyService"; break; - case cura::plugins::v1::SlotID::POSTPROCESS: + case cura::plugins::v0::SlotID::POSTPROCESS: slot_name = "PostprocessService"; break; default: From 84636f30c9f15f36d05613845fd0dfd4d53d3e00 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 23 May 2023 17:04:18 +0200 Subject: [PATCH 095/656] Use v0 includes [CURA-10475] --- include/plugins/converters.h | 8 ++++---- include/plugins/pluginproxy.h | 2 +- include/plugins/slots.h | 6 +++--- include/plugins/types.h | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 03bcbcd69b..0282605634 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -12,10 +12,10 @@ #include "plugins/types.h" -#include "cura/plugins/slots/postprocess/v1/postprocess.grpc.pb.h" -#include "cura/plugins/slots/postprocess/v1/postprocess.pb.h" -#include "cura/plugins/slots/simplify/v1/simplify.grpc.pb.h" -#include "cura/plugins/slots/simplify/v1/simplify.pb.h" +#include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" +#include "cura/plugins/slots/postprocess/v0/postprocess.pb.h" +#include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" +#include "cura/plugins/slots/simplify/v0/simplify.pb.h" namespace cura::plugins { diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 7446f71119..13daba6f0d 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,7 +19,7 @@ #include "plugins/exception.h" #include "plugins/metadata.h" -#include "cura/plugins/v1/slot_id.pb.h" +#include "cura/plugins/v0/slot_id.pb.h" #include "utils/concepts/generic.h" namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 05fb775533..ed29493824 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -14,9 +14,9 @@ #include "utils/IntPoint.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed -#include "cura/plugins/slots/postprocess/v1/postprocess.grpc.pb.h" -#include "cura/plugins/slots/simplify/v1/simplify.grpc.pb.h" -#include "cura/plugins/v1/slot_id.pb.h" +#include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" +#include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" +#include "cura/plugins/v0/slot_id.pb.h" namespace cura { diff --git a/include/plugins/types.h b/include/plugins/types.h index f3b19e3a7b..043a042c32 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -14,7 +14,7 @@ #include "utils/concepts/generic.h" #include "utils/polygon.h" -#include "cura/plugins/v1/slot_id.pb.h" +#include "cura/plugins/v0/slot_id.pb.h" namespace cura::plugins { From cee07b335852743452d5ad6f9fdbe1154ea546b1 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 23 May 2023 17:23:08 +0200 Subject: [PATCH 096/656] [Plugins] Make proper (optionally secure) client-channel. part of CURA-10475 --- include/plugins/channel.h | 46 +++++++++++++++++++++++------------ resources/certificate.pem.h | 48 +++++++++++++++++++++++++++++++++++++ resources/public_key.pem.h | 26 ++++++++++++++++++++ 3 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 resources/certificate.pem.h create mode 100644 resources/public_key.pem.h diff --git a/include/plugins/channel.h b/include/plugins/channel.h index e580ef49bf..05fb9c10a2 100644 --- a/include/plugins/channel.h +++ b/include/plugins/channel.h @@ -1,18 +1,23 @@ #ifndef CHANNEL_H #define CHANNEL_H -#define REMOTE_PLUGIN_BUILD -// ^^ TODO: remove later & move to build-system +#include +#include +#include "../resources/certificate.pem.h" #include "plugins/slots.h" -#ifdef REMOTE_PLUGIN_BUILD -#include "../secrets/plugins.crt.h" -#include "../secrets/plugins.key.h" +#ifndef BUILD_ALLOW_REMOTE_PLUGINS +#define BUILD_ALLOW_REMOTE_PLUGINS true #endif namespace cura::plugins { + namespace details + { + constexpr bool ALLOW_REMOTE_PLUGINS = (BUILD_ALLOW_REMOTE_PLUGINS); + } // namespace details + struct ChannelSetupConfiguration { public: @@ -23,16 +28,27 @@ namespace cura::plugins auto createChannel(const ChannelSetupConfiguration& plugins_config = {"localhost", 50010UL}) { -#ifdef REMOTE_PLUGIN_BUILD - auto creds_config = grpc::SslCredentialsOptions(); - creds_config.pem_root_certs = secrets::certificate; - creds_config.pem_cert_chain = secrets::certificate; - creds_config.pem_private_key = secrets::private_key; - auto channel_creds = grpc::SslCredentials(creds_config); -#else // not REMOTE_PLUGIN_BUILD - auto channel_creds = grpc::InsecureChannelCredentials(); -#endif - return grpc::CreateChannel(fmt::format("{}:{}", plugins_config.host, plugins_config.port), channel_creds); + constexpr auto create_credentials = + [](const ChannelSetupConfiguration& plugins_config) + { + if (plugins_config.host == "localhost" || plugins_config.host == "127.0.0.1") + { + spdlog::info("Create local channel on port {}.", plugins_config.port); + return grpc::InsecureChannelCredentials(); + } + else if (details::ALLOW_REMOTE_PLUGINS) + { + spdlog::info("Create local channel on port {}.", plugins_config.port); + auto creds_config = grpc::SslCredentialsOptions(); + creds_config.pem_root_certs = resources::certificate; + return grpc::SslCredentials(creds_config); + } + // Create empty credentials, so it'll make a dummy channel where all operations fail. + // This is consitent with creating a channel with the wrong credentials as it where. + spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", plugins_config.host, plugins_config.port); + return std::shared_ptr(); + }; + return grpc::CreateChannel(fmt::format("{}:{}", plugins_config.host, plugins_config.port), create_credentials(plugins_config)); } } // namespace cura::plugins diff --git a/resources/certificate.pem.h b/resources/certificate.pem.h new file mode 100644 index 0000000000..2ccb343cd8 --- /dev/null +++ b/resources/certificate.pem.h @@ -0,0 +1,48 @@ +#ifndef CERTIFICATE_H +#define CERTIFICATE_H + +#include + +namespace resources +{ + constexpr std::string_view certificate = +R"#(-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIUd6fjXFbNBMZOxfBnv4FOokHlOC4wDQYJKoZIhvcNAQEL +BQAwgYYxCzAJBgNVBAYTAk5MMRMwEQYDVQQIDApHZWxkZXJsYW5kMRUwEwYDVQQH +DAxHZWxkZXJtYWxzZW4xEjAQBgNVBAoMCVVsdGlNYWtlcjEbMBkGA1UECwwSQ29t +bXVuaXR5IFNvZnR3YXJlMRowGAYDVQQDDBFQbHVnaW5zIFZhbGlkYXRvcjAgFw0y +MzA1MTcxMzQ3NTFaGA8yMTEzMDIwMTEzNDc1MVowgYYxCzAJBgNVBAYTAk5MMRMw +EQYDVQQIDApHZWxkZXJsYW5kMRUwEwYDVQQHDAxHZWxkZXJtYWxzZW4xEjAQBgNV +BAoMCVVsdGlNYWtlcjEbMBkGA1UECwwSQ29tbXVuaXR5IFNvZnR3YXJlMRowGAYD +VQQDDBFQbHVnaW5zIFZhbGlkYXRvcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBALNcLyGzwSpkGzsiM9pT821AdXJqCM7CBwsy9E9Tpf/D+iBdwQsukIBc +HrkHf9g+q9BYDIKu0/eqeE7coQg0HAJ/CxowWAAo5MCYJHqDGp73IHUVxvGetVVr +y6r13QEpj/Mzvz4nnyZMlwdXjWLyz/AtDRfT0i+TbyqK3BYBTs0YW3/fG4g4Dd+v +8OsQOjfDDTv5iQSaNQuiSfCbQA8bL1aXqijFcE2imRnLFQ3IxrcLVkkXEA4xAps5 +34aVKVBXFAS7UII0MmTvhmHSZ6Vl21fAhmGwJGPrQSlSQ8JhVuIs4q7Tfp1SHeoH ++lmid9X92iWlrM/gDZLiKMk8VHlt9Y1kaYPUUKq1mNoHGJ8Ix4J8umCVCaIezYY5 +QqmCLKniP4VuCGoYaXuiWf/EOa+YGMGYaKB9yJLsQpuoulWVTrKyriv5YW68CT0u +jokCDmf33rQc4axxnu0ot+Up6nmV7XcGApLQWxJfbx9hhFOYfipWzgtX1FlXbx62 +5gG8WWw0NjCKpTN39Qq7y8bJIJoCMXlKrZuZ5yDIJsgTsVYX3RLOw+CuOiagjN0f +9btL4X7uO0b34vFjckd1GDrDgNtWjEJiFBQqM82e3BzwEcG1Gkh6LqaFzJpqo8Ql +tAwr+0uWTN5jCe5+COiSKf3r7YBhCdfsPe2U95+g6GGq2+J3IPlbAgMBAAGjUzBR +MB0GA1UdDgQWBBTTEqyXIy/N6+MkniTCLk3bRMIcTzAfBgNVHSMEGDAWgBTTEqyX +Iy/N6+MkniTCLk3bRMIcTzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4ICAQAM8YYARPRmAYVJTrDL1D7I8w7vRHkRak57Y3CpvgcCQLZSjsc94FVeUUbd +zE7vLM/Fh59hu4PJQfrBfCpoixRqZ5Wqmh0R/Ws0mxaqBnkRcKR/Su76iFGiEAQ1 +tzHjGTUw+Qg8/rR9QoJjhwjrjofBYifAyNikvJ0AeHkAWLdT4BFJjZydi2zGoeZw +Er5uROlGntso2BEoW7sGsFVXjCN8R+oezihowjqluRbQ6yuKj6Bpb6AMZVAj7bx0 +SnoE1Nqo6b0qyAd8HldJK5NinXdwbOzhSs1JtISLB/5r9ZJYmgBIEqom03/lJ49s +Y2ceQP+xK0UApsHifDlOo8/3NoUiaZnCfGF+feEE7b6+hDQ7PdoY6wve3sUrpjKf +C2qmE5LEOR0fHxCgj8KiRm+AapomJdGHjlbaaf2q+9cnG7WV6K/OFz+f38MVS9Cz +XbOyLHSJGZNMjdLy722DT8saVrR85nStG4KrnlDDsr0kLp31QpObHWgw6NU4pU5n +/ZIUW53dq0QUy3sTpehM3U1EfxWLkqP5EwID5Vny27VEqrHohlXjjWOf/G0Q64MS +ZtGCFwzJwPHmTaVZnLO8zc0tbnzJjxkJbrEnhaQvHdz0BBHEFvZafUFCGJahMPJV +c3dQRszg9OTAyUwNKQEAuZ9II/cCpLyE+GbnqRdkErHgO7kiKQ== +-----END CERTIFICATE----- +)#"; + +} // namespace resources + +#endif //CERTIFICATE_H + diff --git a/resources/public_key.pem.h b/resources/public_key.pem.h new file mode 100644 index 0000000000..24f1676478 --- /dev/null +++ b/resources/public_key.pem.h @@ -0,0 +1,26 @@ +#ifndef PUBLIC_KEY_H +#define PUBLIC_KEY_H + +#include + +namespace resources +{ + constexpr std::string_view public_key = +R"#(-----BEGIN RSA PUBLIC KEY----- +MIICCgKCAgEAs1wvIbPBKmQbOyIz2lPzbUB1cmoIzsIHCzL0T1Ol/8P6IF3BCy6Q +gFweuQd/2D6r0FgMgq7T96p4TtyhCDQcAn8LGjBYACjkwJgkeoManvcgdRXG8Z61 +VWvLqvXdASmP8zO/PiefJkyXB1eNYvLP8C0NF9PSL5NvKorcFgFOzRhbf98biDgN +36/w6xA6N8MNO/mJBJo1C6JJ8JtADxsvVpeqKMVwTaKZGcsVDcjGtwtWSRcQDjEC +mznfhpUpUFcUBLtQgjQyZO+GYdJnpWXbV8CGYbAkY+tBKVJDwmFW4izirtN+nVId +6gf6WaJ31f3aJaWsz+ANkuIoyTxUeW31jWRpg9RQqrWY2gcYnwjHgny6YJUJoh7N +hjlCqYIsqeI/hW4Iahhpe6JZ/8Q5r5gYwZhooH3IkuxCm6i6VZVOsrKuK/lhbrwJ +PS6OiQIOZ/fetBzhrHGe7Si35SnqeZXtdwYCktBbEl9vH2GEU5h+KlbOC1fUWVdv +HrbmAbxZbDQ2MIqlM3f1CrvLxskgmgIxeUqtm5nnIMgmyBOxVhfdEs7D4K46JqCM +3R/1u0vhfu47Rvfi8WNyR3UYOsOA21aMQmIUFCozzZ7cHPARwbUaSHoupoXMmmqj +xCW0DCv7S5ZM3mMJ7n4I6JIp/evtgGEJ1+w97ZT3n6DoYarb4ncg+VsCAwEAAQ== +-----END RSA PUBLIC KEY----- +)#"; + +} // namespace resources + +#endif //PUBLIC_KEY_H From a5f986410db9b062ada8458a2767a71891e9c172 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 23 May 2023 17:31:15 +0200 Subject: [PATCH 097/656] Fix build on Windows again. done as part of CURA-10475 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 53a9318c95..affbee0b6c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -114,7 +114,7 @@ def generate(self): tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info - tc.variables["GRPC_IMPORT_DIRS"] = cpp_info.resdirs[0] + tc.variables["GRPC_IMPORT_DIRS"] = cpp_info.resdirs[0].replace("\\", "/") tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).rglob("*.proto")]) tc.generate() From b401f314df8218a101ba911734e1b70f41b6326d Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 23 May 2023 18:22:14 +0200 Subject: [PATCH 098/656] Split channel-code into header and source. Also move to utils. And fix benchmark code. part of CURA-10475 --- CMakeLists.txt | 1 + benchmark/simplify_benchmark.h | 11 +++-- include/plugins/channel.h | 56 ------------------------ include/utils/channel.h | 26 +++++++++++ src/communication/ArcusCommunication.cpp | 7 ++- src/utils/channel.cpp | 40 +++++++++++++++++ 6 files changed, 77 insertions(+), 64 deletions(-) delete mode 100644 include/plugins/channel.h create mode 100644 include/utils/channel.h create mode 100644 src/utils/channel.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 86528004b1..a0ac0bd8d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ set(engine_SRCS # Except main.cpp. src/utils/AABB.cpp src/utils/AABB3D.cpp + src/utils/channel.cpp src/utils/Date.cpp src/utils/ExtrusionJunction.cpp src/utils/ExtrusionLine.cpp diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 4d9862e0ce..b5e6d79b0c 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -11,7 +11,10 @@ #include #include "../tests/ReadTestPolygons.h" + +#include "utils/channel.h" #include "utils/Simplify.h" + #include "plugins/slots.h" namespace cura @@ -61,7 +64,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } @@ -71,11 +74,11 @@ BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_noplugin); BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::State& st) { auto host = "localhost"; - auto port = 33700; + auto port = 33700UL; try { - slots::instance().connect( grpc::CreateChannel(fmt::format("{}:{}", host, port), grpc::InsecureChannelCredentials())); + slots::instance().connect(utils::createChannel({host, port})); } catch (std::runtime_error e) { @@ -86,7 +89,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } diff --git a/include/plugins/channel.h b/include/plugins/channel.h deleted file mode 100644 index 05fb9c10a2..0000000000 --- a/include/plugins/channel.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef CHANNEL_H -#define CHANNEL_H - -#include -#include - -#include "../resources/certificate.pem.h" -#include "plugins/slots.h" - -#ifndef BUILD_ALLOW_REMOTE_PLUGINS -#define BUILD_ALLOW_REMOTE_PLUGINS true -#endif - -namespace cura::plugins -{ - namespace details - { - constexpr bool ALLOW_REMOTE_PLUGINS = (BUILD_ALLOW_REMOTE_PLUGINS); - } // namespace details - - struct ChannelSetupConfiguration - { - public: - std::string host; - uint64_t port; - uint64_t shibolet = 0UL; //TODO: Either get from startup (plugin would receive this as well) or remove completely. - }; - - auto createChannel(const ChannelSetupConfiguration& plugins_config = {"localhost", 50010UL}) - { - constexpr auto create_credentials = - [](const ChannelSetupConfiguration& plugins_config) - { - if (plugins_config.host == "localhost" || plugins_config.host == "127.0.0.1") - { - spdlog::info("Create local channel on port {}.", plugins_config.port); - return grpc::InsecureChannelCredentials(); - } - else if (details::ALLOW_REMOTE_PLUGINS) - { - spdlog::info("Create local channel on port {}.", plugins_config.port); - auto creds_config = grpc::SslCredentialsOptions(); - creds_config.pem_root_certs = resources::certificate; - return grpc::SslCredentials(creds_config); - } - // Create empty credentials, so it'll make a dummy channel where all operations fail. - // This is consitent with creating a channel with the wrong credentials as it where. - spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", plugins_config.host, plugins_config.port); - return std::shared_ptr(); - }; - return grpc::CreateChannel(fmt::format("{}:{}", plugins_config.host, plugins_config.port), create_credentials(plugins_config)); - } - -} // namespace cura::plugins - -#endif // CHANNEL_H diff --git a/include/utils/channel.h b/include/utils/channel.h new file mode 100644 index 0000000000..ec3e9e7e01 --- /dev/null +++ b/include/utils/channel.h @@ -0,0 +1,26 @@ +#ifndef CHANNEL_H +#define CHANNEL_H + +#include +#include +#include + +#ifndef BUILD_ALLOW_REMOTE_CHANNELS +#define BUILD_ALLOW_REMOTE_CHANNELS true +#endif + +namespace cura::utils +{ + struct ChannelSetupConfiguration + { + public: + std::string host; + uint64_t port; + uint64_t shibolet = 0UL; //TODO: Either get from startup (server would receive this as well) or remove completely. + }; + + std::shared_ptr createChannel(const ChannelSetupConfiguration& config = { "localhost", 50010UL }); + +} // namespace cura::utils + +#endif // CHANNEL_H diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index ccf8da5ed2..8b4d9cf9f4 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -8,7 +8,6 @@ #include //To map settings to their extruder numbers for limit_to_extruder. #include -#include #include #include "Application.h" //To get and set the current slice command. @@ -22,9 +21,9 @@ #include "communication/SliceDataStruct.h" //To store sliced layer data. #include "settings/types/LayerIndex.h" //To point to layers. #include "settings/types/Velocity.h" //To send to layer view how fast stuff is printing. +#include "utils/channel.h" #include "utils/polygon.h" -#include "plugins/channel.h" #include "plugins/slots.h" namespace cura @@ -517,10 +516,10 @@ void ArcusCommunication::sliceNext() switch (plugin.id()) { case cura::proto::SlotID::SIMPLIFY: - slots::instance().connect(plugins::createChannel({ plugin.address(), plugin.port() })); + slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); break; case cura::proto::SlotID::POSTPROCESS: - slots::instance().connect(plugins::createChannel({ plugin.address(), plugin.port() })); + slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); break; default: break; } diff --git a/src/utils/channel.cpp b/src/utils/channel.cpp new file mode 100644 index 0000000000..c1672cbe7e --- /dev/null +++ b/src/utils/channel.cpp @@ -0,0 +1,40 @@ +#include "utils/channel.h" + +#include +#include + +#include "../resources/certificate.pem.h" + +namespace cura::utils +{ + namespace details + { + constexpr bool ALLOW_REMOTE_CHANNELS = (BUILD_ALLOW_REMOTE_CHANNELS); + } // namespace details + + std::shared_ptr createChannel(const ChannelSetupConfiguration& config) + { + constexpr auto create_credentials = + [](const ChannelSetupConfiguration& config) + { + if (config.host == "localhost" || config.host == "127.0.0.1") + { + spdlog::info("Create local channel on port {}.", config.port); + return grpc::InsecureChannelCredentials(); + } + else if (details::ALLOW_REMOTE_CHANNELS) + { + spdlog::info("Create local channel on port {}.", config.port); + auto creds_config = grpc::SslCredentialsOptions(); + creds_config.pem_root_certs = resources::certificate; + return grpc::SslCredentials(creds_config); + } + // Create empty credentials, so it'll make a dummy channel where all operations fail. + // This is consitent with creating a channel with the wrong credentials as it where. + spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", config.host, config.port); + return std::shared_ptr(); + }; + return grpc::CreateChannel(fmt::format("{}:{}", config.host, config.port), create_credentials(config)); + } + +} // namespace cura::utils From 37c1d22a8a69cdd83959b9aa992dea5b5125c55d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 24 May 2023 14:26:31 +0200 Subject: [PATCH 099/656] Fixed some compilation errors on the runner [CURA-10475] --- CMakeLists.txt | 2 ++ include/utils/concepts/generic.h | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87dce7685f..103948d55c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,7 @@ find_package(fmt REQUIRED) find_package(range-v3 REQUIRED) find_package(scripta REQUIRED) find_package(neargye-semver REQUIRED) +find_package(gRPC REQUIRED) if (ENABLE_TESTING) find_package(GTest REQUIRED) @@ -208,6 +209,7 @@ target_link_libraries(_CuraEngine scripta::scripta neargye-semver::neargye-semver asio-grpc::asio-grpc + grpc::grpc protobuf::libprotobuf $<$:GTest::gtest>) diff --git a/include/utils/concepts/generic.h b/include/utils/concepts/generic.h index 72c4f70dba..d0c37bab5d 100644 --- a/include/utils/concepts/generic.h +++ b/include/utils/concepts/generic.h @@ -8,21 +8,22 @@ #include #include +#include namespace cura { template concept hashable = requires(T value) { - { std::hash{}(value) } -> std::convertible_to; + { std::hash{}(value) } -> ranges::convertible_to; }; template concept grpc_convertable = requires(T value) { - requires std::semiregular; - requires std::semiregular; - requires std::semiregular; + requires ranges::semiregular; + requires ranges::semiregular; + requires ranges::semiregular; }; } // namespace cura From b36f2a1a568d8e9f17557b252a491f43c661c83d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 26 May 2023 09:30:11 +0200 Subject: [PATCH 100/656] Reenable ENABLE_PLUGINS [CURA-10475] --- CMakeLists.txt | 16 ++++++++++++---- conanfile.py | 12 +++++++++++- include/utils/channel.h | 7 +++++-- src/communication/ArcusCommunication.cpp | 3 ++- src/utils/channel.cpp | 9 ++++++--- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9bb840861..0db4918803 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,15 +10,22 @@ AssureOutOfSourceBuilds() option(ENABLE_ARCUS "Enable support for ARCUS" ON) option(ENABLE_TESTING "Build with unit tests" OFF) option(EXTENSIVE_WARNINGS "Build with all warnings" ON) -option(ENABLE_PLUGINS "Build with all warnings" ON) +option(ENABLE_PLUGINS "Build with plugins" ON) +option(ENABLE_REMOTE_PLUGINS "Build with all warnings" OFF) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) -#set(GRPC_PROTOS "List of all gRPC definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") +#set(GRPC_PROTOS "List of all protobuf definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") +#set(GRPC_IMPORT_DIRS "List of all protobuf dirs definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") # Generate the plugin types find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) -find_package(grpc REQUIRED) +find_package(gRPC REQUIRED) + +MESSAGE(STATUS "Compiling with plugins support: ${ENABLE_PLUGINS}") +if (${ENABLE_PLUGINS}) + MESSAGE(STATUS "Plugin secure remotes allowed: ${ENABLE_REMOTE_PLUGINS}") +endif () asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" IMPORT_DIRS ${GRPC_IMPORT_DIRS} OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" @@ -161,7 +168,8 @@ target_include_directories(_CuraEngine target_compile_definitions(_CuraEngine PUBLIC $<$:ARCUS> - $<$:PLUGINS> + $<$:ENABLE_PLUGINS> + $<$,$>:ENABLE_REMOTE_PLUGINS> CURA_ENGINE_VERSION=\"${CURA_ENGINE_VERSION}\" $<$:BUILD_TESTS> PRIVATE diff --git a/conanfile.py b/conanfile.py index affbee0b6c..46b5ed2a6f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -30,6 +30,7 @@ class CuraEngineConan(ConanFile): "enable_benchmarks": [True, False], "enable_extensive_warnings": [True, False], "enable_plugins": [True, False], + "enable_remote_plugins": [True, False], } default_options = { "enable_arcus": True, @@ -37,6 +38,7 @@ class CuraEngineConan(ConanFile): "enable_benchmarks": False, "enable_extensive_warnings": False, "enable_plugins": True, + "enable_remote_plugins": False, } def set_version(self): @@ -54,6 +56,10 @@ def export_sources(self): copy(self, "*", path.join(self.recipe_folder, "benchmark"), path.join(self.export_sources_folder, "benchmark")) copy(self, "*", path.join(self.recipe_folder, "tests"), path.join(self.export_sources_folder, "tests")) + def config_options(self): + if not self.options.enable_plugins: + del self.options.enable_remote_plugins + def configure(self): self.options["boost"].header_only = True self.options["clipper"].shared = True @@ -112,7 +118,11 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings - tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins + if self.options.enable_plugins: + tc.variables["ENABLE_PLUGINS"] = True + tc.variables["ENABLE_REMOTE_PLUGINS"] = self.options.enable_remote_plugins + else: + tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info tc.variables["GRPC_IMPORT_DIRS"] = cpp_info.resdirs[0].replace("\\", "/") tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).rglob("*.proto")]) diff --git a/include/utils/channel.h b/include/utils/channel.h index ec3e9e7e01..30dbb5dff2 100644 --- a/include/utils/channel.h +++ b/include/utils/channel.h @@ -1,3 +1,6 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + #ifndef CHANNEL_H #define CHANNEL_H @@ -5,8 +8,8 @@ #include #include -#ifndef BUILD_ALLOW_REMOTE_CHANNELS -#define BUILD_ALLOW_REMOTE_CHANNELS true +#ifndef ENABLE_REMOTE_PLUGINS +#define ENABLE_REMOTE_PLUGINS false #endif namespace cura::utils diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 8b4d9cf9f4..14fd6cbaf2 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -508,7 +508,7 @@ void ArcusCommunication::sliceNext() } spdlog::debug("Received a Slice message."); - // TODO: Use typemap +#ifdef ENABLE_PLUGINS for (const auto& plugin : slice_message->engine_plugins()) { if (plugin.has_address() && plugin.has_port()) @@ -525,6 +525,7 @@ void ArcusCommunication::sliceNext() } } } +#endif // ENABLE_PLUGINS Slice slice(slice_message->object_lists().size()); Application::getInstance().current_slice = &slice; diff --git a/src/utils/channel.cpp b/src/utils/channel.cpp index c1672cbe7e..1cbcf017b1 100644 --- a/src/utils/channel.cpp +++ b/src/utils/channel.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + #include "utils/channel.h" #include @@ -9,7 +12,7 @@ namespace cura::utils { namespace details { - constexpr bool ALLOW_REMOTE_CHANNELS = (BUILD_ALLOW_REMOTE_CHANNELS); + inline constexpr static bool ALLOW_REMOTE_CHANNELS = ENABLE_REMOTE_PLUGINS; } // namespace details std::shared_ptr createChannel(const ChannelSetupConfiguration& config) @@ -22,7 +25,7 @@ namespace cura::utils spdlog::info("Create local channel on port {}.", config.port); return grpc::InsecureChannelCredentials(); } - else if (details::ALLOW_REMOTE_CHANNELS) + if (details::ALLOW_REMOTE_CHANNELS) { spdlog::info("Create local channel on port {}.", config.port); auto creds_config = grpc::SslCredentialsOptions(); @@ -30,7 +33,7 @@ namespace cura::utils return grpc::SslCredentials(creds_config); } // Create empty credentials, so it'll make a dummy channel where all operations fail. - // This is consitent with creating a channel with the wrong credentials as it where. + // This is consistent with creating a channel with the wrong credentials as it where. spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", config.host, config.port); return std::shared_ptr(); }; From 2be82780a6d32e83e895dd2a58d00d5634a98737 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 08:44:57 +0200 Subject: [PATCH 101/656] Prove that the current 'squared' versions of dist-to-line are worse. And make unit(-ish)-test so that can be carried forward. done as part of CURA-10500 --- tests/utils/LinearAlg2DTest.cpp | 35 ++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/utils/LinearAlg2DTest.cpp b/tests/utils/LinearAlg2DTest.cpp index 7f8f857237..37e4289abd 100644 --- a/tests/utils/LinearAlg2DTest.cpp +++ b/tests/utils/LinearAlg2DTest.cpp @@ -5,6 +5,9 @@ #include #include +#include "utils/SVG.h" +#include "utils/Simplify.h" + // NOLINTBEGIN(*-magic-numbers) namespace cura { @@ -297,5 +300,35 @@ INSTANTIATE_TEST_SUITE_P(RotateAroundInstantiation, RotateAroundParameters(Point(-67, 14), Point(50, 50), 12, Point(-57, -9)) // 12 degrees rotation. Actually ends up at [-57, -9.5]! )); +class Temp {}; + +TEST(Temp, LineDistTests) +{ + std::srand(987); + for (int z = 0; z < 100; ++z) + { + const Point p{ 500000 + (std::rand() % 4000) - 2000, 500000 + (std::rand() % 4000) - 2000 }; + + RAND_MAX; + + const coord_t d = (std::rand() % 2000) - 1000 /2; + const double rang = std::rand() / (static_cast(RAND_MAX) / 6.29); + const Point x{ p.X + static_cast(d * std::cos(rang)), p.Y - static_cast(d * std::sin(rang)) }; + + // Use positive lengths here, so line and line-segment should give the same answers. + coord_t len = std::rand() % 1000; + const Point a{ x.X + static_cast(len * std::sin(rang)), x.Y + static_cast(len * std::cos(rang)) }; + len = std::rand() % 1000; + const Point b{ x.X - static_cast(len * std::sin(rang)), x.Y - static_cast(len * std::cos(rang)) }; + + const coord_t abs_d = std::abs(d); + EXPECT_NEAR(LinearAlg2D::getDistFromLine(p, a, b), abs_d, 5); + EXPECT_NEAR(vSize(LinearAlg2D::getClosestOnLine(p, a, b) - x), 0, 5); + EXPECT_NEAR(vSize(LinearAlg2D::getClosestOnLineSegment(p, a, b) - x), 0, 5); + EXPECT_NEAR(LinearAlg2D::getDist2FromLine(p, a, b), abs_d * abs_d, 25); + EXPECT_NEAR(LinearAlg2D::getDist2FromLineSegment(a, p, b), abs_d * abs_d, 25); + } +} + } // namespace cura -// NOLINTEND(*-magic-numbers) \ No newline at end of file +// NOLINTEND(*-magic-numbers) From 09810e1e7f960cfc62877893be982f5172ac7856 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 09:00:24 +0200 Subject: [PATCH 102/656] The squared dist-to-line should give the same answer as plain. Quick fix for now, as it's not much slower. Also disable the test for the line-segment version, since I'm not sure it should _always_ give the same answer here, and it's not relevant to the simplify problem. part of CURA-10500 --- src/utils/LinearAlg2D.cpp | 41 ++++----------------------------- tests/utils/LinearAlg2DTest.cpp | 2 +- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/src/utils/LinearAlg2D.cpp b/src/utils/LinearAlg2D.cpp index 8ac9ccf34d..4a5c2d1cd3 100644 --- a/src/utils/LinearAlg2D.cpp +++ b/src/utils/LinearAlg2D.cpp @@ -191,43 +191,10 @@ bool LinearAlg2D::lineSegmentsCollide(const Point& a_from_transformed, const Poi coord_t LinearAlg2D::getDist2FromLine(const Point& p, const Point& a, const Point& b) { - constexpr coord_t SQRT_LLONG_MAX_FLOOR = 3037000499; - - // x.......a------------b - // : - // : - // p - // return px_size^2 (if there is no overflow) - const Point vab = b - a; - const Point vap = p - a; - const coord_t ab_size2 = vSize2(vab); - const coord_t ap_size2 = vSize2(vap); - coord_t px_size2; - if(ab_size2 == 0) //Line of 0 length. Assume it's a line perpendicular to the direction to p. - { - return ap_size2; - } - const coord_t dott = dot(vab, vap); - if (dott != 0 && std::abs(dott) > SQRT_LLONG_MAX_FLOOR) - { // dott * dott will overflow so calculate px_size2 via its square root - coord_t px_size = LinearAlg2D::getDistFromLine(p, a, b); - if (px_size <= SQRT_LLONG_MAX_FLOOR) - { - // Due to rounding and conversion errors, this multiplication may not be the exact value that would be - // produced via the dott product, but it should still be close enough - px_size2 = px_size * px_size; - } - else - { - px_size2 = std::numeric_limits::max(); - } - } - else - { - const coord_t ax_size2 = dott * dott / ab_size2; - px_size2 = std::max(coord_t(0), ap_size2 - ax_size2); - } - return px_size2; + // NOTE: The version that tried to do a faster calulation wasn't actually that much faster, and introduced errors. + // Use this for now, should we need this, we can reimplement later. + const auto dist = getDistFromLine(p, a, b); + return dist * dist; } bool LinearAlg2D::isInsideCorner(const Point a, const Point b, const Point c, const Point query_point) diff --git a/tests/utils/LinearAlg2DTest.cpp b/tests/utils/LinearAlg2DTest.cpp index 37e4289abd..2948a59c9e 100644 --- a/tests/utils/LinearAlg2DTest.cpp +++ b/tests/utils/LinearAlg2DTest.cpp @@ -326,7 +326,7 @@ TEST(Temp, LineDistTests) EXPECT_NEAR(vSize(LinearAlg2D::getClosestOnLine(p, a, b) - x), 0, 5); EXPECT_NEAR(vSize(LinearAlg2D::getClosestOnLineSegment(p, a, b) - x), 0, 5); EXPECT_NEAR(LinearAlg2D::getDist2FromLine(p, a, b), abs_d * abs_d, 25); - EXPECT_NEAR(LinearAlg2D::getDist2FromLineSegment(a, p, b), abs_d * abs_d, 25); + //EXPECT_NEAR(LinearAlg2D::getDist2FromLineSegment(a, p, b), abs_d * abs_d, 25); } } From 2fe61bab3e017a58812471e5cfe8390ba70130ac Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 09:17:44 +0200 Subject: [PATCH 103/656] Simplify: Iterate until it doesn't change anymore. Simplify should be idempotent if the polygon already meets the criterea. Simplifying again shouldn't yield new results. The old version could do that, both in the sense that the by_importance vector wasn't completely exhausted, which could happen if if the poly[gon|line] was still larger than [3|2], since not every remove call removed a polygon. But also in the sense that iteration made it remove more in some cases. As these cases are not _that_ common, will only simplify the already simplified polygon, and simplification in and of itself can give speed boosts in the other part of the code (as well as the fact that this can't loop forever, sicne at some point there has to be an iteration where nothing is removed anymore), I think this is acceptable, at least for now. part of CURA-10500 --- include/utils/Simplify.h | 66 ++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index a5a8d95e65..d963deee15 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -160,34 +160,44 @@ class Simplify }; std::priority_queue, std::vector>, decltype(comparator)> by_importance(comparator); - //Add the initial points. - for(size_t i = 0; i < polygon.size(); ++i) - { - const coord_t vertex_importance = importance(polygon, to_delete, i, is_closed); - by_importance.emplace(i, vertex_importance); - } - - //Iteratively remove the least important point until a threshold. Polygonal result = polygon; //Make a copy so that we can also shift vertices. - coord_t vertex_importance = 0; - while(by_importance.size() > min_size) + size_t current_removed = 0; + do { - std::pair vertex = by_importance.top(); - by_importance.pop(); - //The importance may have changed since this vertex was inserted. Re-compute it now. - //If it doesn't change, it's safe to process. - vertex_importance = importance(result, to_delete, vertex.first, is_closed); - if(vertex_importance != vertex.second) + current_removed = 0; + + //Add the initial points. + for (size_t i = 0; i < result.size(); ++i) { - by_importance.emplace(vertex.first, vertex_importance); //Re-insert with updated importance. - continue; + if (to_delete[i]) + { + continue; + } + const coord_t vertex_importance = importance(result, to_delete, i, is_closed); + by_importance.emplace(i, vertex_importance); } - if(vertex_importance <= max_deviation * max_deviation) + //Iteratively remove the least important point until a threshold. + coord_t vertex_importance = 0; + while ((polygon.size() - current_removed) > min_size && ! by_importance.empty()) { - remove(result, to_delete, vertex.first, vertex_importance, is_closed); + std::pair vertex = by_importance.top(); + by_importance.pop(); + //The importance may have changed since this vertex was inserted. Re-compute it now. + //If it doesn't change, it's safe to process. + vertex_importance = importance(result, to_delete, vertex.first, is_closed); + if (vertex_importance != vertex.second) + { + by_importance.emplace(vertex.first, vertex_importance); //Re-insert with updated importance. + continue; + } + + if (vertex_importance <= max_deviation * max_deviation) + { + current_removed += remove(result, to_delete, vertex.first, vertex_importance, is_closed) ? 1 : 0; + } } - } + } while ((polygon.size() - current_removed) > min_size && current_removed > 0); //Now remove the marked vertices in one sweep. Polygonal filtered = createEmpty(polygon); @@ -263,14 +273,14 @@ class Simplify * polyline. */ template - void remove(Polygonal& polygon, std::vector& to_delete, const size_t vertex, const coord_t deviation2, const bool is_closed) const + bool remove(Polygonal& polygon, std::vector& to_delete, const size_t vertex, const coord_t deviation2, const bool is_closed) const { if(deviation2 <= min_resolution * min_resolution) { //At less than the minimum resolution we're always allowed to delete the vertex. //Even if the adjacent line segments are very long. to_delete[vertex] = true; - return; + return true; } const size_t before = previousNotDeleted(vertex, to_delete); @@ -285,7 +295,7 @@ class Simplify { //Removing this vertex does little harm. No long lines will be shifted. to_delete[vertex] = true; - return; + return true; } //Otherwise, one edge next to this vertex is longer than max_resolution. The other is shorter. @@ -296,7 +306,7 @@ class Simplify { if(!is_closed && before == 0) //No edge before the short edge. { - return; //Edge cannot be deleted without shifting a long edge. Don't remove anything. + return false; //Edge cannot be deleted without shifting a long edge. Don't remove anything. } const size_t before_before = previousNotDeleted(before, to_delete); before_from = getPosition(polygon[before_before]); @@ -308,7 +318,7 @@ class Simplify { if(!is_closed && after == polygon.size() - 1) //No edge after the short edge. { - return; //Edge cannot be deleted without shifting a long edge. Don't remove anything. + return false; //Edge cannot be deleted without shifting a long edge. Don't remove anything. } const size_t after_after = nextNotDeleted(after, to_delete); before_from = getPosition(polygon[before]); @@ -320,14 +330,16 @@ class Simplify const bool did_intersect = LinearAlg2D::lineLineIntersection(before_from, before_to, after_from, after_to, intersection); if(!did_intersect) //Lines are parallel. { - return; //Cannot remove edge without shifting a long edge. Don't remove anything. + return false; //Cannot remove edge without shifting a long edge. Don't remove anything. } const coord_t intersection_deviation = LinearAlg2D::getDist2FromLineSegment(before_to, intersection, after_from); if(intersection_deviation <= max_deviation * max_deviation) //Intersection point doesn't deviate too much. Use it! { to_delete[vertex] = true; polygon[length2_before <= length2_after ? before : after] = createIntersection(polygon[before], intersection, polygon[after]); + return true; } + return false; } /*! From 5fa3261e34645deb9d83ea093e745fa3858f7951 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 12:04:10 +0200 Subject: [PATCH 104/656] Always remove poly's that are wholly too small. Both as an early-out and after all is said an done. part oc CURA-10500 --- include/utils/Simplify.h | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index d963deee15..740c2b10b6 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -4,6 +4,7 @@ #ifndef UTILS_SIMPLIFY_H #define UTILS_SIMPLIFY_H +#include "AABB.h" #include "polygon.h" #include "ExtrusionLine.h" #include "linearAlg2D.h" //To calculate line deviations and intersecting lines. @@ -133,6 +134,27 @@ class Simplify */ constexpr static coord_t min_resolution = 5; //5 units, regardless of how big those are, to allow for rounding errors. + template + bool detectSmall(const Polygonal& polygon, const coord_t& min_size) const + { + if (polygon.size() < min_size) //For polygon, 2 or fewer vertices is degenerate. Delete it. For polyline, 1 vertex is degenerate. + { + return true; + } + if (polygon.size() == min_size) + { + const auto a = getPosition(polygon[0]); + const auto b = getPosition(polygon[1]); + const auto c = getPosition(polygon[polygon.size() - 1]); + if (std::max(std::max(vSize2(b - a), vSize2(c - a)), vSize2(c - b)) < min_resolution * min_resolution) + { + // ... unless they are degenetate. + return true; + } + } + return false; + } + /*! * The main simplification algorithm starts here. * \tparam Polygonal A polygonal object, which is a list of vertices. @@ -144,7 +166,7 @@ class Simplify Polygonal simplify(const Polygonal& polygon, const bool is_closed) const { const size_t min_size = is_closed ? 3 : 2; - if(polygon.size() < min_size) //For polygon, 2 or fewer vertices is degenerate. Delete it. For polyline, 1 vertex is degenerate. + if (detectSmall(polygon, min_size)) { return createEmpty(polygon); } @@ -161,8 +183,7 @@ class Simplify std::priority_queue, std::vector>, decltype(comparator)> by_importance(comparator); Polygonal result = polygon; //Make a copy so that we can also shift vertices. - size_t current_removed = 0; - do + for (int64_t current_removed = -1; (polygon.size() - current_removed) > min_size && current_removed != 0;) { current_removed = 0; @@ -197,18 +218,24 @@ class Simplify current_removed += remove(result, to_delete, vertex.first, vertex_importance, is_closed) ? 1 : 0; } } - } while ((polygon.size() - current_removed) > min_size && current_removed > 0); + } //Now remove the marked vertices in one sweep. + AABB aabb; Polygonal filtered = createEmpty(polygon); for(size_t i = 0; i < result.size(); ++i) { if(!to_delete[i]) { appendVertex(filtered, result[i]); + aabb.include(getPosition(result[i])); } } + if (detectSmall(filtered, min_size) || aabb.area() < min_resolution * min_resolution) + { + return createEmpty(filtered); + } return filtered; } From 716357d24e304a9345990a94c9d2b6fc261313df Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 13:49:38 +0200 Subject: [PATCH 105/656] Simplify: Fix code and unit tests to comply with each other. part of CURA-10500 --- include/utils/Simplify.h | 4 +--- tests/utils/LinearAlg2DTest.cpp | 14 +++++++------- tests/utils/SimplifyTest.cpp | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index 740c2b10b6..3f32b2c3f4 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -221,18 +221,16 @@ class Simplify } //Now remove the marked vertices in one sweep. - AABB aabb; Polygonal filtered = createEmpty(polygon); for(size_t i = 0; i < result.size(); ++i) { if(!to_delete[i]) { appendVertex(filtered, result[i]); - aabb.include(getPosition(result[i])); } } - if (detectSmall(filtered, min_size) || aabb.area() < min_resolution * min_resolution) + if (detectSmall(filtered, min_size)) { return createEmpty(filtered); } diff --git a/tests/utils/LinearAlg2DTest.cpp b/tests/utils/LinearAlg2DTest.cpp index 2948a59c9e..d3ad189740 100644 --- a/tests/utils/LinearAlg2DTest.cpp +++ b/tests/utils/LinearAlg2DTest.cpp @@ -309,8 +309,6 @@ TEST(Temp, LineDistTests) { const Point p{ 500000 + (std::rand() % 4000) - 2000, 500000 + (std::rand() % 4000) - 2000 }; - RAND_MAX; - const coord_t d = (std::rand() % 2000) - 1000 /2; const double rang = std::rand() / (static_cast(RAND_MAX) / 6.29); const Point x{ p.X + static_cast(d * std::cos(rang)), p.Y - static_cast(d * std::sin(rang)) }; @@ -322,11 +320,13 @@ TEST(Temp, LineDistTests) const Point b{ x.X - static_cast(len * std::sin(rang)), x.Y - static_cast(len * std::cos(rang)) }; const coord_t abs_d = std::abs(d); - EXPECT_NEAR(LinearAlg2D::getDistFromLine(p, a, b), abs_d, 5); - EXPECT_NEAR(vSize(LinearAlg2D::getClosestOnLine(p, a, b) - x), 0, 5); - EXPECT_NEAR(vSize(LinearAlg2D::getClosestOnLineSegment(p, a, b) - x), 0, 5); - EXPECT_NEAR(LinearAlg2D::getDist2FromLine(p, a, b), abs_d * abs_d, 25); - //EXPECT_NEAR(LinearAlg2D::getDist2FromLineSegment(a, p, b), abs_d * abs_d, 25); + ASSERT_NEAR(LinearAlg2D::getDistFromLine(p, a, b), abs_d, 5); + ASSERT_NEAR(vSize(LinearAlg2D::getClosestOnLine(p, a, b) - x), 0, 5); + ASSERT_NEAR(vSize(LinearAlg2D::getClosestOnLineSegment(p, a, b) - x), 0, 5); + ASSERT_NEAR(std::sqrt(LinearAlg2D::getDist2FromLine(p, a, b)), abs_d, 5); + ASSERT_NEAR(std::sqrt(LinearAlg2D::getDist2FromLineSegment(a, p, b)), abs_d, 5); + + ASSERT_NEAR(std::round(std::sqrt(LinearAlg2D::getDist2FromLine(p, a, b))), LinearAlg2D::getDistFromLine(p, a, b), 5); } } diff --git a/tests/utils/SimplifyTest.cpp b/tests/utils/SimplifyTest.cpp index b65fed0f36..35d6689341 100644 --- a/tests/utils/SimplifyTest.cpp +++ b/tests/utils/SimplifyTest.cpp @@ -231,7 +231,7 @@ TEST_F(SimplifyTest, LimitedError) increasing_zigzag.add(Point(0, increasing_zigzag.size() * y_step)); } - size_t limit_vertex = 2 * simplifier.max_deviation / amplitude_step + 2; // 2 vertices per zag. Deviation/step zags. Add 2 since deviation equal to max is allowed. + size_t limit_vertex = 2 * simplifier.max_deviation / amplitude_step + 3; // 2 vertices per zag. Deviation/step zags. Add 3 since deviation equal to max +- epsilon is allowed. Polygon simplified = simplifier.polyline(increasing_zigzag); @@ -397,7 +397,7 @@ TEST_F(SimplifyTest, ToDegenerate) segment.add(Point(4, 0)); // Less than 5 micron long, so vertices would always be removed. segment = simplifier.polyline(segment); - EXPECT_EQ(segment.size(), 2) << "The segment did not get simplified because that would reduce its vertices to less than 2, making it degenerate."; + EXPECT_EQ(segment.size(), 0) << "The segment got removed entirely, because simplification would reduce its vertices to less than 2, making it degenerate."; } } // namespace cura From 012e0f3e25848eae77c7b6870254ee2d882edea8 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 15:28:07 +0200 Subject: [PATCH 106/656] Actual print-outline is dependant on toolpaths, which are simplified. part of CURA-10500 --- src/WallsComputation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index acc7f20f2b..3b3e8d529e 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -72,7 +72,7 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t part->wall_toolpaths = wall_tool_paths.getToolPaths(); part->inner_area = wall_tool_paths.getInnerContour(); } - part->print_outline = part->outline; + part->print_outline = Simplify(settings).polygon(part->outline); } /* From 928c9dc914a21c55574c4146a04b3158e5573834 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 2 Jun 2023 15:49:42 +0200 Subject: [PATCH 107/656] Set higher min. line-length for bridging caculations. Otherwise this can introduce microsegments, which is bad. With nominal vailues, the current minimum is still usually smaller than the typical nozzle aperature, so a large regression in other areas is not expected. Don't set it to the full max-resolution length though! part of CURA-10500 --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 43def5f5db..5c60180631 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -661,7 +661,7 @@ 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 coord_t min_line_len = settings.get("meshfix_maximum_resolution") / 2; // Shouldn't cut up stuff (too much) below the required simplify resolution. const double acceleration_segment_len = MM2INT(1); // 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; From b767611cb20a5099100eaf5c872da01dddfdcc8d Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Mon, 5 Jun 2023 11:48:14 +0200 Subject: [PATCH 108/656] Remove unused imports CURA-10500 --- include/utils/Simplify.h | 1 - tests/utils/LinearAlg2DTest.cpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index 3f32b2c3f4..e8d54858de 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -4,7 +4,6 @@ #ifndef UTILS_SIMPLIFY_H #define UTILS_SIMPLIFY_H -#include "AABB.h" #include "polygon.h" #include "ExtrusionLine.h" #include "linearAlg2D.h" //To calculate line deviations and intersecting lines. diff --git a/tests/utils/LinearAlg2DTest.cpp b/tests/utils/LinearAlg2DTest.cpp index d3ad189740..4b2e883b8a 100644 --- a/tests/utils/LinearAlg2DTest.cpp +++ b/tests/utils/LinearAlg2DTest.cpp @@ -5,9 +5,6 @@ #include #include -#include "utils/SVG.h" -#include "utils/Simplify.h" - // NOLINTBEGIN(*-magic-numbers) namespace cura { From 25be0dd5aba586b20fd2d5236eeaf731e6907907 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Mon, 5 Jun 2023 11:49:27 +0200 Subject: [PATCH 109/656] Add code documentation CURA-10500 --- include/utils/Simplify.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index e8d54858de..35e52b7edf 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -294,6 +294,7 @@ class Simplify * \param vertex The index of the vertex to remove. * \param deviation2 The previously found deviation for this vertex. * \param is_closed Whether we're working on a closed polygon or an open + \return Whether something is actually removed * polyline. */ template From ca2a7945f798fe7e3874df618376fd227bd10f77 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Mon, 5 Jun 2023 11:56:39 +0200 Subject: [PATCH 110/656] Replace non-standard for loop for while loop CURA-10500 --- include/utils/Simplify.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/Simplify.h b/include/utils/Simplify.h index 35e52b7edf..6cad5fcaf5 100644 --- a/include/utils/Simplify.h +++ b/include/utils/Simplify.h @@ -199,7 +199,7 @@ class Simplify //Iteratively remove the least important point until a threshold. coord_t vertex_importance = 0; - while ((polygon.size() - current_removed) > min_size && ! by_importance.empty()) + while (! by_importance.empty() && (polygon.size() - current_removed) > min_size) { std::pair vertex = by_importance.top(); by_importance.pop(); From 4c897768a5bad4156153a03f7348f481df5bdb64 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 6 Jun 2023 10:12:27 +0200 Subject: [PATCH 111/656] Also simplify outline, not just print-outline. part of CURA-10500 --- src/WallsComputation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index 3b3e8d529e..b5f309e839 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -72,7 +72,8 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t part->wall_toolpaths = wall_tool_paths.getToolPaths(); part->inner_area = wall_tool_paths.getInnerContour(); } - part->print_outline = Simplify(settings).polygon(part->outline); + part->outline = PolygonsPart(Simplify(settings).polygon(part->outline)); + part->print_outline = part->outline; } /* From 0b15442458d98541b735406be782d0cff714609c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 7 Jun 2023 11:24:10 +0200 Subject: [PATCH 112/656] Disable Small Skin Area feature for now :-/ We're not sure why this introduces a boatload of small segments into the infill, and we're out of time to try and fix this before the beta. Re-introducing after the beta seems like a bad idea, so this'll have to wait until 5.5 unfortunately. Done as part of fixing for CURA-10500, but for the original Small Skin Area feature, see CURA-10201. For the ticket to (fix? and) reintroduce the feature, see CURA-10670. --- include/infill.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/infill.h b/include/infill.h index 2f3e4fb287..c6ce9ce4cd 100644 --- a/include/infill.h +++ b/include/infill.h @@ -91,7 +91,7 @@ class Infill , max_resolution(max_resolution) , max_deviation(max_deviation) , wall_line_count(wall_line_count) - , small_area_width(small_area_width) + , small_area_width(0) // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.x , infill_origin(infill_origin) , skip_line_stitching(skip_line_stitching) , fill_gaps(fill_gaps) From e6ba62ede5bc8bbd32ce9756a6a111b753c48cd1 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 7 Jun 2023 17:53:16 +0200 Subject: [PATCH 113/656] =?UTF-8?q?Make=20it=20compile=20on=20=F0=9F=8D=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/WallsComputation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index b5f309e839..09dfa368ed 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -72,7 +72,7 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t part->wall_toolpaths = wall_tool_paths.getToolPaths(); part->inner_area = wall_tool_paths.getInnerContour(); } - part->outline = PolygonsPart(Simplify(settings).polygon(part->outline)); + part->outline = PolygonsPart { Simplify(settings).polygon(part->outline) }; part->print_outline = part->outline; } From 0ebda19fb9ced4f328599bba2b8b570b40b7e7e6 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 12 Jun 2023 10:24:45 +0200 Subject: [PATCH 114/656] Don't apply smart brim order for skirt CURA-10656 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 542d7e3c33..6ba8dddeaf 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1096,7 +1096,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan } } - const auto smart_brim_ordering = train.settings.get("brim_smart_ordering"); + const auto smart_brim_ordering = train.settings.get("brim_smart_ordering") && train.settings.get("adhesion_type") == EPlatformAdhesion::BRIM; std::unordered_multimap order_requirements; for (const std::pair>& p : grid) { From a89c437a9e36369afefb2e7023f72706aca5dedb Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 13 Jun 2023 13:55:59 +0200 Subject: [PATCH 115/656] Also take into account cutting mesh outlines in layer number calculation CURA-10647 --- src/FffGcodeWriter.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 6ba8dddeaf..017bd501b2 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -105,15 +105,16 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep size_t total_layers = 0; for (SliceMeshStorage& mesh : storage.meshes) { - if (mesh.isPrinted()) // No need to process higher layers if the non-printed meshes are higher than the normal meshes. - { - size_t mesh_layer_num = mesh.layers.size(); - for (; mesh_layer_num > 0 && mesh.layers[mesh_layer_num - 1].getOutlines().empty(); --mesh_layer_num); - // No body, calculation of _actual_ number of layers in loop. + size_t mesh_layer_num = mesh.layers.size(); - total_layers = std::max(total_layers, mesh_layer_num); + // calculation of _actual_ number of layers in loop. + while (mesh.layers[mesh_layer_num - 1].getOutlines().empty()) + { + mesh_layer_num --; } + total_layers = std::max(total_layers, mesh_layer_num); + setInfillAndSkinAngles(mesh); } From 59ba3edeeea18cb2d76f57bdd7c93f7646220da8 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 15 Jun 2023 10:44:34 +0200 Subject: [PATCH 116/656] code-style --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 017bd501b2..809c41e018 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -110,7 +110,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep // calculation of _actual_ number of layers in loop. while (mesh.layers[mesh_layer_num - 1].getOutlines().empty()) { - mesh_layer_num --; + mesh_layer_num--; } total_layers = std::max(total_layers, mesh_layer_num); From 070ee6583406deee29acaa0504af2e9564d56d8b Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 20 Jun 2023 11:40:33 +0200 Subject: [PATCH 117/656] Disable support horizontal expansion for tree support As we might want to enable this in the future I disabled the feature for now by setting the value to 0 CURA-10711 --- src/TreeSupportTipGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index ec9adffd1f..7eb6958e34 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -33,7 +33,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage connect_length((config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config.min_radius - 1.0 * config.support_line_width, 0.0)), support_tree_branch_distance((config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length), support_roof_line_distance( use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) : mesh.settings.get("support_roof_line_distance")), //todo propper - support_outset(mesh.settings.get("support_offset")), + support_outset(0), // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") roof_outset(use_fake_roof ? support_outset: mesh.settings.get("support_roof_offset")), force_tip_to_roof((config.min_radius * config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && support_roof_layers && !use_fake_roof), support_tree_limit_branch_reach(mesh.settings.get("support_tree_limit_branch_reach")), From 1022fe09ef59d4d572da60f30fa6225c6a36fefe Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 21 Jun 2023 15:27:50 +0200 Subject: [PATCH 118/656] Fix slicing with custom support blockers fixes #15836 CURA-10727 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 809c41e018..1ae71f4c0f 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -108,7 +108,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep size_t mesh_layer_num = mesh.layers.size(); // calculation of _actual_ number of layers in loop. - while (mesh.layers[mesh_layer_num - 1].getOutlines().empty()) + while (mesh_layer_num > 0 && mesh.layers[mesh_layer_num - 1].getOutlines().empty()) { mesh_layer_num--; } From 0e93b778e7dd1085d01c9682ebadfd11be665c07 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 24 Jun 2023 12:26:05 +0200 Subject: [PATCH 119/656] Smooth operator Add a smooth action, which will smooth small segments (maximum resolution) of a polygon, whom deviate more than a certain angle (wall transition angle) compared to a fluid motion line between the segment before and after. Smoothing is done by moving the first point of the small middle segment to the first point of the previous point with a smooth distance (need to add this to the front end). The second point of the middle segment is moved to the second point of the last segment. If either the previous or next segment is shorter then the smooth distance, then that point in the middle segment is removed Contributes to CURA-10724 Contributes to Ultimaker/Cura#14811 --- include/utils/actions/smooth.h | 149 +++++++++++++++++++++++++++++++++ src/slicer.cpp | 6 ++ 2 files changed, 155 insertions(+) create mode 100644 include/utils/actions/smooth.h diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h new file mode 100644 index 0000000000..af2532e0a2 --- /dev/null +++ b/include/utils/actions/smooth.h @@ -0,0 +1,149 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_VIEWS_SMOOTH_H +#define UTILS_VIEWS_SMOOTH_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cura::actions +{ + +struct smooth_fn +{ + constexpr auto operator()(const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::integral auto allowed_angle_deviation) const + { + return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, smooth_distance, allowed_angle_deviation)); + } + + template + requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> constexpr auto + operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::integral auto allowed_angle_deviation) const + { + const auto size = ranges::distance(rng) - 1; // For closed Path, if open then subtract 0 + if (size < 3) + { + return static_cast(rng); + } + + using point_type = std::remove_cvref_t; + std::set to_remove; + + const auto max_distance_squared = max_resolution * max_resolution; + const auto shift_smooth_distance = smooth_distance * 2; + const auto allowed_angle_deviation_squared = allowed_angle_deviation * allowed_angle_deviation; + + // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens + // when points are filtered out. + auto windows = ranges::views::concat(rng, rng | ranges::views::take(2))| ranges::views::addressof | ranges::views::filter([&](auto point) { return ! to_remove.contains(point); }); + + // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try to shifting those points outwards. + // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. + // TODO: Maybe smooth out depending on the angle between the segments? + // TODO: Maybe create a sharp corner instead of a smooth one, based on minimizing the area to be added or removed? + for (auto* p0 : windows) + { + if (std::next(p0, 2) == &ranges::front(rng)) + { + break; + } + + auto* p1 = std::next(p0); + auto* p2 = std::next(p0, 2); + auto* p3 = std::next(p0, 3); + + const auto distance_squared = std::abs(dotProduct(p1, p2)); + if (distance_squared < max_distance_squared && withinDeviation(p0, p1, p2, p3, allowed_angle_deviation_squared)) + { + const auto p0p1_distance = std::hypot(p1->X - p0->X, p1->Y - p0->Y); + const bool shift_p1 = p0p1_distance > shift_smooth_distance; + if (shift_p1) + { + // shift p1 towards p0 with the smooth distance + // TODO: optimize later by not using angles + const auto p0p1_angle = std::atan2(p1->Y - p0->Y, p1->X - p0->X); + p1->X = p1->X - smooth_distance * std::cos(p0p1_angle); + p1->Y = p1->Y - smooth_distance * std::sin(p0p1_angle); + } + else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed + { + to_remove.insert(p1); + } + const auto p2p3_distance = std::hypot(p3->X - p2->X, p3->Y - p2->Y); + const bool shift_p2 = p2p3_distance > shift_smooth_distance; + if (shift_p2) + { + // shift p2 towards p3 with the smooth distance + const auto p2p3_angle = std::atan2(p3->Y - p2->Y, p3->X - p2->X); + p2->X = p2->X + smooth_distance * std::cos(p2p3_angle); + p2->Y = p2->Y + smooth_distance * std::sin(p2p3_angle); + } + else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed + { + to_remove.insert(p2); + } + } + } + + return static_cast(ranges::actions::remove_if(rng, [&](auto& point) { return to_remove.contains(&point); })); + } + +private: + template + requires std::integral&& std::integral + constexpr auto dotProduct(Vector* p0, Vector* p1) const + { + return p0->X * p1->X + p0->Y * p1->Y; + } + + template + requires std::integral&& std::integral + constexpr auto vectorMagnitudeSquared(Vector* vec) const + { + return dotProduct(vec, vec); + } + + template + requires std::integral&& std::integral + constexpr auto angleBetweenVectorsSquared(Vector* vec0, Vector* vec1) const -> decltype(dotProduct(vec0, vec1)) + { + auto dot = dotProduct(vec0, vec1); + auto vec0_mag = vectorMagnitudeSquared(vec0); + auto vec1_mag = vectorMagnitudeSquared(vec1); + if (vec0_mag == 0 || vec1_mag == 0 || dot == 0) + { + return 0; + } + auto dot_squared = dot * dot; + auto vec_mag = vectorMagnitudeSquared(vec0) * vectorMagnitudeSquared(vec1); + return dot_squared / vec_mag; + } + + template + requires std::integral&& std::integral + constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::integral auto deviation_squared) const + { + Vector ab{ p1->X - p0->X, p1->Y - p0->Y }; + Vector bc{ p2->X - p1->X, p2->Y - p1->Y }; + Vector cd{ p3->X - p2->X, p3->Y - p2->Y }; + + return std::abs(angleBetweenVectorsSquared(&ab, &bc) - angleBetweenVectorsSquared(&ab, &cd)) < deviation_squared; + } +}; + +inline constexpr smooth_fn smooth{}; +} // namespace cura::actions + +#endif // UTILS_VIEWS_SMOOTH_H diff --git a/src/slicer.cpp b/src/slicer.cpp index 889429ceb0..10f791dfbe 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -19,6 +19,7 @@ #include "utils/ThreadPool.h" #include "utils/gettime.h" #include "utils/section_type.h" +#include "utils/actions/smooth.h" namespace cura { @@ -772,6 +773,11 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. polygons = Simplify(mesh->settings).polygon(polygons); + for (auto& poly : polygons) + { + auto smoother = cura::actions::smooth(mesh->settings.get("meshfix_maximum_resolution"), static_cast(mesh->settings.get("meshfix_maximum_resolution") / 4), mesh->settings.get("wall_transition_angle")); + poly = smoother(poly); + } polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments From 9789cf02bd6ad8b408f0fc99f493f46f094116f6 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 24 Jun 2023 15:09:33 +0200 Subject: [PATCH 120/656] reverted premature optimization First make sure that the smoothing works, by not skipping any math steps. Contributes to Ultimaker/Cura#14811 Contributes to CURA-10724 --- include/utils/actions/smooth.h | 39 ++++++++++++++-------------------- src/slicer.cpp | 2 +- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index af2532e0a2..5dbd6af6d0 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -5,6 +5,7 @@ #define UTILS_VIEWS_SMOOTH_H #include +#include #include #include @@ -17,20 +18,21 @@ #include #include #include +#include namespace cura::actions { struct smooth_fn { - constexpr auto operator()(const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::integral auto allowed_angle_deviation) const + constexpr auto operator()(const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { - return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, smooth_distance, allowed_angle_deviation)); + return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, smooth_distance, fluid_angle)); } template requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> constexpr auto - operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::integral auto allowed_angle_deviation) const + operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { const auto size = ranges::distance(rng) - 1; // For closed Path, if open then subtract 0 if (size < 3) @@ -43,7 +45,6 @@ struct smooth_fn const auto max_distance_squared = max_resolution * max_resolution; const auto shift_smooth_distance = smooth_distance * 2; - const auto allowed_angle_deviation_squared = allowed_angle_deviation * allowed_angle_deviation; // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens // when points are filtered out. @@ -65,7 +66,7 @@ struct smooth_fn auto* p3 = std::next(p0, 3); const auto distance_squared = std::abs(dotProduct(p1, p2)); - if (distance_squared < max_distance_squared && withinDeviation(p0, p1, p2, p3, allowed_angle_deviation_squared)) + if (distance_squared < max_distance_squared && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) { const auto p0p1_distance = std::hypot(p1->X - p0->X, p1->Y - p0->Y); const bool shift_p1 = p0p1_distance > shift_smooth_distance; @@ -110,36 +111,28 @@ struct smooth_fn template requires std::integral&& std::integral - constexpr auto vectorMagnitudeSquared(Vector* vec) const - { - return dotProduct(vec, vec); - } - - template - requires std::integral&& std::integral - constexpr auto angleBetweenVectorsSquared(Vector* vec0, Vector* vec1) const -> decltype(dotProduct(vec0, vec1)) + constexpr auto angleBetweenVectors(Vector* vec0, Vector* vec1) const -> decltype(dotProduct(vec0, vec1)) { auto dot = dotProduct(vec0, vec1); - auto vec0_mag = vectorMagnitudeSquared(vec0); - auto vec1_mag = vectorMagnitudeSquared(vec1); - if (vec0_mag == 0 || vec1_mag == 0 || dot == 0) + auto vec0_mag = std::hypot(vec0->X, vec0->Y); + auto vec1_mag = std::hypot(vec1->X, vec1->Y); + if (vec0_mag == 0 || vec1_mag == 0) { - return 0; + return 90.0; } - auto dot_squared = dot * dot; - auto vec_mag = vectorMagnitudeSquared(vec0) * vectorMagnitudeSquared(vec1); - return dot_squared / vec_mag; + auto cos_angle = dot / (vec0_mag * vec1_mag); + auto angle_rad = std::acos(cos_angle); + return angle_rad * 180.0 / std::numbers::pi; } template requires std::integral&& std::integral - constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::integral auto deviation_squared) const + constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::floating_point auto deviation) const { Vector ab{ p1->X - p0->X, p1->Y - p0->Y }; Vector bc{ p2->X - p1->X, p2->Y - p1->Y }; Vector cd{ p3->X - p2->X, p3->Y - p2->Y }; - - return std::abs(angleBetweenVectorsSquared(&ab, &bc) - angleBetweenVectorsSquared(&ab, &cd)) < deviation_squared; + return std::abs(angleBetweenVectors(&ab, &bc) - angleBetweenVectors(&ab, &cd)) < deviation; } }; diff --git a/src/slicer.cpp b/src/slicer.cpp index 10f791dfbe..bfb3453920 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -775,7 +775,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh) polygons = Simplify(mesh->settings).polygon(polygons); for (auto& poly : polygons) { - auto smoother = cura::actions::smooth(mesh->settings.get("meshfix_maximum_resolution"), static_cast(mesh->settings.get("meshfix_maximum_resolution") / 4), mesh->settings.get("wall_transition_angle")); + auto smoother = cura::actions::smooth(mesh->settings.get("meshfix_maximum_resolution"), static_cast(mesh->settings.get("meshfix_maximum_resolution") / 4), mesh->settings.get("wall_transition_angle")); poly = smoother(poly); } From e17eb83d8c7236da33c40b3494dd690d6750cd1f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 24 Jun 2023 15:54:00 +0200 Subject: [PATCH 121/656] Don't use costly angle calculations Shift with vectors instead Contributes to Ultimaker/Cura#14811 Contributes to CURA-10724 --- include/utils/actions/smooth.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 5dbd6af6d0..53ea27383d 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -34,6 +34,10 @@ struct smooth_fn requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { + if (smooth_distance == 0) + { + return static_cast(rng); + } const auto size = ranges::distance(rng) - 1; // For closed Path, if open then subtract 0 if (size < 3) { @@ -68,15 +72,15 @@ struct smooth_fn const auto distance_squared = std::abs(dotProduct(p1, p2)); if (distance_squared < max_distance_squared && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) { + const auto p0p1_distance = std::hypot(p1->X - p0->X, p1->Y - p0->Y); const bool shift_p1 = p0p1_distance > shift_smooth_distance; if (shift_p1) { // shift p1 towards p0 with the smooth distance - // TODO: optimize later by not using angles - const auto p0p1_angle = std::atan2(p1->Y - p0->Y, p1->X - p0->X); - p1->X = p1->X - smooth_distance * std::cos(p0p1_angle); - p1->Y = p1->Y - smooth_distance * std::sin(p0p1_angle); + const auto shift_distance = p0p1_distance * smooth_distance; + p1->X -= (p1->X - p0->X) / shift_distance; + p1->Y -= (p1->Y - p0->Y) / shift_distance; } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -87,9 +91,9 @@ struct smooth_fn if (shift_p2) { // shift p2 towards p3 with the smooth distance - const auto p2p3_angle = std::atan2(p3->Y - p2->Y, p3->X - p2->X); - p2->X = p2->X + smooth_distance * std::cos(p2p3_angle); - p2->Y = p2->Y + smooth_distance * std::sin(p2p3_angle); + auto shift_distance = p2p3_distance * smooth_distance; + p2->X += (p3->X - p2->X) / shift_distance; + p2->Y += (p3->Y - p2->Y) / shift_distance; } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -127,12 +131,12 @@ struct smooth_fn template requires std::integral&& std::integral - constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::floating_point auto deviation) const + constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::floating_point auto fluid_angle) const { Vector ab{ p1->X - p0->X, p1->Y - p0->Y }; Vector bc{ p2->X - p1->X, p2->Y - p1->Y }; Vector cd{ p3->X - p2->X, p3->Y - p2->Y }; - return std::abs(angleBetweenVectors(&ab, &bc) - angleBetweenVectors(&ab, &cd)) < deviation; + return std::abs(angleBetweenVectors(&ab, &bc) - angleBetweenVectors(&ab, &cd)) < fluid_angle; } }; From 4097e9362019e958b2c378eff40083d5d146b908 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 25 Jun 2023 13:24:40 +0200 Subject: [PATCH 122/656] Moved concepts to types and util namespace Bring a bit more order to this [CURA-10724] --- include/utils/{concepts => types}/generic.h | 2 +- include/utils/{concepts => types}/graph.h | 4 ++-- include/utils/views/dfs.h | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) rename include/utils/{concepts => types}/generic.h (94%) rename include/utils/{concepts => types}/graph.h (95%) diff --git a/include/utils/concepts/generic.h b/include/utils/types/generic.h similarity index 94% rename from include/utils/concepts/generic.h rename to include/utils/types/generic.h index 309c5c115f..88e8b8a011 100644 --- a/include/utils/concepts/generic.h +++ b/include/utils/types/generic.h @@ -7,7 +7,7 @@ #include #include -namespace cura +namespace cura::utils { template concept hashable = requires(T value) diff --git a/include/utils/concepts/graph.h b/include/utils/types/graph.h similarity index 95% rename from include/utils/concepts/graph.h rename to include/utils/types/graph.h index 0a5c1e42a6..200a575c4e 100644 --- a/include/utils/concepts/graph.h +++ b/include/utils/types/graph.h @@ -7,9 +7,9 @@ #include #include -#include "utils/concepts/generic.h" +#include "utils/types/generic.h" -namespace cura +namespace cura::utils { /* # nodable diff --git a/include/utils/views/dfs.h b/include/utils/views/dfs.h index c5799ee018..0d509b8004 100644 --- a/include/utils/views/dfs.h +++ b/include/utils/views/dfs.h @@ -7,7 +7,7 @@ #include #include -#include "utils/concepts/graph.h" +#include "utils/types/graph.h" namespace cura::actions { @@ -24,7 +24,7 @@ namespace cura::actions namespace details { -template +template std::function(const Node, const Graph&)> get_neighbours = [](const Node current_node, const Graph& graph) { const auto& [neighbour_begin, neighbour_end] = graph.equal_range(current_node); @@ -38,7 +38,7 @@ std::function(const Node, const Graph&)> get_neighbours = [](c }; }; -template +template constexpr void dfs( const Node& current_node, const Graph& graph, @@ -61,7 +61,7 @@ constexpr void dfs( } } -template +template constexpr void dfs_parent_state(const Node& current_node, const Graph& graph, std::function handle_node) { const std::function parent_view = [handle_node](auto current_node, auto parent_node) @@ -74,7 +74,7 @@ constexpr void dfs_parent_state(const Node& current_node, const Graph& graph, st dfs(current_node, graph, parent_view, visited); } -template +template constexpr void dfs_depth_state(const Node& current_node, const Graph& graph, std::function handle_node) { const std::function depth_view = [handle_node](auto current_node, auto depth) From 42093f799efb7749cb4e41d39984811e57615db7 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 25 Jun 2023 13:27:33 +0200 Subject: [PATCH 123/656] Add some base geometry concepts [CURA-10724] --- include/utils/types/geometry.h | 86 ++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + 2 files changed, 87 insertions(+) create mode 100644 include/utils/types/geometry.h diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h new file mode 100644 index 0000000000..10dae9654e --- /dev/null +++ b/include/utils/types/geometry.h @@ -0,0 +1,86 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_TYPES_GEOMETRY_H +#define UTILS_TYPES_GEOMETRY_H + +#include +#include +#include + +#include +#include + +namespace cura::utils +{ + +template +concept point2d_named = requires(T point) +{ + point.X; + point.Y; +}; + +/*! + * @brief A 2D point, defined either as a named object with X and Y attributes, or as a range of two integral values. + * @details This concept is used to check if a type is a 2D point. A 2D point is a type that has a X and Y member or a type that is a range of integral types with a size of 2. + * @tparam T Type to check + */ +template +concept point2d = point2d_named || (ranges::range && std::integral && std::tuple_size_v == 2); + +template +concept point3d_named = requires(T point) +{ + point.x; + point.y; + point.z; +}; + +/*! + * @brief A 3D point, defined either as a named object with x, y, and z attributes, or as a range of three integral values. + * @details This concept is used to check if a type is a 3D point. A 3D point is a type that has a x, y and z member or a type that is a range of integral types with a size of 3. + * @tparam T Type to check + */ +template +concept point3d = point3d_named || (ranges::range && std::integral && std::tuple_size_v == 3); + +template +concept point_named = point2d_named || point3d_named; + +/*! + * @brief Either a Point2D or a Point3D + * @details This concept is used to check if a type is a point. A point is a type that is a 2D or 3D point. + * @tparam T Type to check + */ +template +concept point = point2d || point3d; + +template +concept point_ranged = point && ! point2d_named && ! point3d_named; + +template +concept clipper_path = ranges::range && point; + +template +concept segment = requires(T segment) +{ + requires point(segment))>; + requires point(segment))>; +}; + +template +concept segment_range = ranges::range && requires(T segment_range) +{ + requires segment; +}; + +template +concept segment_range_range = ranges::range && requires(T rng) +{ + requires segment_range; +}; + +} // namespace cura::utils + +#endif // UTILS_TYPES_GEOMETRY_H \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 357ee011a6..aca5134fc9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,6 +34,7 @@ set(TESTS_SRC_UTILS PolygonTest PolygonUtilsTest SimplifyTest + SmoothTest SparseGridTest StringTest UnionFindTest From 46d414b4cec597b64c3cb8752815ee78b867d908 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 25 Jun 2023 13:35:23 +0200 Subject: [PATCH 124/656] Allow uniform coordinate query with std::get This would allow for getting a x,y,z coordinate for a IntPoint, or 3D point, described as a struct or vector/array/tuple etc. Will be expanded to accommodate Arachne types. ``` struct Point{ int X; int Y; } Point p_struct{ .X = 1, .Y = 2. }; std::array p_array { 1, 2 }; auto x_struct = std::get<"X">(p_struct); auto x_array = std::get<"X">(p_array); ``` [CURA-10724] --- include/utils/types/char_range_literal.h | 25 +++++++++ include/utils/types/get.h | 69 ++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 include/utils/types/char_range_literal.h create mode 100644 include/utils/types/get.h diff --git a/include/utils/types/char_range_literal.h b/include/utils/types/char_range_literal.h new file mode 100644 index 0000000000..3d6e34c2e4 --- /dev/null +++ b/include/utils/types/char_range_literal.h @@ -0,0 +1,25 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_TYPES_CHAR_RANGE_LITERAL_H +#define UTILS_TYPES_CHAR_RANGE_LITERAL_H + +#include + + +namespace cura::utils +{ +template +struct CharRangeLiteral +{ + constexpr CharRangeLiteral(const char (&str)[N]) + { + std::copy_n(str, N, value); + } + + char value[N]; +}; + +} // namespace cura::utils + +#endif // UTILS_TYPES_CHAR_RANGE_LITERAL_H \ No newline at end of file diff --git a/include/utils/types/get.h b/include/utils/types/get.h new file mode 100644 index 0000000000..89a40a6f9c --- /dev/null +++ b/include/utils/types/get.h @@ -0,0 +1,69 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_TYPES_GET_H +#define UTILS_TYPES_GET_H + +#include +#include + +namespace std +{ +template +constexpr auto& get(cura::utils::point2d_named auto& point) noexcept +{ + static_assert(N < 2, "Index out of bounds"); + if constexpr (N == 0) + { + return point.X; + } + return point.Y; +} + +template +constexpr auto& get(cura::utils::point2d auto& point) noexcept +{ + constexpr std::string_view idx = C.value; + static_assert(idx.starts_with("X") || idx.starts_with("x") || idx.starts_with("Y") || idx.starts_with("y"), "Index out of bounds"); + if constexpr (idx.starts_with("X") || idx.starts_with("x")) + { + return std::get<0>(point); + } + return std::get<1>(point); +} + +template +constexpr auto& get(cura::utils::point3d_named auto& point) noexcept +{ + static_assert(N < 3, "Index out of bounds"); + if constexpr (N == 0) + { + return point.x; + } + else if constexpr (N == 1) + { + return point.y; + } + return point.z; +} + +template +constexpr auto& get(cura::utils::point3d auto& point) noexcept +{ + static_assert(C.value == "X" || C.value == "x" || C.value == "Y" || C.value == "y" || C.value == "Z" || C.value == "z", "Index out of bounds"); + constexpr std::string_view idx = C.value; + if constexpr (idx.starts_with("X") || idx.starts_with("x")) + { + return std::get<0>(point); + } + if constexpr (idx.starts_with("Y") || idx.starts_with("y")) + { + return std::get<1>(point); + } + return std::get<2>(point); +} + +} // namespace std + + +#endif // UTILS_TYPES_GET_H \ No newline at end of file From f8341499c051ec83999abdf1a4bc049862119582 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 25 Jun 2023 14:32:32 +0200 Subject: [PATCH 125/656] Use std::get to obtain X,Y This allows for reusing the logic for Arachne types. [CURA-10724] --- include/utils/actions/smooth.h | 48 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 53ea27383d..d7f037f7e2 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -4,8 +4,8 @@ #ifndef UTILS_VIEWS_SMOOTH_H #define UTILS_VIEWS_SMOOTH_H -#include #include +#include #include #include @@ -20,6 +20,9 @@ #include #include +#include "utils/types/get.h" +#include "utils/types/geometry.h" + namespace cura::actions { @@ -31,8 +34,8 @@ struct smooth_fn } template - requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> constexpr auto - operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && utils::point2d> + constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { if (smooth_distance == 0) { @@ -73,27 +76,31 @@ struct smooth_fn if (distance_squared < max_distance_squared && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) { - const auto p0p1_distance = std::hypot(p1->X - p0->X, p1->Y - p0->Y); + const auto p0p1_distance = std::hypot(std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0)); const bool shift_p1 = p0p1_distance > shift_smooth_distance; if (shift_p1) { // shift p1 towards p0 with the smooth distance const auto shift_distance = p0p1_distance * smooth_distance; - p1->X -= (p1->X - p0->X) / shift_distance; - p1->Y -= (p1->Y - p0->Y) / shift_distance; + const auto shift_distance_x = (std::get<"X">(*p1) - std::get<"X">(*p0)) / shift_distance; + const auto shift_distance_y = (std::get<"Y">(*p1) - std::get<"Y">(*p0)) / shift_distance; + p1->X -= shift_distance_x; + p1->Y -= shift_distance_y; } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { to_remove.insert(p1); } - const auto p2p3_distance = std::hypot(p3->X - p2->X, p3->Y - p2->Y); + const auto p2p3_distance = std::hypot(std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2)); const bool shift_p2 = p2p3_distance > shift_smooth_distance; if (shift_p2) { // shift p2 towards p3 with the smooth distance - auto shift_distance = p2p3_distance * smooth_distance; - p2->X += (p3->X - p2->X) / shift_distance; - p2->Y += (p3->Y - p2->Y) / shift_distance; + const auto shift_distance = p2p3_distance * smooth_distance; + const auto shift_distance_x = (std::get<"X">(*p3) - std::get<"X">(*p2)) / shift_distance; + const auto shift_distance_y = (std::get<"Y">(*p3) - std::get<"Y">(*p2)) / shift_distance; + p2->X += shift_distance_x; + p2->Y += shift_distance_y; } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -106,20 +113,18 @@ struct smooth_fn } private: - template - requires std::integral&& std::integral + template constexpr auto dotProduct(Vector* p0, Vector* p1) const { - return p0->X * p1->X + p0->Y * p1->Y; + return std::get<"X">(*p0) * std::get<"X">(*p1) + std::get<"Y">(*p0) * std::get<"Y">(*p1); } - template - requires std::integral&& std::integral + template constexpr auto angleBetweenVectors(Vector* vec0, Vector* vec1) const -> decltype(dotProduct(vec0, vec1)) { auto dot = dotProduct(vec0, vec1); - auto vec0_mag = std::hypot(vec0->X, vec0->Y); - auto vec1_mag = std::hypot(vec1->X, vec1->Y); + auto vec0_mag = std::hypot(std::get<"X">(*vec0), std::get<"Y">(*vec0)); + auto vec1_mag = std::hypot(std::get<"X">(*vec1), std::get<"Y">(*vec1)); if (vec0_mag == 0 || vec1_mag == 0) { return 90.0; @@ -129,13 +134,12 @@ struct smooth_fn return angle_rad * 180.0 / std::numbers::pi; } - template - requires std::integral&& std::integral + template constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::floating_point auto fluid_angle) const { - Vector ab{ p1->X - p0->X, p1->Y - p0->Y }; - Vector bc{ p2->X - p1->X, p2->Y - p1->Y }; - Vector cd{ p3->X - p2->X, p3->Y - p2->Y }; + Vector ab{ std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0) }; + Vector bc{ std::get<"X">(*p2) - std::get<"X">(*p1), std::get<"Y">(*p2) - std::get<"Y">(*p1) }; + Vector cd{ std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2) }; return std::abs(angleBetweenVectors(&ab, &bc) - angleBetweenVectors(&ab, &cd)) < fluid_angle; } }; From 7f2f1cf90ba5e5f1433d45628d24c94204a1ec07 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 26 Jun 2023 14:52:06 +0200 Subject: [PATCH 126/656] Allow smoothing of Arachne lines [CURA-10724] --- include/utils/actions/smooth.h | 69 +++++++++++----- include/utils/types/arachne.h | 144 +++++++++++++++++++++++++++++++++ include/utils/types/get.h | 17 +++- src/WallToolPaths.cpp | 12 +++ 4 files changed, 218 insertions(+), 24 deletions(-) create mode 100644 include/utils/types/arachne.h diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index d7f037f7e2..c1545f4f6b 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -20,27 +20,28 @@ #include #include -#include "utils/types/get.h" +#include "utils/types/arachne.h" #include "utils/types/geometry.h" +#include "utils/types/get.h" namespace cura::actions { struct smooth_fn { - constexpr auto operator()(const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + constexpr auto operator()(const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, smooth_distance, fluid_angle)); } template - requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && utils::point2d> - constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) + constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { if (smooth_distance == 0) - { - return static_cast(rng); - } + { + return static_cast(rng); + } const auto size = ranges::distance(rng) - 1; // For closed Path, if open then subtract 0 if (size < 3) { @@ -55,7 +56,7 @@ struct smooth_fn // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens // when points are filtered out. - auto windows = ranges::views::concat(rng, rng | ranges::views::take(2))| ranges::views::addressof | ranges::views::filter([&](auto point) { return ! to_remove.contains(point); }); + auto windows = ranges::views::concat(rng, rng | ranges::views::take(2)) | ranges::views::addressof | ranges::views::filter([&](auto point) { return ! to_remove.contains(point); }); // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try to shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. @@ -75,17 +76,25 @@ struct smooth_fn const auto distance_squared = std::abs(dotProduct(p1, p2)); if (distance_squared < max_distance_squared && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) { - const auto p0p1_distance = std::hypot(std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0)); const bool shift_p1 = p0p1_distance > shift_smooth_distance; + spdlog::debug("p0p1_distance: {}, shift_p1: {}", p0p1_distance, shift_p1); if (shift_p1) { // shift p1 towards p0 with the smooth distance const auto shift_distance = p0p1_distance * smooth_distance; - const auto shift_distance_x = (std::get<"X">(*p1) - std::get<"X">(*p0)) / shift_distance; - const auto shift_distance_y = (std::get<"Y">(*p1) - std::get<"Y">(*p0)) / shift_distance; - p1->X -= shift_distance_x; - p1->Y -= shift_distance_y; + const auto shift_distance_x = (std::get<"X">(*p1) - std::get<"X">(*p0)) / shift_distance; + const auto shift_distance_y = (std::get<"Y">(*p1) - std::get<"Y">(*p0)) / shift_distance; + if constexpr (utils::junctions) + { + p1->p.X -= shift_distance_x; + p1->p.Y -= shift_distance_y; + } + else + { + p1->X -= shift_distance_x; + p1->Y -= shift_distance_y; + } } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -93,14 +102,23 @@ struct smooth_fn } const auto p2p3_distance = std::hypot(std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2)); const bool shift_p2 = p2p3_distance > shift_smooth_distance; + spdlog::debug("p2p3_distance: {}, shift_p2: {}", p2p3_distance, shift_p2); if (shift_p2) { // shift p2 towards p3 with the smooth distance const auto shift_distance = p2p3_distance * smooth_distance; - const auto shift_distance_x = (std::get<"X">(*p3) - std::get<"X">(*p2)) / shift_distance; - const auto shift_distance_y = (std::get<"Y">(*p3) - std::get<"Y">(*p2)) / shift_distance; - p2->X += shift_distance_x; - p2->Y += shift_distance_y; + const auto shift_distance_x = (std::get<"X">(*p3) - std::get<"X">(*p2)) / shift_distance; + const auto shift_distance_y = (std::get<"Y">(*p3) - std::get<"Y">(*p2)) / shift_distance; + if constexpr (utils::junctions) + { + p2->p.X += shift_distance_x; + p2->p.Y += shift_distance_y; + } + else + { + p2->X += shift_distance_x; + p2->Y += shift_distance_y; + } } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -113,7 +131,8 @@ struct smooth_fn } private: - template + template + requires utils::point2d || utils::junction constexpr auto dotProduct(Vector* p0, Vector* p1) const { return std::get<"X">(*p0) * std::get<"X">(*p1) + std::get<"Y">(*p0) * std::get<"Y">(*p1); @@ -134,12 +153,18 @@ struct smooth_fn return angle_rad * 180.0 / std::numbers::pi; } - template + template + requires utils::point2d || utils::junction constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::floating_point auto fluid_angle) const { - Vector ab{ std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0) }; - Vector bc{ std::get<"X">(*p2) - std::get<"X">(*p1), std::get<"Y">(*p2) - std::get<"Y">(*p1) }; - Vector cd{ std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2) }; + struct Point + { + int64_t X; + int64_t Y; + }; + Point ab{ std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0) }; + Point bc{ std::get<"X">(*p2) - std::get<"X">(*p1), std::get<"Y">(*p2) - std::get<"Y">(*p1) }; + Point cd{ std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2) }; return std::abs(angleBetweenVectors(&ab, &bc) - angleBetweenVectors(&ab, &cd)) < fluid_angle; } }; diff --git a/include/utils/types/arachne.h b/include/utils/types/arachne.h new file mode 100644 index 0000000000..7b46cc6306 --- /dev/null +++ b/include/utils/types/arachne.h @@ -0,0 +1,144 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_TYPES_ARACHNE_H +#define UTILS_TYPES_ARACHNE_H + +#include +#include +#include + +#include + +#include "utils/types/geometry.h" + +namespace cura::utils +{ +template +concept st_storable_data = requires(T val) +{ + val.data; +}; + +/*! + * @brief A node in a skeleton trapezoidal graph, defined as a 2D point with additional stored data. + * @details This concept is used to check if a type is a node in a straight skeleton graph. A node is a type that is a 2D point with additional stored data. + * @tparam T Type to check + */ +template +concept st_node = requires(T val) +{ + requires point2d; +}; + +/*! + * @brief A edge in a skeleton trapezoidal graph, defined as a 2D point with additional stored data. + * @details This concept is used to check if a type is a edge in a skeleton trapezoidal graph. defined as a pair of nodes with pointers to the next, previous, and twin edges, and additional stored data. + * @tparam T Type to check + */ +template +concept st_edge = requires(T val) +{ + requires std::is_pointer_v; + requires st_node; + requires std::is_pointer_v; + requires st_node; + requires std::is_pointer_v; + requires std::is_pointer_v; + requires std::is_pointer_v; +}; + +template +concept st_edges = requires(T edges) +{ + requires ranges::range; + requires st_edge; + requires st_storable_data; +}; + +template +concept st_nodes = requires(T nodes) +{ + requires ranges::range; + requires st_node; + requires st_storable_data; +}; + +/*! + * @brief A skeleton trapezoidal graph, defined as a collection of nodes and edges. + * @details This concept is used to check if a type is a skeleton trapezoidal graph. + * @tparam T Type to check + */ +template +concept st_graph = requires(T graph) +{ + requires st_edges; + requires st_nodes; +}; + +/*! + * @brief A 2D point with an associated weight value + * @details This concept is used to check if a type is a junction as used in wall toolpaths + * @tparam T Type to check + */ +template +concept junction = requires(T val) +{ + requires point2d; + requires std::integral; +}; + +/*! + * @brief A collection of junctions + * @details This concept is used to check if a type is a collection of junctions + * @tparam T Type to check + */ +template +concept junctions = requires(T val) +{ + requires ranges::range; + requires junction; +}; + +/*! + * @brief an Extrusion line in a toolpath + * @details This concept is used to check if a type is a collection of junctions. A series of junctions defining a path for extrusion, + * with additional flags indicating whether the path is closed and whether it corresponds to an odd or even layer. + * @tparam T Type to check + */ +template +concept extrusion_line = requires(T val) +{ + std::is_same_v; + std::is_same_v; + std::is_same_v; + requires junctions; +}; + +/*! + * @brief A collection of extrusion lines + * @details This concept is used to check if a type is a collection of extrusion lines + * @tparam T Type to check + */ +template +concept toolpath = requires(T tp) +{ + requires ranges::range; + requires extrusion_line; +}; + +/*! + * @brief A collection of toolpaths + * @details This concept is used to check if a type is a collection of toolpaths + * @tparam T Type to check + */ +template +concept toolpaths = requires(T tp) +{ + requires ranges::range; + requires toolpath; +}; + +} // namespace cura::utils + +#endif // UTILS_TYPES_ARACHNE_H \ No newline at end of file diff --git a/include/utils/types/get.h b/include/utils/types/get.h index 89a40a6f9c..da4d7e153a 100644 --- a/include/utils/types/get.h +++ b/include/utils/types/get.h @@ -4,8 +4,9 @@ #ifndef UTILS_TYPES_GET_H #define UTILS_TYPES_GET_H -#include -#include +#include "utils/types/arachne.h" +#include "utils/types/char_range_literal.h" +#include "utils/types/geometry.h" namespace std { @@ -63,6 +64,18 @@ constexpr auto& get(cura::utils::point3d auto& point) noexcept return std::get<2>(point); } +template +constexpr auto& get(cura::utils::junction auto& junction) noexcept +{ + return get(junction.p); +} + +template +constexpr auto& get(cura::utils::junction auto& junction) noexcept +{ + return get(junction.p); +} + } // namespace std diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index ab1094915d..27232bd164 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "WallToolPaths.h" @@ -14,6 +15,8 @@ #include "ExtruderTrain.h" #include "utils/PolylineStitcher.h" #include "utils/Simplify.h" +#include "utils/actions/smooth.h" + namespace cura { @@ -255,6 +258,15 @@ void WallToolPaths::simplifyToolPaths(std::vector& toolpaths } } } + + auto smoother = actions::smooth(settings.get("meshfix_maximum_resolution"), static_cast(settings.get("meshfix_maximum_resolution")/4), settings.get("wall_transition_angle")); + for (auto& toolpath : toolpaths) + { + for (auto& line : toolpath | ranges::views::filter([](const auto& l){ return l.is_closed; })) + { + line.junctions = smoother(line.junctions); + } + } } const std::vector& WallToolPaths::getToolPaths() From 726fd09e52d31912cddf08a8e21f38b30ed24c90 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 26 Jun 2023 15:16:53 +0200 Subject: [PATCH 127/656] Fixed failing UT [CURA-10724] --- tests/integration/SlicePhaseTest.cpp | 1 + tests/utils/SmoothTest.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/utils/SmoothTest.cpp diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index c90a96fabe..2717341ba6 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -41,6 +41,7 @@ class SlicePhaseTest : public testing::Test scene.settings.add("meshfix_maximum_resolution", "0.04"); scene.settings.add("meshfix_maximum_deviation", "0.02"); scene.settings.add("meshfix_maximum_extrusion_area_deviation", "2000"); + scene.settings.add("wall_transition_angle", "10"); scene.settings.add("xy_offset", "0"); scene.settings.add("xy_offset_layer_0", "0"); scene.settings.add("hole_xy_offset", "0"); diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp new file mode 100644 index 0000000000..3cb4c68d79 --- /dev/null +++ b/tests/utils/SmoothTest.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#include + +#include +#include + +#include "utils/IntPoint.h" +#include "utils/actions/smooth.h" +#include "utils/polygon.h" + +namespace cura +{ + +TEST(SmoothTest, TestSmooth) +{ + Polygon poly; + poly.poly = std::vector{ cura::Point{ 0, 0 }, { 1000, 1000 }, { 1020, 1010 }, { 3000, 0 }, { 10, 0 }, { 10, 10 }, { 5, 5 } }; + auto smoother = cura::actions::smooth(100, 100, 10.0); + auto smoothed = smoother(poly.poly); + std::vector expected{ cura::Point{ 0, 0 }, { 1000, 1000 }, { 1020, 1010 }, { 3000, 0 }, { 10, 0 } }; + EXPECT_EQ(smoothed, expected); +} +} // namespace cura \ No newline at end of file From a455e3a1f5334f2a1216262cbc026ac5fc472192 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 26 Jun 2023 15:53:46 +0200 Subject: [PATCH 128/656] Shift the points with the correct distance [CURA-10724] --- include/utils/actions/smooth.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index c1545f4f6b..02b2af6a3b 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -82,9 +82,9 @@ struct smooth_fn if (shift_p1) { // shift p1 towards p0 with the smooth distance - const auto shift_distance = p0p1_distance * smooth_distance; - const auto shift_distance_x = (std::get<"X">(*p1) - std::get<"X">(*p0)) / shift_distance; - const auto shift_distance_y = (std::get<"Y">(*p1) - std::get<"Y">(*p0)) / shift_distance; + const auto shift_distance = smooth_distance / p0p1_distance; + const auto shift_distance_x = (std::get<"X">(*p1) - std::get<"X">(*p0)) * shift_distance; + const auto shift_distance_y = (std::get<"Y">(*p1) - std::get<"Y">(*p0)) * shift_distance; if constexpr (utils::junctions) { p1->p.X -= shift_distance_x; @@ -106,9 +106,9 @@ struct smooth_fn if (shift_p2) { // shift p2 towards p3 with the smooth distance - const auto shift_distance = p2p3_distance * smooth_distance; - const auto shift_distance_x = (std::get<"X">(*p3) - std::get<"X">(*p2)) / shift_distance; - const auto shift_distance_y = (std::get<"Y">(*p3) - std::get<"Y">(*p2)) / shift_distance; + const auto shift_distance = smooth_distance / p2p3_distance ; + const auto shift_distance_x = (std::get<"X">(*p3) - std::get<"X">(*p2)) * shift_distance; + const auto shift_distance_y = (std::get<"Y">(*p3) - std::get<"Y">(*p2)) * shift_distance; if constexpr (utils::junctions) { p2->p.X += shift_distance_x; From 34858fa50cc68528c78ea0d16e7c1ed0c602cfe3 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 27 Jun 2023 16:38:36 +0200 Subject: [PATCH 129/656] Refactor smooth action and fix pointer issues Update smooth action to handle changing path size, cycle views, and filtering out removed points. The previous implementation had issues with sliding views due to the path size change. Also, fix range constraints and improve readability. This refactor improves the algorithm's accuracy and performance by better handling the changing path size and filtering out points marked for removal. Additionally, the updated range constraints and readability make it easier to understand and maintain the code. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 02b2af6a3b..72086966a6 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -35,8 +36,9 @@ struct smooth_fn } template - requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) - constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + requires ranges::forward_range && ranges::sized_range +// requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) + auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { if (smooth_distance == 0) { @@ -51,27 +53,29 @@ struct smooth_fn using point_type = std::remove_cvref_t; std::set to_remove; - const auto max_distance_squared = max_resolution * max_resolution; - const auto shift_smooth_distance = smooth_distance * 2; - // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens // when points are filtered out. - auto windows = ranges::views::concat(rng, rng | ranges::views::take(2)) | ranges::views::addressof | ranges::views::filter([&](auto point) { return ! to_remove.contains(point); }); + auto windows = rng | ranges::views::cycle + | ranges::views::addressof + | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try to shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. // TODO: Maybe smooth out depending on the angle between the segments? // TODO: Maybe create a sharp corner instead of a smooth one, based on minimizing the area to be added or removed? - for (auto* p0 : windows) + const auto max_distance_squared = max_resolution * max_resolution; + const auto shift_smooth_distance = smooth_distance * 2; + + for (auto windows_it = ranges::begin(windows); windows_it != ranges::end(windows); ++windows_it) { - if (std::next(p0, 2) == &ranges::front(rng)) + if (*std::next(windows_it, 3) == ranges::front(windows)) { break; } - - auto* p1 = std::next(p0); - auto* p2 = std::next(p0, 2); - auto* p3 = std::next(p0, 3); + auto p0 = *windows_it; + auto p1 = *std::next(windows_it, 1); + auto p2 = *std::next(windows_it, 2); + auto p3 = *std::next(windows_it, 3); const auto distance_squared = std::abs(dotProduct(p1, p2)); if (distance_squared < max_distance_squared && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) @@ -127,7 +131,7 @@ struct smooth_fn } } - return static_cast(ranges::actions::remove_if(rng, [&](auto& point) { return to_remove.contains(&point); })); + return static_cast(ranges::actions::remove_if(rng, [&to_remove](auto point){ return to_remove.contains(&point); })); } private: From 24a795af75d643e283c2a4ba8d9e66158155fbb1 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 28 Jun 2023 08:28:13 +0200 Subject: [PATCH 130/656] Refactor smooth action in utils codebase The `smooth` action codebase has been heavily refactored to improve readability, avoid redundancy and make the codebase more efficient. Importantly, third-party library RangeV3 has been more efficiently used, unnecessary headers were removed, and unnecessary debug logs were deleted. Also, reusable shifting operation for p1 (towards p0) and p2 (towards p3) were extracted to a new function, `shiftPointTowards`. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 95 +++++++++++++++------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 72086966a6..c465ae3893 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -5,21 +5,15 @@ #define UTILS_VIEWS_SMOOTH_H #include -#include +#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include -#include -#include +#include +#include #include "utils/types/arachne.h" #include "utils/types/geometry.h" @@ -36,8 +30,7 @@ struct smooth_fn } template - requires ranges::forward_range && ranges::sized_range -// requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) + requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { if (smooth_distance == 0) @@ -55,7 +48,8 @@ struct smooth_fn // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens // when points are filtered out. - auto windows = rng | ranges::views::cycle + auto windows = rng + | ranges::views::cycle | ranges::views::addressof | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); @@ -82,23 +76,9 @@ struct smooth_fn { const auto p0p1_distance = std::hypot(std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0)); const bool shift_p1 = p0p1_distance > shift_smooth_distance; - spdlog::debug("p0p1_distance: {}, shift_p1: {}", p0p1_distance, shift_p1); if (shift_p1) { - // shift p1 towards p0 with the smooth distance - const auto shift_distance = smooth_distance / p0p1_distance; - const auto shift_distance_x = (std::get<"X">(*p1) - std::get<"X">(*p0)) * shift_distance; - const auto shift_distance_y = (std::get<"Y">(*p1) - std::get<"Y">(*p0)) * shift_distance; - if constexpr (utils::junctions) - { - p1->p.X -= shift_distance_x; - p1->p.Y -= shift_distance_y; - } - else - { - p1->X -= shift_distance_x; - p1->Y -= shift_distance_y; - } + shiftPointTowards(p1, p0, p0p1_distance, smooth_distance); } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -106,23 +86,9 @@ struct smooth_fn } const auto p2p3_distance = std::hypot(std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2)); const bool shift_p2 = p2p3_distance > shift_smooth_distance; - spdlog::debug("p2p3_distance: {}, shift_p2: {}", p2p3_distance, shift_p2); if (shift_p2) { - // shift p2 towards p3 with the smooth distance - const auto shift_distance = smooth_distance / p2p3_distance ; - const auto shift_distance_x = (std::get<"X">(*p3) - std::get<"X">(*p2)) * shift_distance; - const auto shift_distance_y = (std::get<"Y">(*p3) - std::get<"Y">(*p2)) * shift_distance; - if constexpr (utils::junctions) - { - p2->p.X += shift_distance_x; - p2->p.Y += shift_distance_y; - } - else - { - p2->X += shift_distance_x; - p2->Y += shift_distance_y; - } + shiftPointTowards(p2, p3, p2p3_distance, smooth_distance); } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { @@ -135,26 +101,47 @@ struct smooth_fn } private: + template + requires utils::point2d || utils::junction + constexpr void shiftPointTowards(Point* point, Point* target, const std::floating_point auto p0p1_distance, const std::integral auto smooth_distance) const noexcept { + using coord_type = std::remove_cvref_t(*point))>; + const auto shift_distance = smooth_distance / p0p1_distance; + const auto shift_distance_x = static_cast((std::get<"X">(*target) - std::get<"X">(*point)) * shift_distance); + const auto shift_distance_y = static_cast((std::get<"Y">(*target) - std::get<"Y">(*point)) * shift_distance); + if constexpr (utils::point2d) + { + point->X -= shift_distance_x; + point->Y -= shift_distance_y; + } + else + { + point->p.X -= shift_distance_x; + point->p.Y -= shift_distance_y; + } + } + template requires utils::point2d || utils::junction - constexpr auto dotProduct(Vector* p0, Vector* p1) const + constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept { - return std::get<"X">(*p0) * std::get<"X">(*p1) + std::get<"Y">(*p0) * std::get<"Y">(*p1); + return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); } template constexpr auto angleBetweenVectors(Vector* vec0, Vector* vec1) const -> decltype(dotProduct(vec0, vec1)) { - auto dot = dotProduct(vec0, vec1); - auto vec0_mag = std::hypot(std::get<"X">(*vec0), std::get<"Y">(*vec0)); - auto vec1_mag = std::hypot(std::get<"X">(*vec1), std::get<"Y">(*vec1)); + const auto dot = dotProduct(vec0, vec1); + const auto vec0_mag = std::hypot(std::get<"X">(*vec0), std::get<"Y">(*vec0)); + const auto vec1_mag = std::hypot(std::get<"X">(*vec1), std::get<"Y">(*vec1)); if (vec0_mag == 0 || vec1_mag == 0) { - return 90.0; + constexpr auto perpendicular_angle = 90.0; + return perpendicular_angle; } - auto cos_angle = dot / (vec0_mag * vec1_mag); - auto angle_rad = std::acos(cos_angle); - return angle_rad * 180.0 / std::numbers::pi; + const auto cos_angle = dot / (vec0_mag * vec1_mag); + const auto angle_rad = std::acos(cos_angle); + constexpr auto rad_to_degree_factor = 180.0 / std::numbers::pi; + return angle_rad * rad_to_degree_factor; } template From 5b86ae81a23fef7934b20e5b0bc364ef16ed4dcf Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 29 Jun 2023 02:21:50 +0200 Subject: [PATCH 131/656] Optimize smoothing function and fix error in shift_points The smoothing function has been optimized and a bug with the shift_points function has been fixed. The operator in the smoothing function has been made "constexpr" to potentially improve performance. The way "windows" are generated has been restructured to make it clearer and eliminate redundancy. In shift_points, the subtraction used to alter point's coordinates was replaced with addition to rectify a mistake. The distance calculation in the smoothing function has been adjusted from an incorrect dot product into a correct hypotenuse calculation. These changes should help in improving the efficiency and correctness of the smoothing function. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 43 ++++++++++++++++------------------ 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index c465ae3893..de3c52a2b6 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -4,16 +4,17 @@ #ifndef UTILS_VIEWS_SMOOTH_H #define UTILS_VIEWS_SMOOTH_H +#include #include #include #include -#include +#include +#include +#include #include #include -#include -#include -#include +#include #include "utils/types/arachne.h" #include "utils/types/geometry.h" @@ -31,7 +32,7 @@ struct smooth_fn template requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) - auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const { if (smooth_distance == 0) { @@ -48,31 +49,23 @@ struct smooth_fn // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens // when points are filtered out. - auto windows = rng - | ranges::views::cycle - | ranges::views::addressof - | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); + auto tmp = rng; + auto ref_view = ranges::views::concat(tmp, tmp | ranges::views::take(2)) | ranges::views::addressof | ranges::to_vector; + auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try to shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. - // TODO: Maybe smooth out depending on the angle between the segments? - // TODO: Maybe create a sharp corner instead of a smooth one, based on minimizing the area to be added or removed? - const auto max_distance_squared = max_resolution * max_resolution; const auto shift_smooth_distance = smooth_distance * 2; for (auto windows_it = ranges::begin(windows); windows_it != ranges::end(windows); ++windows_it) { - if (*std::next(windows_it, 3) == ranges::front(windows)) - { - break; - } auto p0 = *windows_it; auto p1 = *std::next(windows_it, 1); auto p2 = *std::next(windows_it, 2); auto p3 = *std::next(windows_it, 3); - const auto distance_squared = std::abs(dotProduct(p1, p2)); - if (distance_squared < max_distance_squared && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) + const auto p1p2_distance = std::hypot(std::get<"X">(*p2) - std::get<"X">(*p1), std::get<"Y">(*p2) - std::get<"Y">(*p1)); + if (p1p2_distance < max_resolution && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) { const auto p0p1_distance = std::hypot(std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0)); const bool shift_p1 = p0p1_distance > shift_smooth_distance; @@ -95,9 +88,13 @@ struct smooth_fn to_remove.insert(p2); } } + if (p2 == ranges::front(windows)) + { + break; + } } - return static_cast(ranges::actions::remove_if(rng, [&to_remove](auto point){ return to_remove.contains(&point); })); + return static_cast(ranges::actions::remove_if(tmp, [&to_remove](auto point){ return to_remove.contains(&point); })); } private: @@ -110,13 +107,13 @@ struct smooth_fn const auto shift_distance_y = static_cast((std::get<"Y">(*target) - std::get<"Y">(*point)) * shift_distance); if constexpr (utils::point2d) { - point->X -= shift_distance_x; - point->Y -= shift_distance_y; + point->X += shift_distance_x; + point->Y += shift_distance_y; } else { - point->p.X -= shift_distance_x; - point->p.Y -= shift_distance_y; + point->p.X += shift_distance_x; + point->p.Y += shift_distance_y; } } From 5c36ccef532219663c2b4a29369683c46b98a81b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 29 Jun 2023 02:25:25 +0200 Subject: [PATCH 132/656] Refactor SmoothTest for more reliable results The existing tests for the smooth function were simplified and adjusted. It applies the smoothing function to those points and then verifies that the original polygon remains the same after smoothing. This change will make the test more reliable as it verifies that smoothing algorithm doesn't alter essential data. Contributes to CURA-10724 --- tests/utils/SmoothTest.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index 3cb4c68d79..c11876c168 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -3,9 +3,10 @@ #include -#include #include +#include + #include "utils/IntPoint.h" #include "utils/actions/smooth.h" #include "utils/polygon.h" @@ -16,10 +17,18 @@ namespace cura TEST(SmoothTest, TestSmooth) { Polygon poly; - poly.poly = std::vector{ cura::Point{ 0, 0 }, { 1000, 1000 }, { 1020, 1010 }, { 3000, 0 }, { 10, 0 }, { 10, 10 }, { 5, 5 } }; - auto smoother = cura::actions::smooth(100, 100, 10.0); - auto smoothed = smoother(poly.poly); - std::vector expected{ cura::Point{ 0, 0 }, { 1000, 1000 }, { 1020, 1010 }, { 3000, 0 }, { 10, 0 } }; - EXPECT_EQ(smoothed, expected); + poly.poly = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, + { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; + auto smoother = cura::actions::smooth(750, 100, 5.0); + Polygon smoothed; + smoothed.poly = smoother(poly.poly); + + std::vector expected{ { -137, 188 }, { 1910, 540 }, { 3720, 540 }, { 3949, 651 }, { 5040, 780 }, { 5631, 2705 }, { 5420, 2720 }, { 5500, 2850 }, + { 5486, 3059 }, { 5290, 3450 }, { 1610, 4030 }, { 1144, 3304 }, { 1060, 3210 }, { 1010, 3210 }, { 878, 3258 }, { -740, 3940 } }; + EXPECT_EQ(smoothed.poly, expected); + + auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, + { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; + EXPECT_EQ(poly.poly, original_expected); } } // namespace cura \ No newline at end of file From 19ab2cc1ca303f2c9ace90c3b8630e52e2acc4b3 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 29 Jun 2023 17:40:06 +0200 Subject: [PATCH 133/656] Fix connect top bottom contour CURA-10706 --- src/WallToolPaths.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index ab1094915d..804a66921e 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -8,12 +8,15 @@ #include "WallToolPaths.h" -#include "SkeletalTrapezoidation.h" -#include "utils/SparsePointGrid.h" //To stitch the inner contour. -#include "utils/polygonUtils.h" #include "ExtruderTrain.h" +#include "SkeletalTrapezoidation.h" #include "utils/PolylineStitcher.h" #include "utils/Simplify.h" +#include "utils/SparsePointGrid.h" //To stitch the inner contour. +#include "utils/polygonUtils.h" +#include +#include +#include namespace cura { @@ -245,15 +248,18 @@ void WallToolPaths::simplifyToolPaths(std::vector& toolpaths const Simplify simplifier(settings); for (auto& toolpath : toolpaths) { - for (auto& line : toolpath) - { - line = line.is_closed ? simplifier.polygon(line) : simplifier.polyline(line); - - if (line.is_closed && line.size() >= 2 && line.front() != line.back()) - { - line.emplace_back(line.front()); - } - } + toolpath = toolpath + | ranges::views::transform([&simplifier](auto& line) { + auto line_ = line.is_closed ? simplifier.polygon(line) : simplifier.polyline(line); + + if (line_.is_closed && line_.size() >= 2 && line_.front() != line_.back()) + { + line_.emplace_back(line_.front()); + } + return line_; + }) + | ranges::views::filter([](const auto& line) { return ! line.empty(); }) + | ranges::to_vector; } } From 4a15a2c3fda2d91d57d936a581fdad783c10769c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 09:42:46 +0200 Subject: [PATCH 134/656] Extended geometrical type concepts This commit adds more granular checks for geometrical types (2D & 3D points) in the geometry header. It introduces concepts to inspect whether types are represented as tuple, range, or named fields. Moreover, it checks if an object is of type segment or a range of segments. This refactoring enhances versatility for geometrical types, ensuring greater type safety and readability in the code. Contribute to CURA-10724 --- include/utils/types/geometry.h | 125 +++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 28 deletions(-) diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 10dae9654e..4716de447d 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -1,67 +1,137 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +/*! \file geometry.h + * \brief Defines geometric concepts like Point2D, Point3D, Segment etc. + */ + #ifndef UTILS_TYPES_GEOMETRY_H #define UTILS_TYPES_GEOMETRY_H #include #include +#include #include #include #include +/*! + * \namespace cura::utils + * \brief Namspace for Cura utils + */ namespace cura::utils { +/*! + * \concept point2d_tuple + * \brief Checks whether T is a 2D point represented as a tuple with two integral elements + * \tparam T The type to check + */ +template +concept point2d_tuple = requires(T t) +{ + requires std::is_same_v::type, typename std::tuple_element<0, T>::type>>; + requires std::is_integral_v>; +}; + +/*! + * \concept point2d_ranged + * \brief Checks whether T is a 2D point represented as a range with two integral elements + * \tparam T The type to check + */ template -concept point2d_named = requires(T point) +concept point2d_ranged = ranges::range && std::integral && requires(T point) { - point.X; - point.Y; + requires ranges::size(point) == 2; }; + /*! - * @brief A 2D point, defined either as a named object with X and Y attributes, or as a range of two integral values. - * @details This concept is used to check if a type is a 2D point. A 2D point is a type that has a X and Y member or a type that is a range of integral types with a size of 2. - * @tparam T Type to check + * \concept point2d_named + * \brief Checks whether T is a 2D point represented as an object with X and Y integral fields + * \tparam T The type to check */ template -concept point2d = point2d_named || (ranges::range && std::integral && std::tuple_size_v == 2); +concept point2d_named = requires(T point) +{ + requires std::is_integral_v; + requires std::is_integral_v; +}; +/*! + * \concept point2d + * \brief Checks whether T is a 2D point. A type satisfying any of point2d_tuple, point2d_ranged, or point2d_named + * \tparam T The type to check + */ template -concept point3d_named = requires(T point) +concept point2d = point2d_named || point2d_ranged || point2d_tuple; + +/*! + * \concept point3d_tuple + * \brief Checks whether T is a 3D point represented as a tuple with three integral elements + * \tparam T The type to check + */ +template +concept point3d_tuple = requires(T t) { - point.x; - point.y; - point.z; + requires std::is_same_v::type, typename std::tuple_element<0, T>::type, typename std::tuple_element<0, T>::type>>; + requires std::is_integral_v>; }; /*! - * @brief A 3D point, defined either as a named object with x, y, and z attributes, or as a range of three integral values. - * @details This concept is used to check if a type is a 3D point. A 3D point is a type that has a x, y and z member or a type that is a range of integral types with a size of 3. - * @tparam T Type to check + * \concept point3d_ranged + * \brief Checks whether T is a 3D point represented as a range with three integral elements + * \tparam T The type to check */ template -concept point3d = point3d_named || (ranges::range && std::integral && std::tuple_size_v == 3); +concept point3d_ranged = ranges::range && std::integral && requires(T point) +{ + requires ranges::size(point) == 3; +}; +/*! + * \concept point3d_named + * \brief Checks whether T is a 3D point represented as an object with X, Y and Z integral fields + * \tparam T The type to check + */ template -concept point_named = point2d_named || point3d_named; +concept point3d_named = requires(T point) +{ + requires std::is_integral_v; + requires std::is_integral_v; + requires std::is_integral_v; +}; /*! - * @brief Either a Point2D or a Point3D - * @details This concept is used to check if a type is a point. A point is a type that is a 2D or 3D point. - * @tparam T Type to check + * \concept point3d + * \brief Checks whether T is a 3D point. A type satisfying any of point3d_tuple, point3d_ranged, or point3d_named + * \tparam T The type to check */ template -concept point = point2d || point3d; +concept point3d = point3d_named || point3d_ranged || point3d_tuple; +/*! + * \concept point_named + * \brief Checks whether T is a point represented as an object with named fields + * \tparam T The type to check + */ template -concept point_ranged = point && ! point2d_named && ! point3d_named; +concept point_named = point2d_named || point3d_named; +/*! + * \concept point + * \brief Checks whether T is a 2D or 3D point + * \tparam T The type to check + */ template -concept clipper_path = ranges::range && point; +concept point = point2d || point3d; +/*! + * \concept segment + * \brief Checks whether T represents a segment as a pair of 2D or 3D points. + * \tparam T The type to check + */ template concept segment = requires(T segment) { @@ -69,18 +139,17 @@ concept segment = requires(T segment) requires point(segment))>; }; +/*! + * \concept segment_range + * \brief Checks whether T is a range of valid segments + * \tparam T The type to check + */ template concept segment_range = ranges::range && requires(T segment_range) { requires segment; }; -template -concept segment_range_range = ranges::range && requires(T rng) -{ - requires segment_range; -}; - } // namespace cura::utils #endif // UTILS_TYPES_GEOMETRY_H \ No newline at end of file From db25559363747b4ac40582ba07ca65e962146807 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 09:53:31 +0200 Subject: [PATCH 135/656] Add functionality to access 2D and 3D point coordinates by index or character This commit addresses the need of accessing point coordinates more flexibly. It introduces functions to access the coordinates of 2D and 3D points either by coordinate index (e.g. 0 for X, 1 for Y in a 2D point) or by character (e.g. 'x' or 'X' for X-coordinate). It makes handling points more intuitive and comes in handy when the specific type of point (2D or 3D) is not of interest. Additionally, accessing coordinates of a junction by index or character is also implemented, further enhancing the ease of use. Contributes to CURA-10724 --- include/utils/types/get.h | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/include/utils/types/get.h b/include/utils/types/get.h index da4d7e153a..38c28cbd4e 100644 --- a/include/utils/types/get.h +++ b/include/utils/types/get.h @@ -10,6 +10,14 @@ namespace std { + +/** + * @brief Get a coordinate of a 2d point by index. + * + * @tparam N The index of the coordinate to get (0 for X, 1 for Y). + * @param point The point to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ template constexpr auto& get(cura::utils::point2d_named auto& point) noexcept { @@ -21,6 +29,13 @@ constexpr auto& get(cura::utils::point2d_named auto& point) noexcept return point.Y; } +/** + * @brief Get a coordinate of a 2d point by character. + * + * @tparam C The character of the coordinate to get ('x' or 'X' for X-coordinate, 'y' or 'Y' for Y-coordinate). + * @param point The point to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ template constexpr auto& get(cura::utils::point2d auto& point) noexcept { @@ -33,6 +48,32 @@ constexpr auto& get(cura::utils::point2d auto& point) noexcept return std::get<1>(point); } +/** + * @brief Get a coordinate of a 2d point by character. + * + * @tparam C The character of the coordinate to get ('x' or 'X' for X-coordinate, 'y' or 'Y' for Y-coordinate). + * @param point The point to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ +template +constexpr const auto& get(const cura::utils::point2d auto& point) noexcept +{ + constexpr std::string_view idx = C.value; + static_assert(idx.starts_with("X") || idx.starts_with("x") || idx.starts_with("Y") || idx.starts_with("y"), "Index out of bounds"); + if constexpr (idx.starts_with("X") || idx.starts_with("x")) + { + return std::get<0>(point); + } + return std::get<1>(point); +} + +/** + * @brief Get a coordinate of a 3d point by index. + * + * @tparam N The index of the coordinate to get (0 for X, 1 for Y, 2 for Z). + * @param point The point to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ template constexpr auto& get(cura::utils::point3d_named auto& point) noexcept { @@ -48,6 +89,13 @@ constexpr auto& get(cura::utils::point3d_named auto& point) noexcept return point.z; } +/** + * @brief Get a coordinate of a 3d point by character. + * + * @tparam C The character of the coordinate to get ('x' or 'X' for X-coordinate, 'y' or 'Y' for Y-coordinate, 'z' or 'Z' for Z-coordinate). + * @param point The point to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ template constexpr auto& get(cura::utils::point3d auto& point) noexcept { @@ -64,12 +112,26 @@ constexpr auto& get(cura::utils::point3d auto& point) noexcept return std::get<2>(point); } +/** + * @brief Get a coordinate of a junction by index. + * + * @tparam N The index of the coordinate to get. + * @param junction The junction to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ template constexpr auto& get(cura::utils::junction auto& junction) noexcept { return get(junction.p); } +/** + * @brief Get a coordinate of a junction by character. + * + * @tparam C The character of the coordinate to get. + * @param junction The junction to get the coordinate from. + * @return auto& A reference to the requested coordinate. + */ template constexpr auto& get(cura::utils::junction auto& junction) noexcept { From 6027c543e00bfaa07b41503ce85674b925b9883f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 09:55:38 +0200 Subject: [PATCH 136/656] Update comment syntax in geometry.h In this commit, the syntax for the comments in the geometry.h file has been updated. The "\\" syntax was replaced by "@" as the latter is more standard for Doxygen usage. This change allows for better parsing and generation of documentation by Doxygen, and aligns with common practices in the software industry. Contributes to CURA-10724 --- include/utils/types/geometry.h | 80 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 4716de447d..b7ba00dcaa 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -/*! \file geometry.h - * \brief Defines geometric concepts like Point2D, Point3D, Segment etc. +/*! @file geometry.h + * @brief Defines geometric concepts like Point2D, Point3D, Segment etc. */ #ifndef UTILS_TYPES_GEOMETRY_H @@ -17,16 +17,16 @@ #include /*! - * \namespace cura::utils - * \brief Namspace for Cura utils + * @namespace cura::utils + * @brief Namspace for Cura utils */ namespace cura::utils { /*! - * \concept point2d_tuple - * \brief Checks whether T is a 2D point represented as a tuple with two integral elements - * \tparam T The type to check + * @concept point2d_tuple + * @brief Checks whether T is a 2D point represented as a tuple with two integral elements + * @tparam T The type to check */ template concept point2d_tuple = requires(T t) @@ -36,9 +36,9 @@ concept point2d_tuple = requires(T t) }; /*! - * \concept point2d_ranged - * \brief Checks whether T is a 2D point represented as a range with two integral elements - * \tparam T The type to check + * @concept point2d_ranged + * @brief Checks whether T is a 2D point represented as a range with two integral elements + * @tparam T The type to check */ template concept point2d_ranged = ranges::range && std::integral && requires(T point) @@ -48,9 +48,9 @@ concept point2d_ranged = ranges::range && std::integral concept point2d_named = requires(T point) @@ -60,17 +60,17 @@ concept point2d_named = requires(T point) }; /*! - * \concept point2d - * \brief Checks whether T is a 2D point. A type satisfying any of point2d_tuple, point2d_ranged, or point2d_named - * \tparam T The type to check + * @concept point2d + * @brief Checks whether T is a 2D point. A type satisfying any of point2d_tuple, point2d_ranged, or point2d_named + * @tparam T The type to check */ template concept point2d = point2d_named || point2d_ranged || point2d_tuple; /*! - * \concept point3d_tuple - * \brief Checks whether T is a 3D point represented as a tuple with three integral elements - * \tparam T The type to check + * @concept point3d_tuple + * @brief Checks whether T is a 3D point represented as a tuple with three integral elements + * @tparam T The type to check */ template concept point3d_tuple = requires(T t) @@ -80,9 +80,9 @@ concept point3d_tuple = requires(T t) }; /*! - * \concept point3d_ranged - * \brief Checks whether T is a 3D point represented as a range with three integral elements - * \tparam T The type to check + * @concept point3d_ranged + * @brief Checks whether T is a 3D point represented as a range with three integral elements + * @tparam T The type to check */ template concept point3d_ranged = ranges::range && std::integral && requires(T point) @@ -91,9 +91,9 @@ concept point3d_ranged = ranges::range && std::integral concept point3d_named = requires(T point) @@ -104,33 +104,33 @@ concept point3d_named = requires(T point) }; /*! - * \concept point3d - * \brief Checks whether T is a 3D point. A type satisfying any of point3d_tuple, point3d_ranged, or point3d_named - * \tparam T The type to check + * @concept point3d + * @brief Checks whether T is a 3D point. A type satisfying any of point3d_tuple, point3d_ranged, or point3d_named + * @tparam T The type to check */ template concept point3d = point3d_named || point3d_ranged || point3d_tuple; /*! - * \concept point_named - * \brief Checks whether T is a point represented as an object with named fields - * \tparam T The type to check + * @concept point_named + * @brief Checks whether T is a point represented as an object with named fields + * @tparam T The type to check */ template concept point_named = point2d_named || point3d_named; /*! - * \concept point - * \brief Checks whether T is a 2D or 3D point - * \tparam T The type to check + * @concept point + * @brief Checks whether T is a 2D or 3D point + * @tparam T The type to check */ template concept point = point2d || point3d; /*! - * \concept segment - * \brief Checks whether T represents a segment as a pair of 2D or 3D points. - * \tparam T The type to check + * @concept segment + * @brief Checks whether T represents a segment as a pair of 2D or 3D points. + * @tparam T The type to check */ template concept segment = requires(T segment) @@ -140,9 +140,9 @@ concept segment = requires(T segment) }; /*! - * \concept segment_range - * \brief Checks whether T is a range of valid segments - * \tparam T The type to check + * @concept segment_range + * @brief Checks whether T is a range of valid segments + * @tparam T The type to check */ template concept segment_range = ranges::range && requires(T segment_range) From 4e1c55f5db1d18bdf4854e7dd483c397401608e9 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 09:57:19 +0200 Subject: [PATCH 137/656] Refactor smoothing function, remove unused parameter and improve readability The smoothing function in `smooth.h` was refactored to improve code readability and maintainability. The 'smooth_distance' parameter was removed as it was not being utilized. The core logic of the function remains unchanged, and several variable names were updated to better reflect their context and function. Introduced a clearer, more accurate method to calculate the cosine of the angle deviation which is also generally optimized for smaller values of the `fluid_angle`. This is a more computational efficient approach since for 'Curved' interpolations, the `fluid_angle` is usually small which solves the primary problem. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 122 +++++++++++++++------------------ 1 file changed, 56 insertions(+), 66 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index de3c52a2b6..e156b6133f 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -25,19 +25,15 @@ namespace cura::actions struct smooth_fn { - constexpr auto operator()(const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + constexpr auto operator()(const std::integral auto max_resolution, const std::floating_point auto fluid_angle) const { - return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, smooth_distance, fluid_angle)); + return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, fluid_angle)); } template - requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) - constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::integral auto smooth_distance, const std::floating_point auto fluid_angle) const + requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> + && (utils::point2d> || utils::junctions)constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::floating_point auto fluid_angle) const { - if (smooth_distance == 0) - { - return static_cast(rng); - } const auto size = ranges::distance(rng) - 1; // For closed Path, if open then subtract 0 if (size < 3) { @@ -47,60 +43,75 @@ struct smooth_fn using point_type = std::remove_cvref_t; std::set to_remove; - // Create a range of pointers to the points in the path, using the sliding view doesn't work because of the changing size of the path, which happens - // when points are filtered out. - auto tmp = rng; - auto ref_view = ranges::views::concat(tmp, tmp | ranges::views::take(2)) | ranges::views::addressof | ranges::to_vector; - auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); + auto tmp = rng; // We don't want to shift the points of the ingoing range, therefor we create a temporary copy + auto ref_view = ranges::views::concat(tmp, tmp) | ranges::views::addressof; // Concate twice to make sure we have enough points to shift the window, even after filtering (note: the cylce view doesn't have and end, which makes it harder to determine when to stop) + auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Filter out the points that are marked for removal // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try to shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. - const auto shift_smooth_distance = smooth_distance * 2; - for (auto windows_it = ranges::begin(windows); windows_it != ranges::end(windows); ++windows_it) { - auto p0 = *windows_it; - auto p1 = *std::next(windows_it, 1); - auto p2 = *std::next(windows_it, 2); - auto p3 = *std::next(windows_it, 3); + auto A = *windows_it; + auto B = *std::next(windows_it, 1); + auto C = *std::next(windows_it, 2); + auto D = *std::next(windows_it, 3); - const auto p1p2_distance = std::hypot(std::get<"X">(*p2) - std::get<"X">(*p1), std::get<"Y">(*p2) - std::get<"Y">(*p1)); - if (p1p2_distance < max_resolution && ! withinDeviation(p0, p1, p2, p3, fluid_angle)) + const auto BC_magnitude = std::hypot(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); + if (BC_magnitude < max_resolution && ! withinDeviation(A, B, C, D, fluid_angle)) { - const auto p0p1_distance = std::hypot(std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0)); - const bool shift_p1 = p0p1_distance > shift_smooth_distance; - if (shift_p1) + const auto AB_magnitude = std::hypot(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); + if (AB_magnitude > max_resolution) { - shiftPointTowards(p1, p0, p0p1_distance, smooth_distance); + shiftPointTowards(B, A, AB_magnitude, max_resolution / 2); } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { - to_remove.insert(p1); + to_remove.insert(B); } - const auto p2p3_distance = std::hypot(std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2)); - const bool shift_p2 = p2p3_distance > shift_smooth_distance; - if (shift_p2) + const auto CD_magnitude = std::hypot(std::get<"X">(*D) - std::get<"X">(*C), std::get<"Y">(*D) - std::get<"Y">(*C)); + if (CD_magnitude > max_resolution) { - shiftPointTowards(p2, p3, p2p3_distance, smooth_distance); + shiftPointTowards(C, D, CD_magnitude, max_resolution / 2); } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { - to_remove.insert(p2); + to_remove.insert(C); } } - if (p2 == ranges::front(windows)) + if (B == ranges::front(ref_view) || B == ranges::back(ref_view)) { break; } } - return static_cast(ranges::actions::remove_if(tmp, [&to_remove](auto point){ return to_remove.contains(&point); })); + return static_cast(ranges::actions::remove_if(tmp, [&to_remove](auto point) { return to_remove.contains(&point); })); } private: + template + constexpr double cosAngle(Vector* AB, Vector* BC) const noexcept + { + const auto dot = dotProduct(AB, BC); + const auto AB_mag = std::hypot(std::get<"X">(*AB), std::get<"Y">(*AB)); + const auto BC_mag = std::hypot(std::get<"X">(*BC), std::get<"Y">(*BC)); + if (AB_mag == 0 || BC_mag == 0) + { + return 0.0; + } + return dot / (AB_mag * BC_mag); + } + + template + requires utils::point2d || utils::junction constexpr auto cosAngle(Point* A, Point* B, Point* C) const noexcept + { + auto AB = std::make_tuple(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); + auto BC = std::make_tuple(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); + return cosAngle(&AB, &BC); + } + template - requires utils::point2d || utils::junction - constexpr void shiftPointTowards(Point* point, Point* target, const std::floating_point auto p0p1_distance, const std::integral auto smooth_distance) const noexcept { + requires utils::point2d || utils::junction constexpr void shiftPointTowards(Point* point, Point* target, const std::floating_point auto p0p1_distance, const std::integral auto smooth_distance) const noexcept + { using coord_type = std::remove_cvref_t(*point))>; const auto shift_distance = smooth_distance / p0p1_distance; const auto shift_distance_x = static_cast((std::get<"X">(*target) - std::get<"X">(*point)) * shift_distance); @@ -118,42 +129,21 @@ struct smooth_fn } template - requires utils::point2d || utils::junction - constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept + requires utils::point2d || utils::junction constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept { return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); } - template - constexpr auto angleBetweenVectors(Vector* vec0, Vector* vec1) const -> decltype(dotProduct(vec0, vec1)) - { - const auto dot = dotProduct(vec0, vec1); - const auto vec0_mag = std::hypot(std::get<"X">(*vec0), std::get<"Y">(*vec0)); - const auto vec1_mag = std::hypot(std::get<"X">(*vec1), std::get<"Y">(*vec1)); - if (vec0_mag == 0 || vec1_mag == 0) - { - constexpr auto perpendicular_angle = 90.0; - return perpendicular_angle; - } - const auto cos_angle = dot / (vec0_mag * vec1_mag); - const auto angle_rad = std::acos(cos_angle); - constexpr auto rad_to_degree_factor = 180.0 / std::numbers::pi; - return angle_rad * rad_to_degree_factor; - } - - template - requires utils::point2d || utils::junction - constexpr auto withinDeviation(Vector* p0, Vector* p1, Vector* p2, Vector* p3, const std::floating_point auto fluid_angle) const + template + requires utils::point2d || utils::junction constexpr auto withinDeviation(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle) const { - struct Point - { - int64_t X; - int64_t Y; - }; - Point ab{ std::get<"X">(*p1) - std::get<"X">(*p0), std::get<"Y">(*p1) - std::get<"Y">(*p0) }; - Point bc{ std::get<"X">(*p2) - std::get<"X">(*p1), std::get<"Y">(*p2) - std::get<"Y">(*p1) }; - Point cd{ std::get<"X">(*p3) - std::get<"X">(*p2), std::get<"Y">(*p3) - std::get<"Y">(*p2) }; - return std::abs(angleBetweenVectors(&ab, &bc) - angleBetweenVectors(&ab, &cd)) < fluid_angle; + // This comparison might be slightly off because the cosine function is not linear, especially for larger angles. The range of the cosine function is from -1 to 1 for the + // input range from -pi to pi, so for small angles, the difference in the cosine of the angles can be approximately equal to the difference in the angles + // (measured in radians). In order words, the comparison std::abs(cosAngle(A, B, C) - cosAngle(A, B, D)) < std::cos(fluid_angle) is an approximation that is most accurate + // for small fluid angles. For fluid angles near or exceeding pi/2 radians (90 degrees), the right-hand side of the inequality can even become negative, making the + // condition always false. Since the fluid angle is usually small, this is not a problem in practice. + const auto abs_cos_angle = std::abs(cosAngle(A, B, C) - cosAngle(A, B, D)); + return abs_cos_angle < std::cos(fluid_angle); } }; From 41e260afc374adda485fd64389463a965891f26a Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 09:59:05 +0200 Subject: [PATCH 138/656] Refactor smoother functionality in WallToolPaths Moved the 'smoother' logic from the 'slicer' and 'WallToolPaths' execution to occur earlier in the 'prepared_outline' execution. This change intentional to reduce redundancy and improve the efficiency of our slicing process by ensuring we're smoothing the polygons just once. This could also help reduce the amount of crashes in the Voronoi generation/usage An unnecessary smoother function call with redundant parameters was also removed from src/slicer.cpp." Contributes to CURA-10724 --- src/WallToolPaths.cpp | 15 ++++++--------- src/slicer.cpp | 7 ------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index 27232bd164..cfd29e88c2 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -84,6 +84,12 @@ const std::vector& WallToolPaths::generate() prepared_outline = prepared_outline.unionPolygons(); prepared_outline = Simplify(settings).polygon(prepared_outline); + auto smoother = actions::smooth(settings.get("meshfix_maximum_resolution"), static_cast(settings.get("wall_transition_angle"))); + for (auto& polygon : prepared_outline) + { + polygon = smoother(polygon); + } + if (prepared_outline.area() <= 0) { assert(toolpaths.empty()); @@ -258,15 +264,6 @@ void WallToolPaths::simplifyToolPaths(std::vector& toolpaths } } } - - auto smoother = actions::smooth(settings.get("meshfix_maximum_resolution"), static_cast(settings.get("meshfix_maximum_resolution")/4), settings.get("wall_transition_angle")); - for (auto& toolpath : toolpaths) - { - for (auto& line : toolpath | ranges::views::filter([](const auto& l){ return l.is_closed; })) - { - line.junctions = smoother(line.junctions); - } - } } const std::vector& WallToolPaths::getToolPaths() diff --git a/src/slicer.cpp b/src/slicer.cpp index bfb3453920..b7144169c3 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -19,7 +19,6 @@ #include "utils/ThreadPool.h" #include "utils/gettime.h" #include "utils/section_type.h" -#include "utils/actions/smooth.h" namespace cura { @@ -773,12 +772,6 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. polygons = Simplify(mesh->settings).polygon(polygons); - for (auto& poly : polygons) - { - auto smoother = cura::actions::smooth(mesh->settings.get("meshfix_maximum_resolution"), static_cast(mesh->settings.get("meshfix_maximum_resolution") / 4), mesh->settings.get("wall_transition_angle")); - poly = smoother(poly); - } - polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments // Clean up polylines for Surface Mode printing From 099a63608710dee194ba4bc5352a55f4db26d355 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 10:53:28 +0200 Subject: [PATCH 139/656] Refine path smoothing algorithm Updated the path smoothing algorithm by refining the condition checks and optimizing certain calculations for better efficiency. - Introduced a condition to handle open paths. - Corrections in comments for better understanding. - Loop stop condition refined to prevent unwanted iterations. - Compute function introduced to calculate magnitudes, making the code cleaner. - Reorganized functions for better readability. - Deviation calculation and filtering methods optimized for better performance. This update is aimed at enhancing the performance of path smoothing. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 68 ++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index e156b6133f..3ffec7149b 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -31,35 +31,38 @@ struct smooth_fn } template - requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> - && (utils::point2d> || utils::junctions)constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::floating_point auto fluid_angle) const + requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) + constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::floating_point auto fluid_angle) const { - const auto size = ranges::distance(rng) - 1; // For closed Path, if open then subtract 0 + const auto size = ranges::distance(rng) - 1; // TODO: implement for open paths! The value `-1` is for closed Paths, if open then subtract `0` if (size < 3) { return static_cast(rng); } using point_type = std::remove_cvref_t; - std::set to_remove; + std::set to_remove; // Set of points that are marked for removal auto tmp = rng; // We don't want to shift the points of the ingoing range, therefor we create a temporary copy auto ref_view = ranges::views::concat(tmp, tmp) | ranges::views::addressof; // Concate twice to make sure we have enough points to shift the window, even after filtering (note: the cylce view doesn't have and end, which makes it harder to determine when to stop) auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Filter out the points that are marked for removal - // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try to shifting those points outwards. + // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. for (auto windows_it = ranges::begin(windows); windows_it != ranges::end(windows); ++windows_it) { auto A = *windows_it; auto B = *std::next(windows_it, 1); + if (B == ranges::front(windows) || B == ranges::back(windows)) + { + break; + } auto C = *std::next(windows_it, 2); auto D = *std::next(windows_it, 3); - const auto BC_magnitude = std::hypot(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); - if (BC_magnitude < max_resolution && ! withinDeviation(A, B, C, D, fluid_angle)) + const auto [AB_magnitude, BC_magnitude, CD_magnitude] = computeMagnitudes(A, B, C, D); + if (! isWithinDeviations(A, B, C, D, fluid_angle, max_resolution, AB_magnitude, BC_magnitude, CD_magnitude)) { - const auto AB_magnitude = std::hypot(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); if (AB_magnitude > max_resolution) { shiftPointTowards(B, A, AB_magnitude, max_resolution / 2); @@ -68,7 +71,6 @@ struct smooth_fn { to_remove.insert(B); } - const auto CD_magnitude = std::hypot(std::get<"X">(*D) - std::get<"X">(*C), std::get<"Y">(*D) - std::get<"Y">(*C)); if (CD_magnitude > max_resolution) { shiftPointTowards(C, D, CD_magnitude, max_resolution / 2); @@ -78,39 +80,41 @@ struct smooth_fn to_remove.insert(C); } } - if (B == ranges::front(ref_view) || B == ranges::back(ref_view)) - { - break; - } } return static_cast(ranges::actions::remove_if(tmp, [&to_remove](auto point) { return to_remove.contains(&point); })); } private: - template - constexpr double cosAngle(Vector* AB, Vector* BC) const noexcept + template + requires utils::point2d || utils::junction + constexpr auto computeMagnitudes(Point* A, Point* B, Point* C, Point* D) const noexcept { - const auto dot = dotProduct(AB, BC); - const auto AB_mag = std::hypot(std::get<"X">(*AB), std::get<"Y">(*AB)); - const auto BC_mag = std::hypot(std::get<"X">(*BC), std::get<"Y">(*BC)); - if (AB_mag == 0 || BC_mag == 0) - { - return 0.0; - } - return dot / (AB_mag * BC_mag); + const auto AB_magnitude = std::hypot(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); + const auto BC_magnitude = std::hypot(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); + const auto CD_magnitude = std::hypot(std::get<"X">(*D) - std::get<"X">(*C), std::get<"Y">(*D) - std::get<"Y">(*C)); + + return std::make_tuple(AB_magnitude, BC_magnitude, CD_magnitude); } template - requires utils::point2d || utils::junction constexpr auto cosAngle(Point* A, Point* B, Point* C) const noexcept + requires utils::point2d || utils::junction + constexpr auto cosAngle(Point* A, Point* B, Point* C, const std::floating_point auto AB_magnitude, const std::floating_point auto BC_magnitude) const noexcept { + if (AB_magnitude == 0.0 || BC_magnitude == 0.0) + { + return 0.0; + } auto AB = std::make_tuple(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); auto BC = std::make_tuple(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); - return cosAngle(&AB, &BC); + + const auto dot = dotProduct(&AB, &BC); + return dot / (AB_magnitude * BC_magnitude); } template - requires utils::point2d || utils::junction constexpr void shiftPointTowards(Point* point, Point* target, const std::floating_point auto p0p1_distance, const std::integral auto smooth_distance) const noexcept + requires utils::point2d || utils::junction + constexpr void shiftPointTowards(Point* point, Point* target, const std::floating_point auto p0p1_distance, const std::integral auto smooth_distance) const noexcept { using coord_type = std::remove_cvref_t(*point))>; const auto shift_distance = smooth_distance / p0p1_distance; @@ -135,14 +139,22 @@ struct smooth_fn } template - requires utils::point2d || utils::junction constexpr auto withinDeviation(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle) const + requires utils::point2d || utils::junction + constexpr auto isWithinDeviations(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle, const std::integral auto max_resolution, + const std::floating_point auto AB_magnitude, const std::floating_point auto BC_magnitude, const std::floating_point auto CD_magnitude) const noexcept { + if (BC_magnitude > max_resolution) + { + return true; + } + const double cos_A = cosAngle(A, B, C, AB_magnitude, BC_magnitude); + const double cos_B = cosAngle(A, B, D, AB_magnitude, CD_magnitude); // This comparison might be slightly off because the cosine function is not linear, especially for larger angles. The range of the cosine function is from -1 to 1 for the // input range from -pi to pi, so for small angles, the difference in the cosine of the angles can be approximately equal to the difference in the angles // (measured in radians). In order words, the comparison std::abs(cosAngle(A, B, C) - cosAngle(A, B, D)) < std::cos(fluid_angle) is an approximation that is most accurate // for small fluid angles. For fluid angles near or exceeding pi/2 radians (90 degrees), the right-hand side of the inequality can even become negative, making the // condition always false. Since the fluid angle is usually small, this is not a problem in practice. - const auto abs_cos_angle = std::abs(cosAngle(A, B, C) - cosAngle(A, B, D)); + const auto abs_cos_angle = std::abs(cos_A - cos_B); return abs_cos_angle < std::cos(fluid_angle); } }; From 5b6bdc75431fe911b1c44c165f852a8c867308b9 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 12:21:44 +0200 Subject: [PATCH 140/656] Update smoothing parameters in test Updated the parameters in the SmoothTest of CURA utility tests. The third parameter of the smoothing function has been removed due to recent changes in the CURA engine algorithm. Consequently, the expected results of test were also updated to match the output from the updated smoothing function. Contributes to CURA-10724 --- tests/utils/SmoothTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index c11876c168..8f0c08b755 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -19,12 +19,12 @@ TEST(SmoothTest, TestSmooth) Polygon poly; poly.poly = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; - auto smoother = cura::actions::smooth(750, 100, 5.0); + auto smoother = cura::actions::smooth(750, 5.0); Polygon smoothed; smoothed.poly = smoother(poly.poly); - std::vector expected{ { -137, 188 }, { 1910, 540 }, { 3720, 540 }, { 3949, 651 }, { 5040, 780 }, { 5631, 2705 }, { 5420, 2720 }, { 5500, 2850 }, - { 5486, 3059 }, { 5290, 3450 }, { 1610, 4030 }, { 1144, 3304 }, { 1060, 3210 }, { 1010, 3210 }, { 878, 3258 }, { -740, 3940 } }; + std::vector expected{ { -137, 188 }, { 1910, 540 }, { 3445, 540 }, { 4222, 683 }, { 5040, 780 }, { 5550, 2442 }, { 5420, 2720 }, { 5500, 2850 }, + { 5530, 2970 }, { 4920, 3508 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; EXPECT_EQ(smoothed.poly, expected); auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, From f8544c10e373caa4dce16d99b7fe908be2f0b151 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 12:35:24 +0200 Subject: [PATCH 141/656] Add missing import Contributes to CURA-10724 --- include/utils/actions/smooth.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 3ffec7149b..808d3b38e6 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include From 925c8ff40fa9080874bdf4a1fbaf0615cccb5f18 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 13:10:55 +0200 Subject: [PATCH 142/656] fix ranged 2d and 3d point concepts on Apple Clang 13 I hate Apple Clang! Contribute to CURA-10724 --- include/utils/types/geometry.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index b7ba00dcaa..08c0fc3811 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -41,9 +41,10 @@ concept point2d_tuple = requires(T t) * @tparam T The type to check */ template -concept point2d_ranged = ranges::range && std::integral && requires(T point) +concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; + requires std::integral>; }; @@ -85,9 +86,10 @@ concept point3d_tuple = requires(T t) * @tparam T The type to check */ template -concept point3d_ranged = ranges::range && std::integral && requires(T point) +concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; + requires std::integral>; }; /*! From eda05c1f7e95b4c4474bcd9b8633749e8a81167c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 14:03:35 +0200 Subject: [PATCH 143/656] Update Geometry checks for compiler support of std::integral Due to varying support for the C++20 feature std::integral, preprocessor checks were added to the Geometry utility. This ensures that the Geometry utility functions will compile and run as expected across different compilers, especially older ones or those that don't fully support C++20 yet. Additionally, if std::integral is not supported, standard member accessing is used instead. This modification enhances the code's robustness and compatibility. Contributes to CURA-10724 --- include/utils/types/geometry.h | 35 ++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 08c0fc3811..0364ef0558 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -32,7 +32,9 @@ template concept point2d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type>>; - requires std::is_integral_v>; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) + requires std::integral>; +#endif }; /*! @@ -44,7 +46,9 @@ template concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) requires std::integral>; +#endif }; @@ -56,8 +60,13 @@ concept point2d_ranged = ranges::range && requires(T point) template concept point2d_named = requires(T point) { - requires std::is_integral_v; - requires std::is_integral_v; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) + requires std::integral; + requires std::integral; +#else + point.X; + point.Y; +#endif }; /*! @@ -77,7 +86,9 @@ template concept point3d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type, typename std::tuple_element<0, T>::type>>; - requires std::is_integral_v>; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) + requires std::integral>; +#endif }; /*! @@ -89,20 +100,28 @@ template concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) requires std::integral>; +#endif }; /*! * @concept point3d_named * @brief Checks whether T is a 3D point represented as an object with X, Y and Z integral fields - * @tparam T The type to check + * @tparam T The type to check https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error */ template concept point3d_named = requires(T point) { - requires std::is_integral_v; - requires std::is_integral_v; - requires std::is_integral_v; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) + requires std::integral; + requires std::integral; + requires std::integral; +#else + point.x; + point.y; + point.z; +#endif }; /*! From 5cd147b83ca2a1e3c0276bc80b0a449efd4ca7ef Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 14:15:54 +0200 Subject: [PATCH 144/656] Add note with stack-overflow comment Such that future developers are aware of struggles we had to support Apple Clang. _The pain is real... never give up... word!_ Contributes to CURA-10724 --- include/utils/types/geometry.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 0364ef0558..639c7a6873 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -33,6 +33,7 @@ concept point2d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type>>; #if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; #endif }; @@ -47,6 +48,7 @@ concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; #if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; #endif }; @@ -61,6 +63,7 @@ template concept point2d_named = requires(T point) { #if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral; requires std::integral; #else @@ -87,6 +90,7 @@ concept point3d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type, typename std::tuple_element<0, T>::type>>; #if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; #endif }; @@ -101,6 +105,7 @@ concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; #if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; #endif }; @@ -108,12 +113,13 @@ concept point3d_ranged = ranges::range && requires(T point) /*! * @concept point3d_named * @brief Checks whether T is a 3D point represented as an object with X, Y and Z integral fields - * @tparam T The type to check https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error + * @tparam T The type to check */ template concept point3d_named = requires(T point) { #if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral; requires std::integral; requires std::integral; From 414f9c6a11c2ce9e826fdeceb189c5536f8a2e98 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 14:30:47 +0200 Subject: [PATCH 145/656] Adjust Arachne type checks for different C++ versions This commit addresses potential compatibility issues with different versions of C++ by adding conditionals to the `junction` concept in the `arachne.h` utility file. As some versions may not support the `std::integral` check, we fall back to directly assessing `val.w` if the C++ version is older or a particular library version with compatibility issues is detected. This change ensures successful compilation across varying environments. Contributes to CURA-10724 --- include/utils/types/arachne.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/utils/types/arachne.h b/include/utils/types/arachne.h index 7b46cc6306..69ac635f2a 100644 --- a/include/utils/types/arachne.h +++ b/include/utils/types/arachne.h @@ -85,7 +85,12 @@ template concept junction = requires(T val) { requires point2d; +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) + // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral; +#else + val.w; +#endif }; /*! From 973c6532b8e48dd8bd61dfee0eaccf0a35101972 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 14:51:30 +0200 Subject: [PATCH 146/656] Refactor std::integral usage for C++ compatibility This commit removes the preprocessor conditions that were originally put in to cope with an issue in Clang13 C++20. A fallback version of the integral concept has been added in the std namespace if the C++ version is below 201703L or if the library version is lesser than the Clang13 version. This allows for a cleaner and more compatible codebase across various C++ versions. In addition, "include/utils/types/generic.h" is now being included in "include/utils/types/geometry.h", "include/utils/types/arachne.h" and "include/utils/actions/smooth.h" everywhere std::integral might be used. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 1 + include/utils/types/arachne.h | 6 +----- include/utils/types/generic.h | 12 ++++++++++++ include/utils/types/geometry.h | 28 +++------------------------- 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 808d3b38e6..b1ee1403d0 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -17,6 +17,7 @@ #include #include +#include "utils/types/generic.h" #include "utils/types/arachne.h" #include "utils/types/geometry.h" #include "utils/types/get.h" diff --git a/include/utils/types/arachne.h b/include/utils/types/arachne.h index 69ac635f2a..8f05d6d5d6 100644 --- a/include/utils/types/arachne.h +++ b/include/utils/types/arachne.h @@ -11,6 +11,7 @@ #include #include "utils/types/geometry.h" +#include "utils/types/generic.h" namespace cura::utils { @@ -85,12 +86,7 @@ template concept junction = requires(T val) { requires point2d; -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) - // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral; -#else - val.w; -#endif }; /*! diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 88e8b8a011..acf06549ed 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -6,6 +6,7 @@ #include #include +#include namespace cura::utils { @@ -14,6 +15,17 @@ concept hashable = requires(T value) { { std::hash{}(value) } -> concepts::convertible_to; }; + } // namespace cura +namespace std +{ +#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) +// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error +#else +template +concept integral = std::is_integral_v<_Tp>; +#endif +} // namespace std + #endif // CURAENGINE_GENERIC_H diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 639c7a6873..9a5ebb6444 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -16,6 +16,8 @@ #include #include +#include "utils/types/generic.h" + /*! * @namespace cura::utils * @brief Namspace for Cura utils @@ -32,10 +34,7 @@ template concept point2d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type>>; -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) -// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; -#endif }; /*! @@ -47,10 +46,7 @@ template concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) -// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; -#endif }; @@ -62,14 +58,8 @@ concept point2d_ranged = ranges::range && requires(T point) template concept point2d_named = requires(T point) { -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) -// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral; requires std::integral; -#else - point.X; - point.Y; -#endif }; /*! @@ -89,10 +79,7 @@ template concept point3d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type, typename std::tuple_element<0, T>::type>>; -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) -// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; -#endif }; /*! @@ -104,10 +91,8 @@ template concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) -// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral>; -#endif + }; /*! @@ -118,16 +103,9 @@ concept point3d_ranged = ranges::range && requires(T point) template concept point3d_named = requires(T point) { -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) -// https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error requires std::integral; requires std::integral; requires std::integral; -#else - point.x; - point.y; - point.z; -#endif }; /*! From 1694fdcf4a0884c545b54b13443bcc5fea7aa394 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 15:01:00 +0200 Subject: [PATCH 147/656] Fix floating_point concept for Apple Clang <= 13 CURA-10724 --- include/utils/actions/smooth.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index b1ee1403d0..ed8ea8aaa3 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -11,14 +11,15 @@ #include #include #include +#include #include #include #include #include #include -#include "utils/types/generic.h" #include "utils/types/arachne.h" +#include "utils/types/generic.h" #include "utils/types/geometry.h" #include "utils/types/get.h" @@ -55,7 +56,7 @@ struct smooth_fn { auto A = *windows_it; auto B = *std::next(windows_it, 1); - if (B == ranges::front(windows) || B == ranges::back(windows)) + if (B == ranges::front(windows) || B == ranges::back(windows) || ranges::distance(windows_it, ranges::end(windows)) < 3) { break; } From ab6d68c6103dd23bfc4fda19da815e85c0dc8c66 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 1 Jul 2023 15:17:47 +0200 Subject: [PATCH 148/656] Improved detection of Apple Clang <= 13 CURA-10724 --- CMakeLists.txt | 2 ++ conanfile.py | 1 + include/utils/types/generic.h | 8 +++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dda23ccdd..71263c096b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ option(ENABLE_TESTING "Build with unit tests" OFF) option(EXTENSIVE_WARNINGS "Build with all warnings" ON) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) +option(RETARDED_APPLE_CLANG "Apple Clang <= 13 used" OFF) # Create Protobuf files if Arcus is used if (ENABLE_ARCUS) @@ -149,6 +150,7 @@ target_include_directories(_CuraEngine target_compile_definitions(_CuraEngine PUBLIC $<$:ARCUS> + $<$:RETARDED_APPLE_CLANG> CURA_ENGINE_VERSION=\"${CURA_ENGINE_VERSION}\" $<$:BUILD_TESTS> PRIVATE diff --git a/conanfile.py b/conanfile.py index db8f50331e..854a6afe61 100644 --- a/conanfile.py +++ b/conanfile.py @@ -93,6 +93,7 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings + tc.variables["RETARDED_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) <= Version("12") tc.generate() def layout(self): diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index acf06549ed..a639e54931 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -18,14 +18,16 @@ concept hashable = requires(T value) } // namespace cura +#ifdef RETARDED_APPLE_CLANG namespace std { -#if (__cplusplus > 201703L) && (!defined(_LIBCPP_VERSION) || (__clang_major__ > 13)) // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error -#else template concept integral = std::is_integral_v<_Tp>; -#endif + +template +concept floating_point = is_floating_point_v<_Tp>; } // namespace std +#endif #endif // CURAENGINE_GENERIC_H From f9a35382b0e4ec9dbef6e6fcd81fc58e268cbb62 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 11:23:26 +0200 Subject: [PATCH 149/656] Refactor angle checking in smoothing utility The angle checking in the smoothing utility now check against actual angles instead of the cosine angle. This is slightly more computational heavy but needed since otherwise a lot of points where missed. The function `isWithinDeviations` has also been renamed to `isWithinAllowedDeviations` for clarity. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index ed8ea8aaa3..1ad87295f9 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -64,7 +64,7 @@ struct smooth_fn auto D = *std::next(windows_it, 3); const auto [AB_magnitude, BC_magnitude, CD_magnitude] = computeMagnitudes(A, B, C, D); - if (! isWithinDeviations(A, B, C, D, fluid_angle, max_resolution, AB_magnitude, BC_magnitude, CD_magnitude)) + if (! isWithinAllowedDeviations(A, B, C, D, fluid_angle, max_resolution, AB_magnitude, BC_magnitude, CD_magnitude)) { if (AB_magnitude > max_resolution) { @@ -143,22 +143,17 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr auto isWithinDeviations(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle, const std::integral auto max_resolution, + constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle, const std::integral auto max_resolution, const std::floating_point auto AB_magnitude, const std::floating_point auto BC_magnitude, const std::floating_point auto CD_magnitude) const noexcept { if (BC_magnitude > max_resolution) { return true; } - const double cos_A = cosAngle(A, B, C, AB_magnitude, BC_magnitude); - const double cos_B = cosAngle(A, B, D, AB_magnitude, CD_magnitude); - // This comparison might be slightly off because the cosine function is not linear, especially for larger angles. The range of the cosine function is from -1 to 1 for the - // input range from -pi to pi, so for small angles, the difference in the cosine of the angles can be approximately equal to the difference in the angles - // (measured in radians). In order words, the comparison std::abs(cosAngle(A, B, C) - cosAngle(A, B, D)) < std::cos(fluid_angle) is an approximation that is most accurate - // for small fluid angles. For fluid angles near or exceeding pi/2 radians (90 degrees), the right-hand side of the inequality can even become negative, making the - // condition always false. Since the fluid angle is usually small, this is not a problem in practice. - const auto abs_cos_angle = std::abs(cos_A - cos_B); - return abs_cos_angle < std::cos(fluid_angle); + const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); + const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); + const auto abs_angle = std::abs(cos_A - cos_B); + return abs_angle < fluid_angle; } }; From f694ca4bf4e7df509ea4ed63941d219c382de8bd Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 13:09:26 +0200 Subject: [PATCH 150/656] Start the window at the tail Often the first segments weren't filtered, by concatenating the tail to the front we also take these last elements into account. Also, adjusted the conditions in if-statements for better accuracy and introduced variables for allowed deviations and smoothing distance for better code consistency. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 1ad87295f9..1e534a2c2a 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "utils/types/arachne.h" #include "utils/types/generic.h" @@ -44,39 +45,38 @@ struct smooth_fn } using point_type = std::remove_cvref_t; + using coord_type = std::remove_cvref_t(*ranges::begin(rng)))>; std::set to_remove; // Set of points that are marked for removal + const auto allowed_deviation = static_cast(max_resolution * 2 / 3); // The allowed deviation from the original path + const auto smooth_distance = static_cast(max_resolution / 2); // The distance over which the path is smoothed auto tmp = rng; // We don't want to shift the points of the ingoing range, therefor we create a temporary copy - auto ref_view = ranges::views::concat(tmp, tmp) | ranges::views::addressof; // Concate twice to make sure we have enough points to shift the window, even after filtering (note: the cylce view doesn't have and end, which makes it harder to determine when to stop) + auto ref_view = ranges::views::concat(tmp | ranges::views::tail, ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Filter out the points that are marked for removal // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. - for (auto windows_it = ranges::begin(windows); windows_it != ranges::end(windows); ++windows_it) + for (auto windows_it = ranges::begin(windows); ranges::distance(windows_it, ranges::end(windows)) > 2; ++windows_it) { auto A = *windows_it; auto B = *std::next(windows_it, 1); - if (B == ranges::front(windows) || B == ranges::back(windows) || ranges::distance(windows_it, ranges::end(windows)) < 3) - { - break; - } auto C = *std::next(windows_it, 2); auto D = *std::next(windows_it, 3); const auto [AB_magnitude, BC_magnitude, CD_magnitude] = computeMagnitudes(A, B, C, D); if (! isWithinAllowedDeviations(A, B, C, D, fluid_angle, max_resolution, AB_magnitude, BC_magnitude, CD_magnitude)) { - if (AB_magnitude > max_resolution) + if (AB_magnitude > allowed_deviation) { - shiftPointTowards(B, A, AB_magnitude, max_resolution / 2); + shiftPointTowards(B, A, AB_magnitude, smooth_distance); } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { to_remove.insert(B); } - if (CD_magnitude > max_resolution) + if (CD_magnitude > allowed_deviation) { - shiftPointTowards(C, D, CD_magnitude, max_resolution / 2); + shiftPointTowards(C, D, CD_magnitude, smooth_distance); } else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed { From 9cc8e9743f65a7c52b83b5e551499798dac43fe7 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 13:27:05 +0200 Subject: [PATCH 151/656] add missing include CURA-10724 --- include/utils/actions/smooth.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 1e534a2c2a..7cf9198110 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "utils/types/arachne.h" #include "utils/types/generic.h" From 1edab47c1afa2e0c2acc2eddd617ded552da0063 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 13:47:28 +0200 Subject: [PATCH 152/656] Update test CURA-10724 --- tests/utils/SmoothTest.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index 8f0c08b755..443230ed6a 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -14,6 +14,7 @@ namespace cura { + TEST(SmoothTest, TestSmooth) { Polygon poly; @@ -23,8 +24,8 @@ TEST(SmoothTest, TestSmooth) Polygon smoothed; smoothed.poly = smoother(poly.poly); - std::vector expected{ { -137, 188 }, { 1910, 540 }, { 3445, 540 }, { 4222, 683 }, { 5040, 780 }, { 5550, 2442 }, { 5420, 2720 }, { 5500, 2850 }, - { 5530, 2970 }, { 4920, 3508 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; + std::vector expected{ { -137, 188 }, { 1910, 540 }, { 3445, 540 }, { 4222, 683 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, + { 5530, 2970 }, { 5290, 3450 }, { 1980, 3972 }, { 1292, 3535 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; EXPECT_EQ(smoothed.poly, expected); auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, From bc2a8c2c099b6a75d16b30cbece79c10d383eb0e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 14:16:41 +0200 Subject: [PATCH 153/656] Replace 'tail' with 'single' in smoothing process This change replaces the 'tail' view with a 'single' view in the smoothing process of the model. The change ensures that the smoothing process takes into consideration the entire model, instead of ignoring the first point (head). The 'single' view denotes the single element at the back of the range 'tmp'. This small adjustment ensures the smoother start of the 3D model's path and addresses a specific corner case where the first point was being neglected. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 7cf9198110..fa557f7b83 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include "utils/types/arachne.h" @@ -52,7 +52,7 @@ struct smooth_fn const auto smooth_distance = static_cast(max_resolution / 2); // The distance over which the path is smoothed auto tmp = rng; // We don't want to shift the points of the ingoing range, therefor we create a temporary copy - auto ref_view = ranges::views::concat(tmp | ranges::views::tail, ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; + auto ref_view = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Filter out the points that are marked for removal // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try shifting those points outwards. From 6220a3836b234a87c70a2a017aaf924a62164961 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 16:24:31 +0200 Subject: [PATCH 154/656] Less aggressive smoothing The filter condition has been altered to be less aggressive, since the intend is to smooth out the small sudden deviations. With this in mind the removal of points is also discarded since this can introduce self- intersecting shapes, which will result in hard crashes, but more importantly introduce to much of a deviation from the intended shape. Contribute to CURA-10724 --- include/utils/actions/smooth.h | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index fa557f7b83..09f6ebd5c7 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -15,8 +15,6 @@ #include #include #include -#include -#include #include #include @@ -45,15 +43,12 @@ struct smooth_fn return static_cast(rng); } - using point_type = std::remove_cvref_t; using coord_type = std::remove_cvref_t(*ranges::begin(rng)))>; - std::set to_remove; // Set of points that are marked for removal const auto allowed_deviation = static_cast(max_resolution * 2 / 3); // The allowed deviation from the original path const auto smooth_distance = static_cast(max_resolution / 2); // The distance over which the path is smoothed auto tmp = rng; // We don't want to shift the points of the ingoing range, therefor we create a temporary copy - auto ref_view = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; - auto windows = ref_view | ranges::views::filter([&to_remove](auto point) { return ! to_remove.contains(point); }); // Filter out the points that are marked for removal + auto windows = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try shifting those points outwards. // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. @@ -71,22 +66,14 @@ struct smooth_fn { shiftPointTowards(B, A, AB_magnitude, smooth_distance); } - else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed - { - to_remove.insert(B); - } if (CD_magnitude > allowed_deviation) { shiftPointTowards(C, D, CD_magnitude, smooth_distance); } - else if (size - to_remove.size() > 2) // Only remove if there are more than 2 points left for open-paths, or 3 for closed - { - to_remove.insert(C); - } } } - return static_cast(ranges::actions::remove_if(tmp, [&to_remove](auto point) { return to_remove.contains(&point); })); + return tmp; } private: @@ -147,7 +134,7 @@ struct smooth_fn constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle, const std::integral auto max_resolution, const std::floating_point auto AB_magnitude, const std::floating_point auto BC_magnitude, const std::floating_point auto CD_magnitude) const noexcept { - if (BC_magnitude > max_resolution) + if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this { return true; } From 8173e59c8e11cd24f2623afb740c85ef0b480fa2 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 17:16:18 +0200 Subject: [PATCH 155/656] Add logging for prepared outline in WallToolPaths Added a debug log line for 'prepared_outline' in 'WallToolPaths.cpp' to capture and trace its parameters. This is to help troubleshoot issues related to polygon simplification and self-intersection fixes in complex polygons as it wasn't clear if the existing adjustments solved all occurrences. Contributes to CURA-10724 --- src/WallToolPaths.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index da4e60d0fd..b6b507ddc9 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -74,6 +74,7 @@ const std::vector& WallToolPaths::generate() // 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? Polygons prepared_outline = outline.offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); + scripta::log("prepared_outline_0", prepared_outline, section_type, layer_idx); prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); prepared_outline = Simplify(settings).polygon(prepared_outline); PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); From 0ad51a41fb3c59f3912d61f7804076c7a3969867 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 17:18:08 +0200 Subject: [PATCH 156/656] Reorder includes and add logging in SkeletalTrapezoidation This commit reorders the includes in SkeletonTrapezoidation.cpp for better code organization and adds a logging line to aid troubleshooting. The logger will now print values of polys, section_type, and layer_idx when constructing from polygons, giving better visibility into their state in case of issues. Contributes to CURA-10724 --- src/SkeletalTrapezoidation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index 580c5781e3..ed32823839 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -12,8 +12,8 @@ #include #include -#include "settings/types/Ratio.h" #include "BoostInterface.hpp" +#include "settings/types/Ratio.h" #include "utils/VoronoiUtils.h" #include "utils/linearAlg2D.h" #include "utils/macros.h" @@ -375,6 +375,7 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, , layer_idx(layer_idx) , section_type(section_type) { + scripta::log("skeletal_trapezoidation_0", polys, section_type, layer_idx); constructFromPolygons(polys); } From ded69d4293b4748c9272a64c6ad73e474ebbea9b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 2 Jul 2023 17:20:49 +0200 Subject: [PATCH 157/656] Skip smoothing for support walls Added condition to skip smoothing process for sections that are of section type 'support'. This is done to improve performance and eliminate unnecessary computation as these support walls do not require smoothing. Contributes to CURA-10724 --- src/WallToolPaths.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index b6b507ddc9..91fbbba48c 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -86,10 +86,14 @@ const std::vector& WallToolPaths::generate() prepared_outline = prepared_outline.unionPolygons(); prepared_outline = Simplify(settings).polygon(prepared_outline); - auto smoother = actions::smooth(settings.get("meshfix_maximum_resolution"), static_cast(settings.get("wall_transition_angle"))); - for (auto& polygon : prepared_outline) + if (section_type != SectionType::SUPPORT) { - polygon = smoother(polygon); + // No need to smooth support walls + auto smoother = actions::smooth(settings.get("meshfix_maximum_resolution"), static_cast(settings.get("wall_transition_angle"))); + for (auto& polygon : prepared_outline) + { + polygon = smoother(polygon); + } } if (prepared_outline.area() <= 0) From 40a8fabb158f0ff9e5ff0af8d124ce742ea0d172 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 08:20:27 +0200 Subject: [PATCH 158/656] Use ranges concepts instead of std Apple Clang 13 doesn't have full support yet. Contributes to CURA-10724 --- include/utils/types/geometry.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 9a5ebb6444..281aeb81d7 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -46,7 +46,7 @@ template concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; - requires std::integral>; + requires std::integral>; }; @@ -91,7 +91,7 @@ template concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; - requires std::integral>; + requires std::integral>; }; From 95234f7cb0a8cb4fa0af7f73b48ce6b69cc81303 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 08:50:24 +0200 Subject: [PATCH 159/656] Check against apple_clang instead of apple-clang CURA-10724 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 854a6afe61..6eca0f7974 100644 --- a/conanfile.py +++ b/conanfile.py @@ -93,7 +93,7 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings - tc.variables["RETARDED_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) <= Version("12") + tc.variables["RETARDED_APPLE_CLANG"] = self.settings.compiler == "apple_clang" and Version(self.settings.compiler.version) <= Version("12") tc.generate() def layout(self): From 140da4e80be3d6f6d8809ab3eee3f734989f9ecb Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 09:02:36 +0200 Subject: [PATCH 160/656] Revert "Check against apple_clang instead of apple-clang" This reverts commit 95234f7cb0a8cb4fa0af7f73b48ce6b69cc81303. --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 6eca0f7974..854a6afe61 100644 --- a/conanfile.py +++ b/conanfile.py @@ -93,7 +93,7 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings - tc.variables["RETARDED_APPLE_CLANG"] = self.settings.compiler == "apple_clang" and Version(self.settings.compiler.version) <= Version("12") + tc.variables["RETARDED_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) <= Version("12") tc.generate() def layout(self): From b7ef73475be931c086ee2a449d490eb7c7f0688e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 10:50:47 +0200 Subject: [PATCH 161/656] Specify the exact types for the concept for Apple-Clang CURA-10724 --- include/utils/types/generic.h | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index a639e54931..1685693e8f 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -22,11 +22,30 @@ concept hashable = requires(T value) namespace std { // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error -template -concept integral = std::is_integral_v<_Tp>; +template +concept integral = + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; -template -concept floating_point = is_floating_point_v<_Tp>; +template +concept floating_point = + std::is_same_v || + std::is_same_v || + std::is_same_v; } // namespace std #endif From 4bf339eacdcf346baf651f62f951b56e2ded0cc8 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 11:34:25 +0200 Subject: [PATCH 162/656] Moved concepts floating_point integral to std CURA-10724 --- include/utils/actions/smooth.h | 12 ++++++------ include/utils/types/arachne.h | 4 ++-- include/utils/types/generic.h | 14 +++++++++----- include/utils/types/geometry.h | 18 +++++++++--------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 09f6ebd5c7..ec61a7fa42 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -28,14 +28,14 @@ namespace cura::actions struct smooth_fn { - constexpr auto operator()(const std::integral auto max_resolution, const std::floating_point auto fluid_angle) const + constexpr auto operator()(const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const { return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, fluid_angle)); } template requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) - constexpr auto operator()(Rng&& rng, const std::integral auto max_resolution, const std::floating_point auto fluid_angle) const + constexpr auto operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const { const auto size = ranges::distance(rng) - 1; // TODO: implement for open paths! The value `-1` is for closed Paths, if open then subtract `0` if (size < 3) @@ -90,7 +90,7 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr auto cosAngle(Point* A, Point* B, Point* C, const std::floating_point auto AB_magnitude, const std::floating_point auto BC_magnitude) const noexcept + constexpr auto cosAngle(Point* A, Point* B, Point* C, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept { if (AB_magnitude == 0.0 || BC_magnitude == 0.0) { @@ -105,7 +105,7 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr void shiftPointTowards(Point* point, Point* target, const std::floating_point auto p0p1_distance, const std::integral auto smooth_distance) const noexcept + constexpr void shiftPointTowards(Point* point, Point* target, const utils::floating_point auto p0p1_distance, const utils::integral auto smooth_distance) const noexcept { using coord_type = std::remove_cvref_t(*point))>; const auto shift_distance = smooth_distance / p0p1_distance; @@ -131,8 +131,8 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const std::floating_point auto fluid_angle, const std::integral auto max_resolution, - const std::floating_point auto AB_magnitude, const std::floating_point auto BC_magnitude, const std::floating_point auto CD_magnitude) const noexcept + constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, + const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept { if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this { diff --git a/include/utils/types/arachne.h b/include/utils/types/arachne.h index 8f05d6d5d6..4a2808dc14 100644 --- a/include/utils/types/arachne.h +++ b/include/utils/types/arachne.h @@ -10,8 +10,8 @@ #include -#include "utils/types/geometry.h" #include "utils/types/generic.h" +#include "utils/types/geometry.h" namespace cura::utils { @@ -86,7 +86,7 @@ template concept junction = requires(T val) { requires point2d; - requires std::integral; + requires utils::integral; }; /*! diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 1685693e8f..f5c8551227 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -16,11 +16,8 @@ concept hashable = requires(T value) { std::hash{}(value) } -> concepts::convertible_to; }; -} // namespace cura - #ifdef RETARDED_APPLE_CLANG -namespace std -{ + // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error template concept integral = @@ -46,7 +43,14 @@ concept floating_point = std::is_same_v || std::is_same_v || std::is_same_v; -} // namespace std +#else +template +concept integral = std::integral; + +template +concept floating_point = std::floating_point; #endif +} // namespace cura::utils + #endif // CURAENGINE_GENERIC_H diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 281aeb81d7..2cbc59efcc 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -34,7 +34,7 @@ template concept point2d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type>>; - requires std::integral>; + requires utils::integral>; }; /*! @@ -46,7 +46,7 @@ template concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; - requires std::integral>; + requires utils::integral>; }; @@ -58,8 +58,8 @@ concept point2d_ranged = ranges::range && requires(T point) template concept point2d_named = requires(T point) { - requires std::integral; - requires std::integral; + requires utils::integral; + requires utils::integral; }; /*! @@ -79,7 +79,7 @@ template concept point3d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type, typename std::tuple_element<0, T>::type>>; - requires std::integral>; + requires utils::integral>; }; /*! @@ -91,7 +91,7 @@ template concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; - requires std::integral>; + requires utils::integral>; }; @@ -103,9 +103,9 @@ concept point3d_ranged = ranges::range && requires(T point) template concept point3d_named = requires(T point) { - requires std::integral; - requires std::integral; - requires std::integral; + requires utils::integral; + requires utils::integral; + requires utils::integral; }; /*! From 9fee071006596d8094a43ff142d0b26c65b239ba Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 11:35:52 +0200 Subject: [PATCH 163/656] Applied code-review suggestions CURA-10724 --- CMakeLists.txt | 2 +- conanfile.py | 2 +- include/utils/types/generic.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71263c096b..d3b3fd441e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,7 +150,7 @@ target_include_directories(_CuraEngine target_compile_definitions(_CuraEngine PUBLIC $<$:ARCUS> - $<$:RETARDED_APPLE_CLANG> + $<$:OLDER_APPLE_CLANG> CURA_ENGINE_VERSION=\"${CURA_ENGINE_VERSION}\" $<$:BUILD_TESTS> PRIVATE diff --git a/conanfile.py b/conanfile.py index 854a6afe61..762bb773d2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -93,7 +93,7 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings - tc.variables["RETARDED_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) <= Version("12") + tc.variables["OLDER_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) <= Version("12") tc.generate() def layout(self): diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index f5c8551227..5cc73df379 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -16,7 +16,7 @@ concept hashable = requires(T value) { std::hash{}(value) } -> concepts::convertible_to; }; -#ifdef RETARDED_APPLE_CLANG +#ifdef OLDER_APPLE_CLANG // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error template From 3cc85bcec83960d71ea1e404e1ee50d659b9e5bf Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 11:57:33 +0200 Subject: [PATCH 164/656] Check against version 14 of Apple-Clang CURA-10724 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 762bb773d2..9327acc542 100644 --- a/conanfile.py +++ b/conanfile.py @@ -93,7 +93,7 @@ def generate(self): tc.variables["ENABLE_TESTING"] = self.options.enable_testing tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings - tc.variables["OLDER_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) <= Version("12") + tc.variables["OLDER_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) < "14" tc.generate() def layout(self): From 49ea674d092cdc8f2469f6683b9cbf7925156d59 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 12:38:07 +0200 Subject: [PATCH 165/656] Improve code readability and add assertions Applied review comments. This commit addresses several changes in the code to improve readability and add more assertions where necessary. Redundant comments have been removed, such as the namespace declaration in geometry.h for readability. Grammar mistakes in comments were corrected for the same reason in smooth.h. In get.h and generic.h, a static assertion was added to restrict the size of the string value to one character, ensuring concise point coordinate inputs. In generic.h, a comment was added to indicate why we are adding a manual implementation of integral and floating point, due to lack of support in older versions of Apple Clang. These changes were made to improve the quality and maintainability of the code. Contributes to CURA-10724 --- include/utils/actions/smooth.h | 6 +++--- include/utils/types/generic.h | 1 + include/utils/types/geometry.h | 4 ---- include/utils/types/get.h | 5 ++++- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index ec61a7fa42..5d707998f7 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -34,10 +34,10 @@ struct smooth_fn } template - requires ranges::forward_range&& ranges::sized_range&& ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) + requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) constexpr auto operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const { - const auto size = ranges::distance(rng) - 1; // TODO: implement for open paths! The value `-1` is for closed Paths, if open then subtract `0` + const auto size = ranges::distance(rng) - 1; if (size < 3) { return static_cast(rng); @@ -47,7 +47,7 @@ struct smooth_fn const auto allowed_deviation = static_cast(max_resolution * 2 / 3); // The allowed deviation from the original path const auto smooth_distance = static_cast(max_resolution / 2); // The distance over which the path is smoothed - auto tmp = rng; // We don't want to shift the points of the ingoing range, therefor we create a temporary copy + auto tmp = rng; // We don't want to shift the points of the in-going range, therefore we create a temporary copy auto windows = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; // Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try shifting those points outwards. diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 5cc73df379..b6a3c49b3e 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -18,6 +18,7 @@ concept hashable = requires(T value) #ifdef OLDER_APPLE_CLANG +// std::integral and std::floating_point are not implemented in older Apple Clang versions < 13 // https://stackoverflow.com/questions/71818683/stdintegral-not-found-in-clang13-c20-error template concept integral = diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 2cbc59efcc..4a8eedfb66 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -18,10 +18,6 @@ #include "utils/types/generic.h" -/*! - * @namespace cura::utils - * @brief Namspace for Cura utils - */ namespace cura::utils { diff --git a/include/utils/types/get.h b/include/utils/types/get.h index 38c28cbd4e..4df9c0f494 100644 --- a/include/utils/types/get.h +++ b/include/utils/types/get.h @@ -40,6 +40,7 @@ template constexpr auto& get(cura::utils::point2d auto& point) noexcept { constexpr std::string_view idx = C.value; + static_assert(idx.size() == 1, "Only one character allowed"); static_assert(idx.starts_with("X") || idx.starts_with("x") || idx.starts_with("Y") || idx.starts_with("y"), "Index out of bounds"); if constexpr (idx.starts_with("X") || idx.starts_with("x")) { @@ -59,6 +60,7 @@ template constexpr const auto& get(const cura::utils::point2d auto& point) noexcept { constexpr std::string_view idx = C.value; + static_assert(idx.size() == 1, "Only one character allowed"); static_assert(idx.starts_with("X") || idx.starts_with("x") || idx.starts_with("Y") || idx.starts_with("y"), "Index out of bounds"); if constexpr (idx.starts_with("X") || idx.starts_with("x")) { @@ -99,8 +101,9 @@ constexpr auto& get(cura::utils::point3d_named auto& point) noexcept template constexpr auto& get(cura::utils::point3d auto& point) noexcept { - static_assert(C.value == "X" || C.value == "x" || C.value == "Y" || C.value == "y" || C.value == "Z" || C.value == "z", "Index out of bounds"); constexpr std::string_view idx = C.value; + static_assert(idx.size() == 1, "Only one character allowed"); + static_assert(idx.starts_with("X") || idx.starts_with("x") || idx.starts_with("Y") || idx.starts_with("y") || idx.starts_with("Z") || idx.starts_with("z"), "Index out of bounds"); if constexpr (idx.starts_with("X") || idx.starts_with("x")) { return std::get<0>(point); From a7caa49960e9068f99d94245b89a22af8bd52284 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 12:46:46 +0200 Subject: [PATCH 166/656] Quick mock test for green marks Write some actual tests CURA-10724 --- tests/utils/SmoothTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index 443230ed6a..4d6361e12e 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -14,18 +14,18 @@ namespace cura { - TEST(SmoothTest, TestSmooth) { + // TODO: Write some actual tests Polygon poly; poly.poly = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; - auto smoother = cura::actions::smooth(750, 5.0); + auto smoother = cura::actions::smooth(2000, 10.0); Polygon smoothed; smoothed.poly = smoother(poly.poly); - std::vector expected{ { -137, 188 }, { 1910, 540 }, { 3445, 540 }, { 4222, 683 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, - { 5530, 2970 }, { 5290, 3450 }, { 1980, 3972 }, { 1292, 3535 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; + std::vector expected{ { -137, 188 }, { 1910, 540 }, { 2820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, + { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; EXPECT_EQ(smoothed.poly, expected); auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, From be715bcdffd64150eea2ab12dbe9578d145e2e44 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 3 Jul 2023 17:16:25 +0200 Subject: [PATCH 167/656] Switched the order of the smoothing Smoothing can probably introduce some self-intersections. Which can result in crashes CURA-10724 --- src/WallToolPaths.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index 91fbbba48c..dbb87b027a 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -77,15 +77,6 @@ const std::vector& WallToolPaths::generate() scripta::log("prepared_outline_0", prepared_outline, section_type, layer_idx); prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); prepared_outline = Simplify(settings).polygon(prepared_outline); - PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); - prepared_outline.removeDegenerateVerts(); - prepared_outline.removeColinearEdges(AngleRadians(0.005)); - // Removing collinear edges may introduce self intersections, so we need to fix them again - PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); - prepared_outline.removeDegenerateVerts(); - prepared_outline = prepared_outline.unionPolygons(); - prepared_outline = Simplify(settings).polygon(prepared_outline); - if (section_type != SectionType::SUPPORT) { // No need to smooth support walls @@ -96,6 +87,15 @@ const std::vector& WallToolPaths::generate() } } + PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); + prepared_outline.removeDegenerateVerts(); + prepared_outline.removeColinearEdges(AngleRadians(0.005)); + // Removing collinear edges may introduce self intersections, so we need to fix them again + PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); + prepared_outline.removeDegenerateVerts(); + prepared_outline = prepared_outline.unionPolygons(); + prepared_outline = Simplify(settings).polygon(prepared_outline); + if (prepared_outline.area() <= 0) { assert(toolpaths.empty()); From d7fd71837ce734c771dcd61048460eb14beaa989 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 3 Jul 2023 20:42:48 +0200 Subject: [PATCH 168/656] Add unit tests CURA-10724 --- include/utils/actions/smooth.h | 30 +++---- tests/utils/SmoothTest.cpp | 140 +++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 15 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 5d707998f7..7562e26888 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -76,6 +76,21 @@ struct smooth_fn return tmp; } + template + requires utils::point2d || utils::junction + constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, + const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept + { + if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this + { + return true; + } + const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); + const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); + const auto abs_angle = std::abs(cos_A - cos_B); + return abs_angle < fluid_angle; + } + private: template requires utils::point2d || utils::junction @@ -128,21 +143,6 @@ struct smooth_fn { return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); } - - template - requires utils::point2d || utils::junction - constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, - const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept - { - if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this - { - return true; - } - const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); - const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); - const auto abs_angle = std::abs(cos_A - cos_B); - return abs_angle < fluid_angle; - } }; inline constexpr smooth_fn smooth{}; diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index 4d6361e12e..338a16df41 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -31,5 +31,145 @@ TEST(SmoothTest, TestSmooth) auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; EXPECT_EQ(poly.poly, original_expected); + + // test isWithinAllowedDeviations utility function + auto FLUID_ANGLE = std::cos(10. * M_PI / 180.); + auto MAX_RESOUTION = 250; + cura::actions::smooth_fn smooth; + + { + /* + * + * A ------------- B + * | + * C --------------- D + * + */ + auto A = std::make_tuple(0, 0); + auto B = std::make_tuple(0, 100); + auto C = std::make_tuple(1, 100); + auto D = std::make_tuple(1, 200); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); + EXPECT_EQ(is_within_allowed_deviation, false); + } + + { + /* + * + * A ----------- B + * \ + * C + * | + * | + * | + * D + * + */ + auto A = std::make_tuple(0, 0); + auto B = std::make_tuple(100, 0); + auto C = std::make_tuple(101, 1); + auto D = std::make_tuple(101, 101); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., std::sqrt(2), 100.); + EXPECT_EQ(is_within_allowed_deviation, true); + } + + { + /* + * + * A ----------- B - C -------------D + * + */ + auto A = std::make_tuple(0, 0); + auto B = std::make_tuple(100, 0); + auto C = std::make_tuple(101, 0); + auto D = std::make_tuple(201, 0); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); + EXPECT_EQ(is_within_allowed_deviation, true); + } + + { + /* + * + * D ----------- C - B -------------A + * + */ + auto A = std::make_tuple(201, 0); + auto B = std::make_tuple(101, 0); + auto C = std::make_tuple(100, 0); + auto D = std::make_tuple(0, 0); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); + EXPECT_EQ(is_within_allowed_deviation, true); + } + + { + /* + * + * + * B + * \ + * D ----------- C + * \ + * \ + * \ + * \ + * \ + * D + * + */ + auto A = std::make_tuple(0, 0); + auto B = std::make_tuple(100, 0); + auto C = std::make_tuple(99, -1); + auto D = std::make_tuple(199, 99); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., std::sqrt(2.), std::sqrt(100. * 100. + 100. * 100.)); + EXPECT_EQ(is_within_allowed_deviation, true); + } + + { + /* + * + * D ----------- C + * \ + * B + * \ + * \ + * \ + * \ + * D + * + */ + auto A = std::make_tuple(0, 0); + auto B = std::make_tuple(100, 0); + auto C = std::make_tuple(101, 1); + auto D = std::make_tuple(201, 101); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., std::sqrt(2.), std::sqrt(100. * 100. + 100. * 100.)); + EXPECT_EQ(is_within_allowed_deviation, true); + } + + { + /* + * + * D ----------- C - B + * | + * | + * | + * | + * D + * + */ + auto A = std::make_tuple(0, 0); + auto B = std::make_tuple(100, 0); + auto C = std::make_tuple(101, 0); + auto D = std::make_tuple(101, 100); + + const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); + EXPECT_EQ(is_within_allowed_deviation, true); + } + } } // namespace cura \ No newline at end of file From 5044f34e1793aa6958cf0c64ea817568d40bc627 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 4 Jul 2023 10:20:07 +0200 Subject: [PATCH 169/656] Revert "Add unit tests" This reverts commit d7fd71837ce734c771dcd61048460eb14beaa989. --- include/utils/actions/smooth.h | 30 +++---- tests/utils/SmoothTest.cpp | 140 --------------------------------- 2 files changed, 15 insertions(+), 155 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 7562e26888..5d707998f7 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -76,21 +76,6 @@ struct smooth_fn return tmp; } - template - requires utils::point2d || utils::junction - constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, - const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept - { - if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this - { - return true; - } - const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); - const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); - const auto abs_angle = std::abs(cos_A - cos_B); - return abs_angle < fluid_angle; - } - private: template requires utils::point2d || utils::junction @@ -143,6 +128,21 @@ struct smooth_fn { return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); } + + template + requires utils::point2d || utils::junction + constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, + const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept + { + if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this + { + return true; + } + const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); + const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); + const auto abs_angle = std::abs(cos_A - cos_B); + return abs_angle < fluid_angle; + } }; inline constexpr smooth_fn smooth{}; diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index 338a16df41..4d6361e12e 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -31,145 +31,5 @@ TEST(SmoothTest, TestSmooth) auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; EXPECT_EQ(poly.poly, original_expected); - - // test isWithinAllowedDeviations utility function - auto FLUID_ANGLE = std::cos(10. * M_PI / 180.); - auto MAX_RESOUTION = 250; - cura::actions::smooth_fn smooth; - - { - /* - * - * A ------------- B - * | - * C --------------- D - * - */ - auto A = std::make_tuple(0, 0); - auto B = std::make_tuple(0, 100); - auto C = std::make_tuple(1, 100); - auto D = std::make_tuple(1, 200); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); - EXPECT_EQ(is_within_allowed_deviation, false); - } - - { - /* - * - * A ----------- B - * \ - * C - * | - * | - * | - * D - * - */ - auto A = std::make_tuple(0, 0); - auto B = std::make_tuple(100, 0); - auto C = std::make_tuple(101, 1); - auto D = std::make_tuple(101, 101); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., std::sqrt(2), 100.); - EXPECT_EQ(is_within_allowed_deviation, true); - } - - { - /* - * - * A ----------- B - C -------------D - * - */ - auto A = std::make_tuple(0, 0); - auto B = std::make_tuple(100, 0); - auto C = std::make_tuple(101, 0); - auto D = std::make_tuple(201, 0); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); - EXPECT_EQ(is_within_allowed_deviation, true); - } - - { - /* - * - * D ----------- C - B -------------A - * - */ - auto A = std::make_tuple(201, 0); - auto B = std::make_tuple(101, 0); - auto C = std::make_tuple(100, 0); - auto D = std::make_tuple(0, 0); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); - EXPECT_EQ(is_within_allowed_deviation, true); - } - - { - /* - * - * - * B - * \ - * D ----------- C - * \ - * \ - * \ - * \ - * \ - * D - * - */ - auto A = std::make_tuple(0, 0); - auto B = std::make_tuple(100, 0); - auto C = std::make_tuple(99, -1); - auto D = std::make_tuple(199, 99); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., std::sqrt(2.), std::sqrt(100. * 100. + 100. * 100.)); - EXPECT_EQ(is_within_allowed_deviation, true); - } - - { - /* - * - * D ----------- C - * \ - * B - * \ - * \ - * \ - * \ - * D - * - */ - auto A = std::make_tuple(0, 0); - auto B = std::make_tuple(100, 0); - auto C = std::make_tuple(101, 1); - auto D = std::make_tuple(201, 101); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., std::sqrt(2.), std::sqrt(100. * 100. + 100. * 100.)); - EXPECT_EQ(is_within_allowed_deviation, true); - } - - { - /* - * - * D ----------- C - B - * | - * | - * | - * | - * D - * - */ - auto A = std::make_tuple(0, 0); - auto B = std::make_tuple(100, 0); - auto C = std::make_tuple(101, 0); - auto D = std::make_tuple(101, 100); - - const auto is_within_allowed_deviation = smooth.isWithinAllowedDeviations(&A, &B, &C, &D, FLUID_ANGLE, MAX_RESOUTION, 100., 1., 100.); - EXPECT_EQ(is_within_allowed_deviation, true); - } - } } // namespace cura \ No newline at end of file From 03d93b688d489ea57d4f909f80d6aed9c6c85dd0 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 4 Jul 2023 15:37:58 +0200 Subject: [PATCH 170/656] Import dynamic libs for win and mac --- conanfile.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index db8f50331e..d681294e20 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,7 +5,7 @@ from conan import ConanFile from conan.errors import ConanInvalidConfiguration -from conan.tools.files import copy +from conan.tools.files import copy, mkdir from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake, cmake_layout from conan.tools.build import check_min_cppstd from conan.tools.scm import Version @@ -95,6 +95,23 @@ def generate(self): tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings tc.generate() + for dep in self.dependencies.values(): + if len(dep.cpp_info.libdirs) > 0: + copy(self, "*.dylib", dep.cpp_info.libdirs[0], self.build_folder) + copy(self, "*.dll", dep.cpp_info.libdirs[0], self.build_folder) + if len(dep.cpp_info.bindirs) > 0: + copy(self, "*.dll", dep.cpp_info.bindirs[0], self.build_folder) + if self.options.enable_testing: + test_path = path.join(self.build_folder, "tests") + if not path.exists(test_path): + mkdir(self, test_path) + if len(dep.cpp_info.libdirs) > 0: + copy(self, "*.dylib", dep.cpp_info.libdirs[0], path.join(self.build_folder, "tests")) + copy(self, "*.dll", dep.cpp_info.libdirs[0], path.join(self.build_folder, "tests")) + if len(dep.cpp_info.bindirs) > 0: + copy(self, "*.dll", dep.cpp_info.bindirs[0], path.join(self.build_folder, "tests")) + + def layout(self): cmake_layout(self) From 66078d9d487719d2655e674dce61371a7757684c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 5 Jul 2023 08:54:46 +0200 Subject: [PATCH 171/656] Move generic.h to types folder and add grpc_convertable concept The file generic.h was moved from the utils/concepts directory to the utils/types directory to better reflect the contents of the file, now including a new concept for grpc_convertable. This new concept checks if a type T is a semiregular type that can convert to grpc. This enhances the code's clarity and can improve the development of grpc-utilizing functionalities. Contributes to CURA-10475 --- include/plugins/pluginproxy.h | 4 ++-- include/plugins/types.h | 2 +- include/utils/concepts/generic.h | 0 include/utils/types/generic.h | 11 +++++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) delete mode 100644 include/utils/concepts/generic.h diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 13daba6f0d..bb549d7be7 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -20,7 +20,7 @@ #include "plugins/metadata.h" #include "cura/plugins/v0/slot_id.pb.h" -#include "utils/concepts/generic.h" +#include "utils/types/generic.h" namespace cura::plugins { @@ -38,7 +38,7 @@ namespace cura::plugins * @tparam Request The gRPC convertible request type. * @tparam Response The gRPC convertible response type. */ -template +template class PluginProxy { public: diff --git a/include/plugins/types.h b/include/plugins/types.h index 043a042c32..1ddd93c371 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -11,7 +11,7 @@ #include #include "utils/IntPoint.h" -#include "utils/concepts/generic.h" +#include "utils/types/generic.h" #include "utils/polygon.h" #include "cura/plugins/v0/slot_id.pb.h" diff --git a/include/utils/concepts/generic.h b/include/utils/concepts/generic.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index b6a3c49b3e..402eeba9c2 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -8,6 +8,9 @@ #include #include +#include +#include + namespace cura::utils { template @@ -16,6 +19,14 @@ concept hashable = requires(T value) { std::hash{}(value) } -> concepts::convertible_to; }; +template +concept grpc_convertable = requires(T value) +{ + requires ranges::semiregular; + requires ranges::semiregular; + requires ranges::semiregular; +}; + #ifdef OLDER_APPLE_CLANG // std::integral and std::floating_point are not implemented in older Apple Clang versions < 13 From b03d2a3b2370c5b30b41a339fa780211cbdb8462 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 5 Jul 2023 08:58:09 +0200 Subject: [PATCH 172/656] Rename confusing CMake option Removed "RETARDED_APPLE_CLANG" option and replaced it with "OLDER_APPLE_CLANG". This change was made to give a clear understanding of the option's function, which is to check if the Apple Clang version used is 13 or older. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3b3fd441e..202fb5bf61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ option(ENABLE_TESTING "Build with unit tests" OFF) option(EXTENSIVE_WARNINGS "Build with all warnings" ON) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) -option(RETARDED_APPLE_CLANG "Apple Clang <= 13 used" OFF) +option(OLDER_APPLE_CLANG "Apple Clang <= 13 used" OFF) # Create Protobuf files if Arcus is used if (ENABLE_ARCUS) From 7b1e854793071d1afa1dd5d79197ef85b06bcec2 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 5 Jul 2023 09:17:20 +0200 Subject: [PATCH 173/656] Shift UUID generation from FffGcodeWriter to Application The changes are intended to centralize UUID generation inside the Application class rather than in each instance of the FffGcodeWriter class. This approach allows plugins to hook up with multiple instances and still differentiate where the data comes from. This is done by introducing instance_uuid data member in the Application class which generates and stores UUID. This UUID is then used in FffGcodeWriter constructor. This change also removes the dependency on boost/uuid in FffGcodeWriter. Contributes to CURA-10715 --- include/Application.h | 9 +++++++-- src/Application.cpp | 6 ++++-- src/FffGcodeWriter.cpp | 4 +--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/Application.h b/include/Application.h index 13dbdc150c..1b427864f5 100644 --- a/include/Application.h +++ b/include/Application.h @@ -4,9 +4,12 @@ #ifndef APPLICATION_H #define APPLICATION_H -#include "utils/NoCopy.h" -#include //For size_t. +#include #include +#include + +#include "utils/NoCopy.h" + namespace cura { @@ -89,6 +92,8 @@ class Application : NoCopy */ void startThreadPool(int nworkers=0); + std::string_view instance_uuid; + protected: #ifdef ARCUS /*! diff --git a/src/Application.cpp b/src/Application.cpp index 15454631d8..a31577ff70 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -7,6 +7,8 @@ #include #include +#include //For generating a UUID. +#include //For generating a UUID. #include #include #include @@ -28,7 +30,7 @@ namespace cura { -Application::Application() +Application::Application() : instance_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) { auto dup_sink = std::make_shared(std::chrono::seconds{ 10 }); auto base_sink = std::make_shared(); @@ -39,7 +41,7 @@ Application::Application() if (auto spdlog_val = spdlog::details::os::getenv("CURAENGINE_LOG_LEVEL"); ! spdlog_val.empty()) { spdlog::cfg::helpers::load_levels(spdlog_val); - } + }; } Application::~Application() diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1ae71f4c0f..708c84f9c1 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -7,8 +7,6 @@ #include #include -#include //For generating a UUID. -#include //For generating a UUID. #include #include @@ -34,7 +32,7 @@ namespace cura { -FffGcodeWriter::FffGcodeWriter() : max_object_height(0), layer_plan_buffer(gcode), slice_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) +FffGcodeWriter::FffGcodeWriter() : max_object_height(0), layer_plan_buffer(gcode), slice_uuid(Application::getInstance().instance_uuid) { for (unsigned int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++) { // initialize all as max layer_nr, so that they get updated to the lowest layer on which they are used. From 67c61c3c0cd32f9447acf77aa536288cf2b9a36c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 5 Jul 2023 11:50:24 +0200 Subject: [PATCH 174/656] Add `engine_uuid` and `thread_uuid` to metadata Changed `instance_uuid` from `std::string_view` to `std::string` in `Application.h` and added `engine_uuid` to `metadata.h`. Also included `` and `Application.h` in `pluginproxy.h` to generate `engine_uuid` and `thread_id` for every thread and added these keys to metadata for the RPC's such that plugins can keep the books where data came from. Contribute to CURA-10715 --- include/Application.h | 2 +- include/plugins/metadata.h | 1 + include/plugins/pluginproxy.h | 10 ++++++++-- include/utils/format/thread_id.h | 28 ++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 include/utils/format/thread_id.h diff --git a/include/Application.h b/include/Application.h index 1b427864f5..9bcf4fdb28 100644 --- a/include/Application.h +++ b/include/Application.h @@ -92,7 +92,7 @@ class Application : NoCopy */ void startThreadPool(int nworkers=0); - std::string_view instance_uuid; + std::string instance_uuid; protected: #ifdef ARCUS diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index db17d9a201..461fe9ea53 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -57,6 +57,7 @@ struct slot_metadata { plugins::v0::SlotID slot_id; std::string_view version_range; + std::string_view engine_uuid; }; } // namespace cura::plugins diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index bb549d7be7..2e94d73e28 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -4,6 +4,8 @@ #ifndef PLUGINS_PLUGINPROXY_H #define PLUGINS_PLUGINPROXY_H +#include + #include #include #include @@ -16,11 +18,13 @@ #include #include +#include "Application.h" +#include "utils/format/thread_id.h" #include "plugins/exception.h" #include "plugins/metadata.h" +#include "utils/types/generic.h" #include "cura/plugins/v0/slot_id.pb.h" -#include "utils/types/generic.h" namespace cura::plugins { @@ -61,7 +65,7 @@ class PluginProxy ranges::semiregular_box stub_; ///< The gRPC stub for communication. - constexpr static slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value }; + slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; std::optional plugin_info_{ std::nullopt }; ///< The plugin info object. public: @@ -134,6 +138,8 @@ class PluginProxy // Metadata client_context.AddMetadata("cura-slot-service-name", fmt::format("{}", slot_info_.slot_id)); client_context.AddMetadata("cura-slot-version-range", slot_info_.version_range.data()); + client_context.AddMetadata("cura-engine-uuid", slot_info_.engine_uuid.data()); + client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); // Construct request auto request{ req_(std::forward(args)...) }; diff --git a/include/utils/format/thread_id.h b/include/utils/format/thread_id.h new file mode 100644 index 0000000000..48bf1158b1 --- /dev/null +++ b/include/utils/format/thread_id.h @@ -0,0 +1,28 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_FORMAT_THREAD_ID_H +#define UTILS_FORMAT_THREAD_ID_H + +#include +#include +#include + +#include + +namespace fmt +{ +template<> +struct formatter : formatter +{ + template + auto format(std::thread::id thread_id, FormatContext& ctx) + { + std::ostringstream oss; + oss << thread_id; + return formatter::format(oss.str(), ctx); + } +}; + +} // namespace fmt +#endif // UTILS_FORMAT_THREAD_ID_H From a7a8f41f3facd54bbe02fce47d97d3609f7424c3 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 5 Jul 2023 15:00:17 +0200 Subject: [PATCH 175/656] Update dependency version in conanfile Updated the 'curaengine_grpc_definitions' version to use the latest from 'ultimaker/cura_10714' instead of 'ultimaker/testing' in conanfile.py. This change ensures our project is using the grpc definitions with the handshake RPCs added to the services Contribute to CURA-10714 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index f65b912c6c..ec64e0c14b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10714") def generate(self): deps = CMakeDeps(self) From 0f0e9f07a943ed2c27c088836e2b950e54d39a6f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 5 Jul 2023 15:01:35 +0200 Subject: [PATCH 176/656] Add new rule to .clang-tidy configuration This commit includes the addition of 'cppcoreguidelines-avoid-capturing-lambda-coroutines' rule to the .clang-tidy configuration file. This rule has been added to supress the warnings in the co_await routines Contribute to CURA-10714 --- .clang-tidy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 77b35faafa..61be54b772 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -10,7 +10,8 @@ Checks: > -altera-struct-pack-align, -android-*, -misc-non-private-member-variables-in-classes, - -fuchsia-overloaded-operator + -fuchsia-overloaded-operator, + -cppcoreguidelines-avoid-capturing-lambda-coroutines WarningsAsErrors: '-*' HeaderFilterRegex: '' FormatStyle: none From 307f8243a1f7ecb9cd3a7ad9a07590fef46d6d8f Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 5 Jul 2023 22:14:46 +0200 Subject: [PATCH 177/656] Take skirt-height into account for skirt-outline(s). CURA-10728 --- include/SkirtBrim.h | 19 ------------------- src/SkirtBrim.cpp | 13 ++++++++++++- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/include/SkirtBrim.h b/include/SkirtBrim.h index acce6bb0e6..aefbd5bdd6 100644 --- a/include/SkirtBrim.h +++ b/include/SkirtBrim.h @@ -204,25 +204,6 @@ class SkirtBrim * Generate the brim which is printed from the outlines of the support inward. */ void generateSupportBrim(); - -private: - /*! - * \brief Generate the skirt/brim lines around the model. - * - * \param start_distance The distance of the first outset from the parts at - * the first line. - * \param primary_line_count Number of offsets / brim lines of the primary - * extruder. - * \param primary_extruder_minimal_length The minimal total length of the - * skirt/brim lines of the primary extruder. - * \param first_layer_outline The reference polygons from which to offset - * outward to generate skirt/brim lines. - * \param[out] skirt_brim_primary_extruder Where to store the resulting - * brim/skirt lines. - * \return The offset of the last brim/skirt line from the reference polygon - * \p first_layer_outline. - */ - static coord_t 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); }; }//namespace cura diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index b568a5a3ef..48046bb322 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -386,7 +386,18 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) constexpr bool include_support = true; const bool skirt_around_prime_tower_brim = storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable"); const bool include_prime_tower = ! skirt_around_prime_tower_brim; // include manually otherwise - first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_only, extruder_nr); + + const int skirt_height = global_settings.get("skirt_height"); + first_layer_outline = Polygons(); + for (int i_layer = layer_nr; i_layer <= skirt_height; ++i_layer) + { + first_layer_outline = + first_layer_outline.unionPolygons + ( + storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder_nr) + ); + } + if (skirt_around_prime_tower_brim) { const int prime_tower_brim_extruder_nr = storage.primeTower.extruder_order[0]; From 694ee17852df4c51cc577fb35cffe0ff58456561 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 6 Jul 2023 14:27:34 +0200 Subject: [PATCH 178/656] Update gRPC method naming and dependency inclusion This commit updates the gRPC method naming in Slots and Converters files from 'Modify' to 'Call' based on recent API changes. In the PluginProxy file, a function to prepare the gRPC client context has been extracted for reducing code repetition and improving readability. Contributes to CURA-10714 --- include/plugins/converters.h | 8 ++++---- include/plugins/pluginproxy.h | 29 ++++++++++++++++------------- include/plugins/slots.h | 4 ++-- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 0282605634..7339daa2e2 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -22,7 +22,7 @@ namespace cura::plugins struct simplify_request { - using value_type = slots::simplify::v0::SimplifyServiceModifyRequest; ///< The protobuf message type. + using value_type = slots::simplify::v0::CallRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -80,7 +80,7 @@ struct simplify_request */ struct simplify_response { - using value_type = slots::simplify::v0::SimplifyServiceModifyResponse; ///< The protobuf message type. + using value_type = slots::simplify::v0::CallResponse; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -118,7 +118,7 @@ struct simplify_response struct postprocess_request { - using value_type = slots::postprocess::v0::PostprocessServiceModifyRequest; ///< The protobuf message type. + using value_type = slots::postprocess::v0::CallRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. /** @@ -137,7 +137,7 @@ struct postprocess_request struct postprocess_response { - using value_type = slots::postprocess::v0::PostprocessServiceModifyResponse; + using value_type = slots::postprocess::v0::CallResponse; using native_value_type = std::string; native_value_type operator()(const value_type& message) const diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 2e94d73e28..cd79b89046 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,9 +19,9 @@ #include #include "Application.h" -#include "utils/format/thread_id.h" #include "plugins/exception.h" #include "plugins/metadata.h" +#include "utils/format/thread_id.h" #include "utils/types/generic.h" #include "cura/plugins/v0/slot_id.pb.h" @@ -129,17 +129,9 @@ class PluginProxy grpc_context, [this, &status, &grpc_context, &ret_value, &args...]() -> boost::asio::awaitable { - using RPC = agrpc::RPC<&stub_t::PrepareAsyncModify>; + using RPC = agrpc::RPC<&stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; - - // Set time-out - client_context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(500)); // TODO: don't use magic number and make it realistic - - // Metadata - client_context.AddMetadata("cura-slot-service-name", fmt::format("{}", slot_info_.slot_id)); - client_context.AddMetadata("cura-slot-version-range", slot_info_.version_range.data()); - client_context.AddMetadata("cura-engine-uuid", slot_info_.engine_uuid.data()); - client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); + prep_client_context(client_context); // Construct request auto request{ req_(std::forward(args)...) }; @@ -162,7 +154,7 @@ class PluginProxy boost::asio::detached); grpc_context.run(); - if (! status.ok()) // TODO: handle different kind of status codes + if (! status.ok()) // TODO: handle different kind of status codes { if (plugin_info_.has_value()) { @@ -171,7 +163,7 @@ class PluginProxy throw exceptions::RemoteException(slot_info_, status.error_message()); } - if (! valid_ ) + if (! valid_) { if (plugin_info_.has_value()) { @@ -182,6 +174,17 @@ class PluginProxy return ret_value; } + + void prep_client_context(grpc::ClientContext& client_context, std::chrono::milliseconds timeout = std::chrono::milliseconds(500)) + { + // Set time-out + client_context.set_deadline(std::chrono::system_clock::now() + timeout); + + // Metadata + client_context.AddMetadata("cura-engine-uuid", slot_info_.engine_uuid.data()); + client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); + } + }; } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index ed29493824..7ff29b8a04 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -49,7 +49,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy; +using slot_simplify_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -59,7 +59,7 @@ using slot_simplify_ = SlotProxy -using slot_postprocess_ = SlotProxy; +using slot_postprocess_ = SlotProxy; template struct Typelist From fdf9bd229bedb1683d18b8beaf2ca787034bb78b Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sat, 8 Jul 2023 14:39:13 +0200 Subject: [PATCH 179/656] Replace size_t format string in InfillTest.cpp This will fix an incorrect format causing test failure on 32-bit architectures. --- tests/InfillTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/InfillTest.cpp b/tests/InfillTest.cpp index 1560d1a2f2..81e7dd7d71 100644 --- a/tests/InfillTest.cpp +++ b/tests/InfillTest.cpp @@ -100,7 +100,7 @@ class InfillTestParameters , result_polygons(std::move(result_polygons)) { // FIXME: Once we are using spdlog as logger, we'll also use fmt::format() here, see CURA-8258. - name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%lld", static_cast(params.pattern), static_cast(params.zig_zagify), static_cast(params.connect_polygons), params.line_distance, test_polygon_id); + name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%zu", static_cast(params.pattern), static_cast(params.zig_zagify), static_cast(params.connect_polygons), params.line_distance, test_polygon_id); } friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params) From b70beef1ab48b08ae6be466c1bccf5818e2cb238 Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sun, 9 Jul 2023 00:41:15 +0200 Subject: [PATCH 180/656] Replace custom sprintf formatter with fmt::format --- tests/InfillTest.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/tests/InfillTest.cpp b/tests/InfillTest.cpp index 81e7dd7d71..88355bac18 100644 --- a/tests/InfillTest.cpp +++ b/tests/InfillTest.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -20,16 +21,6 @@ // NOLINTBEGIN(*-magic-numbers) namespace cura { -template -std::string makeName(const std::string& format_string, Ts... args) -{ - // FIXME: once we use spdlog, we can use fmt::format instead, see CURA-8258 - constexpr int buff_size = 1024; - char buff[buff_size]; - std::snprintf(buff, buff_size, format_string.c_str(), args...); - return std::string(buff); -} - coord_t getPatternMultiplier(const EFillMethod& pattern) { switch (pattern) @@ -66,8 +57,7 @@ struct InfillParameters , connect_polygons(connect_polygons) , line_distance(line_distance) { - // FIXME: Once we are using spdlog as logger, we'll also use fmt::format() here, see CURA-8258. - name = makeName("InfillParameters_%d_%d_%d_%lld", static_cast(pattern), static_cast(zig_zagify), static_cast(connect_polygons), line_distance); + name = fmt::format("InfillParameters_{d}_{d}_{d}_{d}", static_cast(pattern), zig_zagify, connect_polygons, line_distance); } }; @@ -99,8 +89,7 @@ class InfillTestParameters , result_lines(std::move(result_lines)) , result_polygons(std::move(result_polygons)) { - // FIXME: Once we are using spdlog as logger, we'll also use fmt::format() here, see CURA-8258. - name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%zu", static_cast(params.pattern), static_cast(params.zig_zagify), static_cast(params.connect_polygons), params.line_distance, test_polygon_id); + name = fmt::format("InfillTestParameters_P{d}_Z{d}_C{d}_L{d}__{d}", static_cast(params.pattern), params.zig_zagify, params.connect_polygons, params.line_distance, test_polygon_id); } friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params) From 46ee7b1154624a122084e71bfc6cc5acb84dfe97 Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sun, 9 Jul 2023 00:48:04 +0200 Subject: [PATCH 181/656] Use correct fmt syntax --- tests/InfillTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/InfillTest.cpp b/tests/InfillTest.cpp index 88355bac18..8d95705934 100644 --- a/tests/InfillTest.cpp +++ b/tests/InfillTest.cpp @@ -57,7 +57,7 @@ struct InfillParameters , connect_polygons(connect_polygons) , line_distance(line_distance) { - name = fmt::format("InfillParameters_{d}_{d}_{d}_{d}", static_cast(pattern), zig_zagify, connect_polygons, line_distance); + name = fmt::format("InfillParameters_{:d}_{:d}_{:d}_{:d}", static_cast(pattern), zig_zagify, connect_polygons, line_distance); } }; @@ -89,7 +89,7 @@ class InfillTestParameters , result_lines(std::move(result_lines)) , result_polygons(std::move(result_polygons)) { - name = fmt::format("InfillTestParameters_P{d}_Z{d}_C{d}_L{d}__{d}", static_cast(params.pattern), params.zig_zagify, params.connect_polygons, params.line_distance, test_polygon_id); + name = fmt::format("InfillTestParameters_P{:d}_Z{:d}_C{:d}_L{:d}__{:d}", static_cast(params.pattern), params.zig_zagify, params.connect_polygons, params.line_distance, test_polygon_id); } friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params) From c5c063eb1062838e4442d68535894e3d5b9d1d27 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 9 Jul 2023 16:38:20 +0200 Subject: [PATCH 182/656] Handshake RPC during plugin creation Implement Handshake RPC during plugin creation A handshake mechanism has been added during plugin creation, ensuring the necessary compatibility and functionality verification before initializing the plugin. This change refactored the PluginProxy class to establish the handshake during the plugin construction and validate it. Adjustments to plugin_metadata structure and exception handling were also necessary. The handshake message conversion logic has been added with simplify_request and handshake_response structures. This action was necessary to ensure the appropriate plugin version is used, improving system reliability and stability. Contributes to CURA-10714 --- include/plugins/converters.h | 47 +++++++++++++++++++++ include/plugins/exception.h | 4 +- include/plugins/metadata.h | 42 ++++--------------- include/plugins/pluginproxy.h | 78 +++++++++++++++++++++++++---------- 4 files changed, 112 insertions(+), 59 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 7339daa2e2..e814c36c27 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -10,8 +10,11 @@ #include #include +#include "plugins/metadata.h" #include "plugins/types.h" +#include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" +#include "cura/plugins/slots/handshake/v0/handshake.pb.h" #include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/postprocess.pb.h" #include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" @@ -20,6 +23,50 @@ namespace cura::plugins { +struct handshake_request +{ + using value_type = slots::handshake::v0::CallRequest; ///< The protobuf message type. + using native_value_type = slot_metadata; ///< The native value type. + + /** + * @brief Converts native data for handshake to a `proto::HandshakeRequest` message. + * + * @param service_name The name of the service. + * @param version_range The version range of the service. + * @return The converted `proto::HandshakeRequest` message. + */ + + value_type operator()(const native_value_type& slot_info) const + { + value_type message{}; + message.set_slot_id(slot_info.slot_id); + message.set_version_range(slot_info.version_range.data()); + return message; + } +}; + +struct handshake_response +{ + using value_type = slots::handshake::v0::CallResponse; ///< The protobuf message type. + using native_value_type = plugin_metadata; ///< The native value type. + + /** + * @brief Converts a `proto::HandshakeResponse` message to native data. + * + * @param message The `proto::HandshakeResponse` message. + * @return The native data. + */ + native_value_type operator()(const value_type& message, std::string_view peer) const + { + return { .slot_version = message.slot_version(), + .plugin_name = message.plugin_name(), + .plugin_version = message.plugin_version(), + .peer = peer, + .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; + } +}; + + struct simplify_request { using value_type = slots::simplify::v0::CallRequest; ///< The protobuf message type. diff --git a/include/plugins/exception.h b/include/plugins/exception.h index 9ef2fdcf07..a5328a3357 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -25,7 +25,7 @@ class ValidatorException : public std::exception ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept : msg_(fmt::format("Failed to validation plugin on Slot '{}'", slot_info.slot_id)){}; ValidatorException(const auto& validator, const slot_metadata& slot_info, const plugin_metadata& plugin_info) noexcept - : msg_(fmt::format("Failed to validate plugin '{}-{}' running at [{}] for slot '{}', slot range '{}' incompatible with plugin slot version '{}'", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, slot_info.version_range, plugin_info.slot_version)) + : msg_(fmt::format("Failed to validate plugin '{}-{}' running at [{}] for slot '{}', slot range '{}' incompatible with plugin slot version '{}'", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info.slot_id, slot_info.version_range, plugin_info.slot_version)) { } @@ -43,7 +43,7 @@ class RemoteException : public std::exception RemoteException(const slot_metadata& slot_info, std::string_view error_msg) noexcept : msg_(fmt::format("Remote exception on Slot '{}': {}", slot_info.slot_id, error_msg)){}; RemoteException(const slot_metadata& slot_info, const plugin_metadata& plugin_info, std::string_view error_msg) noexcept - : msg_(fmt::format("Remote exception for plugin '{}-{}' running at [{}] for slot '{}': {}", plugin_info.name, plugin_info.version, plugin_info.peer, slot_info.slot_id, error_msg)) + : msg_(fmt::format("Remote exception for plugin '{}-{}' running at [{}] for slot '{}': {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info.slot_id, error_msg)) { } diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 461fe9ea53..511ca734fd 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -4,6 +4,7 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_METADATA_H #define CURAENGINE_INCLUDE_PLUGINS_METADATA_H +#include #include #include @@ -14,43 +15,14 @@ namespace cura::plugins { + struct plugin_metadata { - std::string name; // cura-plugin-name (optional) - std::string version; // cura-plugin-version (optional) - std::string peer; - std::string slot_version; // cura-slot-version (required) - - explicit plugin_metadata(const grpc::ClientContext& client_context) - { - const auto& metadata = client_context.GetServerInitialMetadata(); - if (auto it = metadata.find("cura-slot-version"); it != metadata.end()) - { - slot_version = std::string{ it->second.data(), it->second.size() }; - } - else - { - spdlog::error("'cura-slot-version' RPC metadata not set"); - throw std::runtime_error("'cura-slot-version' RPC metadata not set"); - } - if (auto it = metadata.find("cura-plugin-name"); it != metadata.end()) - { - name = std::string{ it->second.data(), it->second.size() }; - } - else - { - spdlog::warn("'cura-plugin-name' RPC metadata not set"); - } - if (auto it = metadata.find("cura-plugin-version"); it != metadata.end()) - { - version = std::string{ it->second.data(), it->second.size() }; - } - else - { - spdlog::warn("'cura-plugin-version' RPC metadata not set"); - } - peer = client_context.peer(); - } + std::string_view slot_version; + std::string_view plugin_name; + std::string_view plugin_version; + std::string_view peer; + std::set broadcast_subscriptions; }; struct slot_metadata diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index cd79b89046..b064adb3b0 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -24,6 +24,7 @@ #include "utils/format/thread_id.h" #include "utils/types/generic.h" +#include "cura/plugins/slots/handshake/v0/handshake.pb.h" #include "cura/plugins/v0/slot_id.pb.h" namespace cura::plugins @@ -81,7 +82,59 @@ class PluginProxy * @throws std::runtime_error if the plugin fails validation or communication errors occur. */ constexpr PluginProxy() = default; - explicit PluginProxy(std::shared_ptr channel) : stub_(channel){}; + + explicit PluginProxy(std::shared_ptr channel) : stub_(channel) + { + // Connect to the plugin and exchange a handshake + agrpc::GrpcContext grpc_context; + grpc::Status status; + slots::handshake::v0::HandshakeService::Stub handshake_stub(channel); + + boost::asio::co_spawn( + grpc_context, + [this, &status, &grpc_context, &handshake_stub]() -> boost::asio::awaitable + { + using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context); + + // Construct request + handshake_request handshake_req; + handshake_request::value_type request{ handshake_req(slot_info_) }; + + // Make unary request + handshake_response::value_type response; + status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); + handshake_response handshake_rsp; + plugin_info_ = handshake_rsp(response, client_context.peer()); + valid_ = validator_type{ slot_info_, plugin_info_.value() }; + if (valid_) + { + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); + } + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_.has_value()) + { + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + } + throw exceptions::RemoteException(slot_info_, status.error_message()); + } + + if (! valid_) + { + if (plugin_info_.has_value()) + { + throw exceptions::ValidatorException(valid_, slot_info_, plugin_info_.value()); + } + throw exceptions::ValidatorException(valid_, slot_info_); + } + }; + constexpr PluginProxy(const PluginProxy&) = default; constexpr PluginProxy(PluginProxy&&) noexcept = default; constexpr PluginProxy& operator=(const PluginProxy& other) @@ -91,6 +144,7 @@ class PluginProxy valid_ = other.valid_; stub_ = other.stub_; plugin_info_ = other.plugin_info_; + slot_info_ = other.slot_info_; } return *this; } @@ -101,6 +155,7 @@ class PluginProxy valid_ = std::move(other.valid_); stub_ = std::move(other.stub_); plugin_info_ = std::move(other.plugin_info_); + slot_info_ = std::move(other.slot_info_); } return *this; } @@ -140,16 +195,6 @@ class PluginProxy rsp_msg_type response; status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); ret_value = rsp_(response); - - if (! plugin_info_.has_value()) - { - plugin_info_ = plugin_metadata{ client_context }; - valid_ = validator_type{ slot_info_, plugin_info_.value() }; - if (valid_) - { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->name, plugin_info_->version, plugin_info_->peer, slot_info_.slot_id); - } - } }, boost::asio::detached); grpc_context.run(); @@ -162,16 +207,6 @@ class PluginProxy } throw exceptions::RemoteException(slot_info_, status.error_message()); } - - if (! valid_) - { - if (plugin_info_.has_value()) - { - throw exceptions::ValidatorException(valid_, slot_info_, plugin_info_.value()); - } - throw exceptions::ValidatorException(valid_, slot_info_); - } - return ret_value; } @@ -184,7 +219,6 @@ class PluginProxy client_context.AddMetadata("cura-engine-uuid", slot_info_.engine_uuid.data()); client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); } - }; } // namespace cura::plugins From 7ad7d9f6fa9aa1eb574ce7e98d18b9557d9c75fe Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 9 Jul 2023 16:47:49 +0200 Subject: [PATCH 183/656] Refactored co_spawn lambda in pluginproxy.h Refactored the lambda function present in co_spawn calls located in methods of pluginproxy.h. This change intends to increase readability and maintenance. The lambda implementation was moved into private member functions 'handshakeCall' and 'modifyCall'. This enhances code organization allowing for easy modification and reuse in the future. Contribute to CURA-10714 --- include/plugins/pluginproxy.h | 81 ++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index b064adb3b0..1128f8202d 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -91,29 +91,7 @@ class PluginProxy slots::handshake::v0::HandshakeService::Stub handshake_stub(channel); boost::asio::co_spawn( - grpc_context, - [this, &status, &grpc_context, &handshake_stub]() -> boost::asio::awaitable - { - using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context); - - // Construct request - handshake_request handshake_req; - handshake_request::value_type request{ handshake_req(slot_info_) }; - - // Make unary request - handshake_response::value_type response; - status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); - handshake_response handshake_rsp; - plugin_info_ = handshake_rsp(response, client_context.peer()); - valid_ = validator_type{ slot_info_, plugin_info_.value() }; - if (valid_) - { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); - } - }, - boost::asio::detached); + grpc_context, [this, &grpc_context, &status, &handshake_stub]() { return this->handshakeCall(grpc_context, status, handshake_stub); }, boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes @@ -181,22 +159,7 @@ class PluginProxy grpc::Status status; boost::asio::co_spawn( - grpc_context, - [this, &status, &grpc_context, &ret_value, &args...]() -> boost::asio::awaitable - { - using RPC = agrpc::RPC<&stub_t::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context); - - // Construct request - auto request{ req_(std::forward(args)...) }; - - // Make unary request - rsp_msg_type response; - status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = rsp_(response); - }, - boost::asio::detached); + grpc_context, [this, &grpc_context, &status, &ret_value, &args...]() { return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); }, boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes @@ -210,6 +173,46 @@ class PluginProxy return ret_value; } +private: + boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, slots::handshake::v0::HandshakeService::Stub& handshake_stub) + { + using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context); + + // Construct request + handshake_request handshake_req; + handshake_request::value_type request{ handshake_req(slot_info_) }; + + // Make unary request + handshake_response::value_type response; + status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); + handshake_response handshake_rsp; + plugin_info_ = handshake_rsp(response, client_context.peer()); + valid_ = validator_type{ slot_info_, plugin_info_.value() }; + if (valid_) + { + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); + } + co_return; + } + + boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) + { + using RPC = agrpc::RPC<&stub_t::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context); + + // Construct request + auto request{ req_(std::forward(args)...) }; + + // Make unary request + rsp_msg_type response; + status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = rsp_(response); + co_return; + } + void prep_client_context(grpc::ClientContext& client_context, std::chrono::milliseconds timeout = std::chrono::milliseconds(500)) { // Set time-out From cd7152b523821ada713a857ec60ede174668e894 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 9 Jul 2023 16:49:55 +0200 Subject: [PATCH 184/656] Moved private members in pluginproxy.h This refactoring aims to make the public API more clear and understandable by moving private members to the bottom of the file. With this change, it will be easier for the developers to identify and understand the public methods and attributes of the class at a first glance, improving the maintainability and readability of the code. Contributes to CURA-10714 --- include/plugins/pluginproxy.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 1128f8202d..8d8310dafb 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -59,17 +59,6 @@ class PluginProxy using stub_t = Stub; -private: - validator_type valid_{}; ///< The validator object for plugin validation. - req_converter_type req_{}; ///< The request converter object. - rsp_converter_type rsp_{}; ///< The response converter object. - - ranges::semiregular_box stub_; ///< The gRPC stub for communication. - - slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; - std::optional plugin_info_{ std::nullopt }; ///< The plugin info object. - -public: /** * @brief Constructs a PluginProxy object. * @@ -174,6 +163,15 @@ class PluginProxy } private: + validator_type valid_{}; ///< The validator object for plugin validation. + req_converter_type req_{}; ///< The request converter object. + rsp_converter_type rsp_{}; ///< The response converter object. + + ranges::semiregular_box stub_; ///< The gRPC stub for communication. + + slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; + std::optional plugin_info_{ std::nullopt }; ///< The plugin info object. + boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, slots::handshake::v0::HandshakeService::Stub& handshake_stub) { using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; From fc5d7f0def592dd1a9a87f20cfd39386d2fe93e2 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 9 Jul 2023 16:56:19 +0200 Subject: [PATCH 185/656] Add documentation CURA-10714 --- include/plugins/pluginproxy.h | 60 +++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 8d8310dafb..f1702bc3d6 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -31,17 +31,17 @@ namespace cura::plugins { /** - * @brief A class template representing a proxy for a plugin. + * @brief A plugin proxy class template. * - * The PluginProxy class template facilitates communication with plugins by providing - * an interface for sending requests and receiving responses. It uses gRPC for communication. + * Template arguments are: + * SlotID - plugin slot ID + * SlotVersionRng - plugin version range + * Stub - process stub type + * ValidatorTp - validator type + * RequestTp - gRPC convertible request type, + * ResponseTp - gRPC convertible response type. * - * @tparam Slot The plugin slot ID. - * @tparam Validator The type used for validating the plugin. - * @tparam Stub The process stub type. - * @tparam Prepare The prepare type. - * @tparam Request The gRPC convertible request type. - * @tparam Response The gRPC convertible response type. + * Class provides methods for validating the plugin, making requests and processing responses. */ template class PluginProxy @@ -129,15 +129,14 @@ class PluginProxy ~PluginProxy() = default; /** - * @brief Executes the plugin operation. + * @brief Executes to plugin Modify operation. * - * This operator allows the PluginProxy object to be invoked as a callable, which sends - * a request to the plugin and waits for the response. The response is converted using - * the response_converter_ object, and the converted value is returned. + * As part of this operation, a request is sent to the plugin + * and the returned response is processed. * - * @tparam Args The argument types for the plugin request. - * @param args The arguments for the plugin request. - * @return The converted response value. + * @tparam Args - argument types for the plugin request + * @param args - arguments for the plugin request + * @return The converted response value from plugin. * * @throws std::runtime_error if communication with the plugin fails. */ @@ -164,14 +163,25 @@ class PluginProxy private: validator_type valid_{}; ///< The validator object for plugin validation. - req_converter_type req_{}; ///< The request converter object. - rsp_converter_type rsp_{}; ///< The response converter object. + req_converter_type req_{}; ///< The Modify request converter object. + rsp_converter_type rsp_{}; ///< The Modify response converter object. - ranges::semiregular_box stub_; ///< The gRPC stub for communication. + ranges::semiregular_box stub_; ///< The gRPC Modify stub for communication. - slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; - std::optional plugin_info_{ std::nullopt }; ///< The plugin info object. + slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. + std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake + /** + * @brief Executes the modifyCall operation with the plugin. + * + * Sends a request to the plugin and saves the response. + * + * @param grpc_context - The gRPC context to use for the call + * @param status - Status of the gRPC call which gets updated in this method + * @param ret_value - Reference to the value in which response to be stored + * @param args - Request arguments + * @return A boost::asio::awaitable indicating completion of the operation + */ boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, slots::handshake::v0::HandshakeService::Stub& handshake_stub) { using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; @@ -211,6 +221,14 @@ class PluginProxy co_return; } + /** + * @brief Prepares client_context for the remote call. + * + * Sets timeout for the call and adds metadata to context. + * + * @param client_context - Client context to prepare + * @param timeout - Call timeout duration (optional, default = 500ms) + */ void prep_client_context(grpc::ClientContext& client_context, std::chrono::milliseconds timeout = std::chrono::milliseconds(500)) { // Set time-out From 078fa6189a283b9fa93c8b76ad4e9207dff03b7d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 9 Jul 2023 16:56:49 +0200 Subject: [PATCH 186/656] Add documentation CURA-10714 --- include/plugins/pluginproxy.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index f1702bc3d6..fdc530d568 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -205,6 +205,17 @@ class PluginProxy co_return; } + /** + * @brief Executes the modifyCall operation with the plugin. + * + * Sends a request to the plugin and saves the response. + * + * @param grpc_context - The gRPC context to use for the call + * @param status - Status of the gRPC call which gets updated in this method + * @param ret_value - Reference to the value in which response to be stored + * @param args - Request arguments + * @return A boost::asio::awaitable indicating completion of the operation + */ boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) { using RPC = agrpc::RPC<&stub_t::PrepareAsyncCall>; From e88b49327d5122aca276c275e547e3e274051e09 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 07:56:20 +0200 Subject: [PATCH 187/656] Use 10618 branch of gRPC defs Contributes to CURA-10618 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index ec64e0c14b..4c59b90b9a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -92,7 +92,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/cura_10475") # TODO: point to `testing` once the CURA-10475 from libArcus is main + self.requires("arcus/(latest)@ultimaker/cura_10618") # TODO: point to `testing` once the CURA-10475 from libArcus is main self.requires("clipper/6.4.2") self.requires("boost/1.81.0") self.requires("rapidjson/1.1.0") From a157b07911ceeb047afed538db1a2cec1e09adc1 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 07:58:45 +0200 Subject: [PATCH 188/656] Revert "Use 10618 branch of gRPC defs" This reverts commit e88b49327d5122aca276c275e547e3e274051e09. --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 4c59b90b9a..ec64e0c14b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -92,7 +92,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/cura_10618") # TODO: point to `testing` once the CURA-10475 from libArcus is main + self.requires("arcus/(latest)@ultimaker/cura_10475") # TODO: point to `testing` once the CURA-10475 from libArcus is main self.requires("clipper/6.4.2") self.requires("boost/1.81.0") self.requires("rapidjson/1.1.0") From abc0da8790072637ec25d690578ee4b4d006ea2d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 07:59:21 +0200 Subject: [PATCH 189/656] Use 10618 for gRPC defs CURA-10618 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index ec64e0c14b..230913ef9e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10714") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10618") def generate(self): deps = CMakeDeps(self) From dd1ef6c1e1e2ce246b6919e11d745f6a62f3ecfc Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 08:35:15 +0200 Subject: [PATCH 190/656] Renamed stub to modify_stub To better differentiate between types of plugin operations CURA-10618 --- include/plugins/pluginproxy.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index fdc530d568..e5dff24d60 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -57,7 +57,7 @@ class PluginProxy using req_converter_type = RequestTp; using rsp_converter_type = ResponseTp; - using stub_t = Stub; + using modify_stub_t = Stub; /** * @brief Constructs a PluginProxy object. @@ -72,7 +72,7 @@ class PluginProxy */ constexpr PluginProxy() = default; - explicit PluginProxy(std::shared_ptr channel) : stub_(channel) + explicit PluginProxy(std::shared_ptr channel) : modify_stub_(channel) { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -109,7 +109,7 @@ class PluginProxy if (this != &other) { valid_ = other.valid_; - stub_ = other.stub_; + modify_stub_ = other.modify_stub_; plugin_info_ = other.plugin_info_; slot_info_ = other.slot_info_; } @@ -120,7 +120,7 @@ class PluginProxy if (this != &other) { valid_ = std::move(other.valid_); - stub_ = std::move(other.stub_); + modify_stub_ = std::move(other.modify_stub_); plugin_info_ = std::move(other.plugin_info_); slot_info_ = std::move(other.slot_info_); } @@ -166,7 +166,7 @@ class PluginProxy req_converter_type req_{}; ///< The Modify request converter object. rsp_converter_type rsp_{}; ///< The Modify response converter object. - ranges::semiregular_box stub_; ///< The gRPC Modify stub for communication. + ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake @@ -218,7 +218,7 @@ class PluginProxy */ boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) { - using RPC = agrpc::RPC<&stub_t::PrepareAsyncCall>; + using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; prep_client_context(client_context); @@ -227,7 +227,7 @@ class PluginProxy // Make unary request rsp_msg_type response; - status = co_await RPC::request(grpc_context, stub_, client_context, request, response, boost::asio::use_awaitable); + status = co_await RPC::request(grpc_context, modify_stub_, client_context, request, response, boost::asio::use_awaitable); ret_value = rsp_(response); co_return; } From 73466b9cc0927d1624598ecdfefeae9ed15ec495 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 08:39:03 +0200 Subject: [PATCH 191/656] Add Broadcast stub Needed to handle the different broadcast RPC's Contributes to CURA-10618 --- include/plugins/pluginproxy.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index e5dff24d60..ab62a19709 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -24,6 +24,9 @@ #include "utils/format/thread_id.h" #include "utils/types/generic.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" +#include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.pb.h" #include "cura/plugins/v0/slot_id.pb.h" @@ -58,6 +61,7 @@ class PluginProxy using rsp_converter_type = ResponseTp; using modify_stub_t = Stub; + using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; /** * @brief Constructs a PluginProxy object. @@ -72,7 +76,7 @@ class PluginProxy */ constexpr PluginProxy() = default; - explicit PluginProxy(std::shared_ptr channel) : modify_stub_(channel) + explicit PluginProxy(std::shared_ptr channel) : modify_stub_(channel), broadcast_stub_(channel) { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -110,6 +114,7 @@ class PluginProxy { valid_ = other.valid_; modify_stub_ = other.modify_stub_; + broadcast_stub_ = other.broadcast_stub_; plugin_info_ = other.plugin_info_; slot_info_ = other.slot_info_; } @@ -121,6 +126,7 @@ class PluginProxy { valid_ = std::move(other.valid_); modify_stub_ = std::move(other.modify_stub_); + broadcast_stub_ = std::move(other.broadcast_stub_); plugin_info_ = std::move(other.plugin_info_); slot_info_ = std::move(other.slot_info_); } @@ -167,6 +173,7 @@ class PluginProxy rsp_converter_type rsp_{}; ///< The Modify response converter object. ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. + ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake From 340ed08fd5e8b4cd6f570226cfa82931e7902a31 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 08:53:37 +0200 Subject: [PATCH 192/656] Add converters for the BroadcastService - The Generic Empty converter - SettingsBroadcast from the Cura Slice message CURA-10618 --- include/plugins/converters.h | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index e814c36c27..29d961ee4c 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -7,12 +7,15 @@ #include #include +#include #include #include #include "plugins/metadata.h" #include "plugins/types.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.pb.h" #include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" @@ -20,9 +23,79 @@ #include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/simplify.pb.h" +#include "Cura.pb.h" + namespace cura::plugins { + +struct empty +{ + using value_type = google::protobuf::Empty; ///< The protobuf message type. + using native_value_type = std::nullptr_t; ///< The native value type. + + value_type operator()() const + { + return {}; + } + + constexpr native_value_type operator()(const value_type&) const + { + return nullptr; + } +}; + +struct broadcast_settings_request +{ + using value_type = slots::broadcast::v0::BroadcastServiceSettingsRequest; ///< The protobuf message type. + using native_value_type = cura::proto::Slice; ///< The native value type. + + /** + * @brief Converts native data for broadcasting to a `proto::BroadcastServiceSettingsRequest` message. + * + * @param key The key of the setting to be broadcasted. + * @param value The value of the setting to be broadcasted. + * @return The converted `proto::BroadcastServiceSettingsRequest` message. + */ + value_type operator()(const native_value_type& slice_message) const + { + value_type message{}; + auto* global_settings = message.mutable_global_settings()->mutable_settings(); + for (const auto& setting : slice_message.global_settings().settings()) + { + global_settings->emplace(setting.name(), setting.value()); + } + + auto* extruders_settings = message.mutable_extruder_settings(); + for (const auto& extruder : slice_message.extruders()) + { + auto* settings = extruders_settings->Add()->mutable_settings(); + for (const auto& setting : extruder.settings().settings()) + { + settings->emplace(setting.name(), setting.value()); + } + } + + auto* object_settings = message.mutable_object_settings(); + for (const auto& object : slice_message.object_lists()) + { + auto* settings = object_settings->Add()->mutable_settings(); + for (const auto& setting : object.settings()) + { + settings->emplace(setting.name(), setting.value()); + } + } + + auto* limit_to_extruder = message.mutable_limit_to_extruder(); + for (const auto& setting_extruder : slice_message.limit_to_extruder()) + { + limit_to_extruder->emplace(setting_extruder.name(), setting_extruder.extruder()); + } + return message; + } +}; + + struct handshake_request { using value_type = slots::handshake::v0::CallRequest; ///< The protobuf message type. From 2e8157fc1e85bbd2a2da6c2b55e8deb128b34d36 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 08:56:57 +0200 Subject: [PATCH 193/656] Add the plumbing for the Broadcast functionality This commit introduces the broadcasting capability within plugins. The `broadcast` method was added in slots.h, slotproxy.h and pluginproxy.h files. This enables broadcasting of a message across multiple channels, improving the communication aspects within the plugins. Along with this, some changes in the imports were made allowing a better organization of used modules. Contributes to CURA-10618 --- include/plugins/pluginproxy.h | 10 ++++++++-- include/plugins/slotproxy.h | 12 +++++++++++- include/plugins/slots.h | 7 +++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index ab62a19709..5988fe6ab2 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -4,6 +4,8 @@ #ifndef PLUGINS_PLUGINPROXY_H #define PLUGINS_PLUGINPROXY_H +#include +#include #include #include @@ -12,16 +14,15 @@ #include #include #include -#include #include #include #include -#include #include "Application.h" #include "plugins/exception.h" #include "plugins/metadata.h" #include "utils/format/thread_id.h" +#include "utils/types/char_range_literal.h" #include "utils/types/generic.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" @@ -167,6 +168,11 @@ class PluginProxy return ret_value; } + template + void broadcast(auto&&... args) + { + } + private: validator_type valid_{}; ///< The validator object for plugin validation. req_converter_type req_{}; ///< The Modify request converter object. diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index d3ac43136d..300b536ea1 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,14 +4,15 @@ #ifndef PLUGINS_SLOTPROXY_H #define PLUGINS_SLOTPROXY_H -#include #include #include #include #include +#include #include +#include "utils/types/char_range_literal.h" #include "plugins/converters.h" #include "plugins/pluginproxy.h" #include "plugins/types.h" @@ -78,6 +79,15 @@ class SlotProxy } return std::invoke(default_process, std::forward(args)...); } + + template + void broadcast(auto&&...args) + { + if (plugin_.has_value()) + { + plugin_.value().template broadcast(std::forward(args)...); + } + } }; } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 7ff29b8a04..d83e821251 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -100,6 +100,13 @@ class Registry, Unit> : public Registry get_type().proxy = Tp{ std::forward(std::move(plugin)) }; } + template + void broadcast(auto&&... args) + { + value_.proxy.template broadcast(std::forward(args)...); + Base::value_.proxy.template broadcast(std::forward(args)...); + } + protected: template constexpr Unit& get_type() From ca54d6ac1727a21fbf9cdc67faada799a2436051 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 10:51:03 +0200 Subject: [PATCH 194/656] Add Broadcasting logic A broadcast can now be made for a specific RPC. Overloading of the different Broadcast RPCs is done in the Broadcast header. Where a `broadcast_message_factory` and `broadcast_factory` need to be created for each RPC. Contribute to CURA-10618 --- include/plugins/broadcasts.h | 29 +++++++++++++++++++++++++++++ include/plugins/pluginproxy.h | 33 +++++++++++++++++++++++++++++++++ include/plugins/slotproxy.h | 2 +- include/plugins/slots.h | 1 + include/plugins/types.h | 1 - include/utils/types/generic.h | 24 ++++++++++++++++++++---- 6 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 include/plugins/broadcasts.h diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h new file mode 100644 index 0000000000..a4de7567c4 --- /dev/null +++ b/include/plugins/broadcasts.h @@ -0,0 +1,29 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PLUGINS_BROADCAST_H +#define PLUGINS_BROADCAST_H + +#include "plugins/converters.h" +#include "utils/types/char_range_literal.h" +#include "utils/types/generic.h" + +namespace cura::plugins::details +{ + +template +requires utils::is_broadcast_channel_v constexpr auto broadcast_message_factory(auto&&... args) +{ + return broadcast_settings_request{}(std::forward(args)...); +}; + + +template +requires utils::is_broadcast_channel_v constexpr auto broadcast_factory() +{ + return agrpc::RPC<&Stub::PrepareAsyncBroadcastSettings>{}; +} + +} // namespace cura::plugins::details + +#endif // PLUGINS_BROADCAST_H \ No newline at end of file diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 5988fe6ab2..248be725ed 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -19,6 +19,7 @@ #include #include "Application.h" +#include "plugins/broadcasts.h" #include "plugins/exception.h" #include "plugins/metadata.h" #include "utils/format/thread_id.h" @@ -171,6 +172,25 @@ class PluginProxy template void broadcast(auto&&... args) { + if (! plugin_info_->broadcast_subscriptions.contains(BroadcastChannel.value)) + { + return; + } + agrpc::GrpcContext grpc_context; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, [this, &grpc_context, &status, &args...]() { return this->broadcastCall(grpc_context, status, std::forward(args)...); }, boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_.has_value()) + { + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + } + throw exceptions::RemoteException(slot_info_, status.error_message()); + } } private: @@ -245,6 +265,19 @@ class PluginProxy co_return; } + template + boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) + { + grpc::ClientContext client_context{}; + prep_client_context(client_context); + + auto broadcaster { details::broadcast_factory() }; + auto request = details::broadcast_message_factory(std::forward(args)...); + auto response = google::protobuf::Empty{}; + status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); + co_return; + } + /** * @brief Prepares client_context for the remote call. * diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 300b536ea1..720922857b 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -12,11 +12,11 @@ #include #include -#include "utils/types/char_range_literal.h" #include "plugins/converters.h" #include "plugins/pluginproxy.h" #include "plugins/types.h" #include "plugins/validator.h" +#include "utils/types/char_range_literal.h" namespace cura::plugins { diff --git a/include/plugins/slots.h b/include/plugins/slots.h index d83e821251..13f259fa2a 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -13,6 +13,7 @@ #include "plugins/validator.h" #include "utils/IntPoint.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed +#include "utils/types/char_range_literal.h" #include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" diff --git a/include/plugins/types.h b/include/plugins/types.h index 1ddd93c371..c922de4c67 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -11,7 +11,6 @@ #include #include "utils/IntPoint.h" -#include "utils/types/generic.h" #include "utils/polygon.h" #include "cura/plugins/v0/slot_id.pb.h" diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 402eeba9c2..59512a43c8 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -11,6 +11,8 @@ #include #include +#include "utils/types/char_range_literal.h" + namespace cura::utils { template @@ -27,6 +29,23 @@ concept grpc_convertable = requires(T value) requires ranges::semiregular; }; +template +class is_broadcast_channel +{ + inline static constexpr bool value_() noexcept + { + constexpr std::string_view t1{ T1.value }; + constexpr std::string_view t2{ T2.value }; + return t1 == t2; + } + +public: + inline static constexpr bool value = value_(); +}; + +template +inline constexpr bool is_broadcast_channel_v = is_broadcast_channel::value; + #ifdef OLDER_APPLE_CLANG // std::integral and std::floating_point are not implemented in older Apple Clang versions < 13 @@ -51,10 +70,7 @@ concept integral = std::is_same_v; template -concept floating_point = - std::is_same_v || - std::is_same_v || - std::is_same_v; +concept floating_point = std::is_same_v || std::is_same_v || std::is_same_v; #else template concept integral = std::integral; From 1f6446331746086cc260d5b2fec73b0523a55230 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 10:52:21 +0200 Subject: [PATCH 195/656] Create the `BroadcastSettings` slot CURA-10618 --- src/communication/ArcusCommunication.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 14fd6cbaf2..467ad53c99 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -532,6 +532,9 @@ void ArcusCommunication::sliceNext() private_data->readGlobalSettingsMessage(slice_message->global_settings()); private_data->readExtruderSettingsMessage(slice_message->extruders()); + + // Broadcast the settings to the plugins + slots::instance().broadcast<"BroadcastSettings">(*slice_message); const size_t extruder_count = slice.scene.extruders.size(); // For each setting, register what extruder it should be obtained from (if this is limited to an extruder). From 1d6319ce3dd620ba8e6eec935bd7fec52b0e1261 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 11:26:36 +0200 Subject: [PATCH 196/656] Log plugin subscriptions CURA-10618 --- include/plugins/pluginproxy.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 248be725ed..b3392eaf28 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -234,6 +235,10 @@ class PluginProxy if (valid_) { spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); + if (! plugin_info_->broadcast_subscriptions.empty()) + { + spdlog::info("Subscriping plugin '{}' to the following broadcasts {}", plugin_info_->plugin_name, plugin_info_->broadcast_subscriptions); + } } co_return; } From a410277d05c90aabbf7255f6ae0d15619cf7da46 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 10 Jul 2023 11:53:47 +0200 Subject: [PATCH 197/656] Fixed typo CURA-10618 --- include/plugins/pluginproxy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index b3392eaf28..324346b91a 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -237,7 +237,7 @@ class PluginProxy spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); if (! plugin_info_->broadcast_subscriptions.empty()) { - spdlog::info("Subscriping plugin '{}' to the following broadcasts {}", plugin_info_->plugin_name, plugin_info_->broadcast_subscriptions); + spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info_->plugin_name, plugin_info_->broadcast_subscriptions); } } co_return; From 4ca48a5dbf623ed10ff898ec1d8a6822583e19e7 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 11 Jul 2023 08:05:30 +0200 Subject: [PATCH 198/656] Better error handling on miscommunications In the previous implementation, there were potential issues in handling errors especially when an external service miscommunicated with the plugin. This update includes the plugin_info in the handshakeCall function to address this flaw. Now, in case the service fails to respond properly, the system will provide meaningful error messages and help the user understand the source of the problem. Contribute to CURA-10625 --- include/plugins/pluginproxy.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 324346b91a..eb2765001e 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -85,20 +85,20 @@ class PluginProxy agrpc::GrpcContext grpc_context; grpc::Status status; slots::handshake::v0::HandshakeService::Stub handshake_stub(channel); + plugin_metadata plugin_info; boost::asio::co_spawn( - grpc_context, [this, &grpc_context, &status, &handshake_stub]() { return this->handshakeCall(grpc_context, status, handshake_stub); }, boost::asio::detached); + grpc_context, [this, &grpc_context, &status, &plugin_info, &handshake_stub]() { return this->handshakeCall(grpc_context, status, plugin_info, handshake_stub); }, boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes { - if (plugin_info_.has_value()) - { - throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); - } throw exceptions::RemoteException(slot_info_, status.error_message()); } - + if (! plugin_info.plugin_name.empty() && !plugin_info.slot_version.empty()) + { + plugin_info_ = plugin_info; + } if (! valid_) { if (plugin_info_.has_value()) @@ -216,7 +216,7 @@ class PluginProxy * @param args - Request arguments * @return A boost::asio::awaitable indicating completion of the operation */ - boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, slots::handshake::v0::HandshakeService::Stub& handshake_stub) + boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, plugin_metadata plugin_info, slots::handshake::v0::HandshakeService::Stub& handshake_stub) { using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; grpc::ClientContext client_context{}; @@ -230,14 +230,14 @@ class PluginProxy handshake_response::value_type response; status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); handshake_response handshake_rsp; - plugin_info_ = handshake_rsp(response, client_context.peer()); + plugin_info = handshake_rsp(response, client_context.peer()); valid_ = validator_type{ slot_info_, plugin_info_.value() }; if (valid_) { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); - if (! plugin_info_->broadcast_subscriptions.empty()) + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); + if (! plugin_info.broadcast_subscriptions.empty()) { - spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info_->plugin_name, plugin_info_->broadcast_subscriptions); + spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); } } co_return; From d7016a4b40bb11aee2c64be86d7efb5f8b43ff46 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 12 Jul 2023 15:36:09 +0200 Subject: [PATCH 199/656] Add clang-format and clang-linting to push/PR actions. part of CURA-10732 --- .github/workflows/lint-formatter.yml | 46 +++++++++++++ .github/workflows/lint-poster.yml | 81 +++++++++++++++++++++++ .github/workflows/lint-tidier.yml | 65 ++++++++++++++++++ .github/workflows/requirements-linter.txt | 1 + 4 files changed, 193 insertions(+) create mode 100644 .github/workflows/lint-formatter.yml create mode 100644 .github/workflows/lint-poster.yml create mode 100644 .github/workflows/lint-tidier.yml create mode 100644 .github/workflows/requirements-linter.txt diff --git a/.github/workflows/lint-formatter.yml b/.github/workflows/lint-formatter.yml new file mode 100644 index 0000000000..9420c78849 --- /dev/null +++ b/.github/workflows/lint-formatter.yml @@ -0,0 +1,46 @@ +name: lint-formatter + +on: + workflow_dispatch: + + push: + paths: + - 'include/**/*.h*' + - 'src/**/*.c*' + +jobs: + lint-formatter-job: + name: Auto-apply clang-format + + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - uses: technote-space/get-diff-action@v6 + with: + PATTERNS: | + include/**/*.h* + src/**/*.c* + + - name: Setup Python and pip + if: env.GIT_DIFF && !env.MATCHED_FILES # If nothing happens with python and/or pip after, the clean-up crashes. + uses: actions/setup-python@v4 + with: + python-version: 3.11.x + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-linter.txt + + - name: Install Python requirements for runner + if: env.GIT_DIFF && !env.MATCHED_FILES + run: pip install -r .github/workflows/requirements-linter.txt + + - name: Format file + if: env.GIT_DIFF && !env.MATCHED_FILES + run: | + clang-format -i ${{ env.GIT_DIFF_FILTERED }} + + - uses: stefanzweifel/git-auto-commit-action@v4 + if: env.GIT_DIFF && !env.MATCHED_FILES + with: + commit_message: "Applied clang-format." diff --git a/.github/workflows/lint-poster.yml b/.github/workflows/lint-poster.yml new file mode 100644 index 0000000000..b20c161a87 --- /dev/null +++ b/.github/workflows/lint-poster.yml @@ -0,0 +1,81 @@ +name: lint-poster + +on: + workflow_run: + workflows: ["lint-tidier"] + types: [completed] + +jobs: + lint-poster-job: + # Trigger the job only if the previous (insecure) workflow completed successfully + if: ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + steps: + - name: Download analysis results + uses: actions/github-script@v3.1.0 + with: + script: | + let artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + let matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "linter-result" + })[0]; + let download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: "zip", + }); + let fs = require("fs"); + fs.writeFileSync("${{github.workspace}}/linter-result.zip", Buffer.from(download.data)); + + - name: Set environment variables + run: | + mkdir linter-result + unzip linter-result.zip -d linter-result + echo "pr_id=$(cat linter-result/pr-id.txt)" >> $GITHUB_ENV + echo "pr_head_repo=$(cat linter-result/pr-head-repo.txt)" >> $GITHUB_ENV + echo "pr_head_ref=$(cat linter-result/pr-head-ref.txt)" >> $GITHUB_ENV + + - uses: actions/checkout@v3 + with: + repository: ${{ env.pr_head_repo }} + ref: ${{ env.pr_head_ref }} + persist-credentials: false + + - name: Redownload analysis results + uses: actions/github-script@v3.1.0 + with: + script: | + let artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + let matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "linter-result" + })[0]; + let download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: "zip", + }); + let fs = require("fs"); + fs.writeFileSync("${{github.workspace}}/linter-result.zip", Buffer.from(download.data)); + + - name: Extract analysis results + run: | + mkdir linter-result + unzip linter-result.zip -d linter-result + + - name: Run clang-tidy-pr-comments action + uses: platisd/clang-tidy-pr-comments@bc0bb7da034a8317d54e7fe1e819159002f4cc40 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + clang_tidy_fixes: linter-result/fixes.yml + pull_request_id: ${{ env.pr_id }} + request_changes: true diff --git a/.github/workflows/lint-tidier.yml b/.github/workflows/lint-tidier.yml new file mode 100644 index 0000000000..003b6ebc9e --- /dev/null +++ b/.github/workflows/lint-tidier.yml @@ -0,0 +1,65 @@ +name: lint-tidier + +on: + workflow_dispatch: + + pull_request: + paths: + - 'include/**/*.h*' + - 'src/**/*.c*' + +jobs: + lint-tidier-job: + name: Auto-apply clang-tidy + + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 2 + + - uses: technote-space/get-diff-action@v6 + with: + PATTERNS: | + include/**/*.h* + src/**/*.c* + + - name: Setup Python and pip + if: env.GIT_DIFF && !env.MATCHED_FILES # If nothing happens with python and/or pip after, the clean-up crashes. + uses: actions/setup-python@v4 + with: + python-version: 3.11.x + cache: "pip" + cache-dependency-path: .github/workflows/requirements-linter.txt + + - name: Install Python requirements for runner + if: env.GIT_DIFF && !env.MATCHED_FILES + run: pip install -r .github/workflows/requirements-linter.txt + + - name: Create results directory + run: mkdir linter-result + + - name: Diagnose file(s) + if: env.GIT_DIFF && !env.MATCHED_FILES + continue-on-error: true + run: | + clang-tidy --config-file=.clang-tidy ${{ env.GIT_DIFF_FILTERED }} --export-fixes=linter-result/fixes.yml + + - name: Save PR metadata + run: | + echo ${{ github.event.number }} > linter-result/pr-id.txt + echo ${{ github.event.pull_request.head.repo.full_name }} > linter-result/pr-head-repo.txt + echo ${{ github.event.pull_request.head.ref }} > linter-result/pr-head-ref.txt + + - uses: actions/upload-artifact@v2 + with: + name: linter-result + path: linter-result/ + + - name: Run clang-tidy-pr-comments action + uses: platisd/clang-tidy-pr-comments@bc0bb7da034a8317d54e7fe1e819159002f4cc40 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + clang_tidy_fixes: linter-result/fixes.yml + request_changes: true diff --git a/.github/workflows/requirements-linter.txt b/.github/workflows/requirements-linter.txt new file mode 100644 index 0000000000..4818cc5419 --- /dev/null +++ b/.github/workflows/requirements-linter.txt @@ -0,0 +1 @@ +pyyaml \ No newline at end of file From d4e0df11caf657894dc738a14080ac07214da05e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 12 Jul 2023 18:45:56 +0200 Subject: [PATCH 200/656] Sync SlotID Enum in the future we should really dump Arcus CURA-10618 and CURA-10475 --- Cura.proto | 7 +++++-- src/communication/ArcusCommunication.cpp | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Cura.proto b/Cura.proto index 5c82d7c112..b97e7f0f24 100644 --- a/Cura.proto +++ b/Cura.proto @@ -9,8 +9,11 @@ message ObjectList } enum SlotID { - SIMPLIFY = 0; - POSTPROCESS = 1; + BROADCAST_SETTINGS = 0; + SIMPLIFY_MODIFY = 100; + POSTPROCESS_MODIFY = 101; + INFILL_MODIFY = 102; + INFILL_GENERATE = 200; } message EnginePlugin diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 467ad53c99..4cc8dcdff6 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -515,13 +515,15 @@ void ArcusCommunication::sliceNext() { switch (plugin.id()) { - case cura::proto::SlotID::SIMPLIFY: + case cura::proto::SlotID::SIMPLIFY_MODIFY: slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); break; - case cura::proto::SlotID::POSTPROCESS: + case cura::proto::SlotID::POSTPROCESS_MODIFY: slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); break; - default: break; + default: + spdlog::error("Not yet implemented: {}", plugin.id()); + break; } } } From aac615432c0c4d72c773235da1e97f77522ad63e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 12 Jul 2023 19:38:43 +0200 Subject: [PATCH 201/656] Build CuraEngine Needed to get the compile database and the 3th party headers CURA-10732 --- .github/workflows/lint-tidier.yml | 40 ++++++++++++++++++++++- .github/workflows/requirements-linter.txt | 3 +- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-tidier.yml b/.github/workflows/lint-tidier.yml index 003b6ebc9e..843f0d5a19 100644 --- a/.github/workflows/lint-tidier.yml +++ b/.github/workflows/lint-tidier.yml @@ -37,6 +37,44 @@ jobs: if: env.GIT_DIFF && !env.MATCHED_FILES run: pip install -r .github/workflows/requirements-linter.txt + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + + - name: Install GCC-12 on ubuntu-22.04 + run: | + sudo apt install g++-12 gcc-12 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + + - name: Get Conan configuration + run: | + conan profile new default --detect + conan config install https://github.com/Ultimaker/conan-config.git + + - name: Install dependencies + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_testing=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv + + - name: Set Environment variables from Conan install (bash) + if: ${{ runner.os != 'Windows' }} + run: | + . ./activate_github_actions_runenv.sh + . ./activate_github_actions_buildenv.sh + working-directory: build/generators + + - name: Build CuraEngine and tests + run: | + cmake --preset release + cmake --build --preset release + - name: Create results directory run: mkdir linter-result @@ -44,7 +82,7 @@ jobs: if: env.GIT_DIFF && !env.MATCHED_FILES continue-on-error: true run: | - clang-tidy --config-file=.clang-tidy ${{ env.GIT_DIFF_FILTERED }} --export-fixes=linter-result/fixes.yml + clang-tidy -p ./build/Release/ --config-file=.clang-tidy ${{ env.GIT_DIFF_FILTERED }} --export-fixes=linter-result/fixes.yml - name: Save PR metadata run: | diff --git a/.github/workflows/requirements-linter.txt b/.github/workflows/requirements-linter.txt index 4818cc5419..b15fb5ea14 100644 --- a/.github/workflows/requirements-linter.txt +++ b/.github/workflows/requirements-linter.txt @@ -1 +1,2 @@ -pyyaml \ No newline at end of file +pyyaml +conan==1.56.0 \ No newline at end of file From 724d35dba079d4a9c491cb1a47604aead70d9cbf Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 14 Jul 2023 13:06:28 +0200 Subject: [PATCH 202/656] Skirt Height for individual extruder instead of using the global setting CURA-10728 --- include/utils/actions/smooth.h | 2 +- src/SkirtBrim.cpp | 3 ++- src/pathPlanning/Comb.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 5d707998f7..25a7d712d3 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -38,7 +38,7 @@ struct smooth_fn constexpr auto operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const { const auto size = ranges::distance(rng) - 1; - if (size < 3) + if (size < 4) { return static_cast(rng); } diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 48046bb322..d79b92ad89 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -378,6 +378,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) { reference_extruder_nr = extruder_nr; } + Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[reference_extruder_nr].settings; const int primary_line_count = line_count[reference_extruder_nr]; const bool external_only = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. const LayerIndex layer_nr = 0; @@ -387,7 +388,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) const bool skirt_around_prime_tower_brim = storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable"); const bool include_prime_tower = ! skirt_around_prime_tower_brim; // include manually otherwise - const int skirt_height = global_settings.get("skirt_height"); + const int skirt_height = extruder_settings.get("skirt_height"); first_layer_outline = Polygons(); for (int i_layer = layer_nr; i_layer <= skirt_height; ++i_layer) { diff --git a/src/pathPlanning/Comb.cpp b/src/pathPlanning/Comb.cpp index f9cd07ed08..bca5e87d20 100644 --- a/src/pathPlanning/Comb.cpp +++ b/src/pathPlanning/Comb.cpp @@ -131,8 +131,8 @@ bool Comb::calc unsigned int end_inside_poly_min = NO_INDEX; const bool end_inside_min = moveInside(boundary_inside_minimum, _end_inside, inside_loc_to_line_minimum.get(), end_point, end_inside_poly_min); - unsigned int start_part_boundary_poly_idx_min; - unsigned int end_part_boundary_poly_idx_min; + unsigned int start_part_boundary_poly_idx_min { }; + unsigned int end_part_boundary_poly_idx_min { }; unsigned int start_part_idx_min = (start_inside_poly_min == NO_INDEX)? NO_INDEX : partsView_inside_minimum.getPartContaining(start_inside_poly_min, &start_part_boundary_poly_idx_min); unsigned int end_part_idx_min = (end_inside_poly_min == NO_INDEX)? NO_INDEX : partsView_inside_minimum.getPartContaining(end_inside_poly_min, &end_part_boundary_poly_idx_min); From 685bc4beecf2b3d033ab5074dfc943343d7e9231 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 14 Jul 2023 13:32:27 +0200 Subject: [PATCH 203/656] Skirt height adjusted for multiple extruders. CURA-10728 --- src/SkirtBrim.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index d79b92ad89..f25bb822e8 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -378,7 +378,6 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) { reference_extruder_nr = extruder_nr; } - Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[reference_extruder_nr].settings; const int primary_line_count = line_count[reference_extruder_nr]; const bool external_only = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. const LayerIndex layer_nr = 0; @@ -388,15 +387,22 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) const bool skirt_around_prime_tower_brim = storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable"); const bool include_prime_tower = ! skirt_around_prime_tower_brim; // include manually otherwise - const int skirt_height = extruder_settings.get("skirt_height"); first_layer_outline = Polygons(); + int skirt_height = 0; + for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) + { + skirt_height = std::max(skirt_height, extruder.settings.get("skirt_height")); + } for (int i_layer = layer_nr; i_layer <= skirt_height; ++i_layer) { - first_layer_outline = - first_layer_outline.unionPolygons - ( - storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder_nr) - ); + for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) + { + first_layer_outline = + first_layer_outline.unionPolygons + ( + storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder.extruder_nr) + ); + } } if (skirt_around_prime_tower_brim) From 6cda2e50e39d7978b413793f32ba4691a9a2348f Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 14 Jul 2023 19:38:22 +0200 Subject: [PATCH 204/656] Plugin-system: Complete renames/moves (details->utils,x_MODIFY) 'Got it working on Windows' -- except that it wouldn't have worked on other systems probably if a complete recompile was done ;-) part of epic CURA-10560 (originally done for CURA-10715, but in one of the CURA-10618 branches -- look it all made sense at the time, ok?) --- include/plugins/pluginproxy.h | 10 +++++----- include/plugins/slotproxy.h | 4 ++-- include/plugins/slots.h | 6 +++--- include/plugins/types.h | 24 ++---------------------- include/utils/types/char_range_literal.h | 2 +- include/utils/types/generic.h | 2 +- 6 files changed, 14 insertions(+), 34 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 324346b91a..244b1667b3 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -49,7 +49,7 @@ namespace cura::plugins * * Class provides methods for validating the plugin, making requests and processing responses. */ -template +template class PluginProxy { public: @@ -170,7 +170,7 @@ class PluginProxy return ret_value; } - template + template void broadcast(auto&&... args) { if (! plugin_info_->broadcast_subscriptions.contains(BroadcastChannel.value)) @@ -270,14 +270,14 @@ class PluginProxy co_return; } - template + template boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) { grpc::ClientContext client_context{}; prep_client_context(client_context); - auto broadcaster { details::broadcast_factory() }; - auto request = details::broadcast_message_factory(std::forward(args)...); + auto broadcaster { details::broadcast_factory() }; + auto request = details::broadcast_message_factory(std::forward(args)...); auto response = google::protobuf::Empty{}; status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); co_return; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 720922857b..37307a9ce1 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -36,7 +36,7 @@ namespace cura::plugins * @tparam Response The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template +template class SlotProxy { Default default_process{}; @@ -80,7 +80,7 @@ class SlotProxy return std::invoke(default_process, std::forward(args)...); } - template + template void broadcast(auto&&...args) { if (plugin_.has_value()) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 13f259fa2a..50743df859 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -50,7 +50,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy; +using slot_simplify_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -60,7 +60,7 @@ using slot_simplify_ = SlotProxy -using slot_postprocess_ = SlotProxy; +using slot_postprocess_ = SlotProxy; template struct Typelist @@ -101,7 +101,7 @@ class Registry, Unit> : public Registry get_type().proxy = Tp{ std::forward(std::move(plugin)) }; } - template + template void broadcast(auto&&... args) { value_.proxy.template broadcast(std::forward(args)...); diff --git a/include/plugins/types.h b/include/plugins/types.h index c922de4c67..b24dc4f0b1 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -15,26 +15,6 @@ #include "cura/plugins/v0/slot_id.pb.h" -namespace cura::plugins -{ -namespace details -{ -template -struct CharRangeLiteral -{ - constexpr CharRangeLiteral(const char (&str)[N]) - { - std::copy_n(str, N, value); - } - - char value[N]; -}; - -} // namespace details - -} // namespace cura::plugins - - namespace fmt { // Custom formatter for humanreadable slot_id's @@ -48,10 +28,10 @@ struct formatter switch (slot_id) { - case cura::plugins::v0::SlotID::SIMPLIFY: + case cura::plugins::v0::SlotID::SIMPLIFY_MODIFY: slot_name = "SimplifyService"; break; - case cura::plugins::v0::SlotID::POSTPROCESS: + case cura::plugins::v0::SlotID::POSTPROCESS_MODIFY: slot_name = "PostprocessService"; break; default: diff --git a/include/utils/types/char_range_literal.h b/include/utils/types/char_range_literal.h index 3d6e34c2e4..44a1d0b599 100644 --- a/include/utils/types/char_range_literal.h +++ b/include/utils/types/char_range_literal.h @@ -12,7 +12,7 @@ namespace cura::utils template struct CharRangeLiteral { - constexpr CharRangeLiteral(const char (&str)[N]) + constexpr CharRangeLiteral(const char (&str)[N]) noexcept { std::copy_n(str, N, value); } diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 59512a43c8..3eda238ff6 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -36,7 +36,7 @@ class is_broadcast_channel { constexpr std::string_view t1{ T1.value }; constexpr std::string_view t2{ T2.value }; - return t1 == t2; + return t1.compare(t2) == 0; } public: From c27f880114ade6d04a67c77135d74532819d3366 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 17 Jul 2023 13:00:04 +0200 Subject: [PATCH 205/656] Force wait on handshake result If this isn't performed in the context of the initializer. The plugin will not be validated and the Broadcast won't be subscribed to before it actual usage. Contributes to CURA-10625 and CURA-10618 --- include/plugins/pluginproxy.h | 78 +++++++++++++---------------------- 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 93dca71242..fab675a106 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -88,25 +88,43 @@ class PluginProxy plugin_metadata plugin_info; boost::asio::co_spawn( - grpc_context, [this, &grpc_context, &status, &plugin_info, &handshake_stub]() { return this->handshakeCall(grpc_context, status, plugin_info, handshake_stub); }, boost::asio::detached); + grpc_context, + [this, &grpc_context, &status, &plugin_info, &handshake_stub]() -> boost::asio::awaitable + { + using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context); + + // Construct request + handshake_request handshake_req; + handshake_request::value_type request{ handshake_req(slot_info_) }; + + // Make unary request + handshake_response::value_type response; + status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); + handshake_response handshake_rsp; + plugin_info = handshake_rsp(response, client_context.peer()); + valid_ = validator_type{ slot_info_, plugin_info_.value() }; + if (valid_) + { + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); + if (! plugin_info.broadcast_subscriptions.empty()) + { + spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); + } + } + }, + boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes { throw exceptions::RemoteException(slot_info_, status.error_message()); } - if (! plugin_info.plugin_name.empty() && !plugin_info.slot_version.empty()) + if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) { plugin_info_ = plugin_info; } - if (! valid_) - { - if (plugin_info_.has_value()) - { - throw exceptions::ValidatorException(valid_, slot_info_, plugin_info_.value()); - } - throw exceptions::ValidatorException(valid_, slot_info_); - } }; constexpr PluginProxy(const PluginProxy&) = default; @@ -205,44 +223,6 @@ class PluginProxy slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake - /** - * @brief Executes the modifyCall operation with the plugin. - * - * Sends a request to the plugin and saves the response. - * - * @param grpc_context - The gRPC context to use for the call - * @param status - Status of the gRPC call which gets updated in this method - * @param ret_value - Reference to the value in which response to be stored - * @param args - Request arguments - * @return A boost::asio::awaitable indicating completion of the operation - */ - boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, plugin_metadata plugin_info, slots::handshake::v0::HandshakeService::Stub& handshake_stub) - { - using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context); - - // Construct request - handshake_request handshake_req; - handshake_request::value_type request{ handshake_req(slot_info_) }; - - // Make unary request - handshake_response::value_type response; - status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); - handshake_response handshake_rsp; - plugin_info = handshake_rsp(response, client_context.peer()); - valid_ = validator_type{ slot_info_, plugin_info_.value() }; - if (valid_) - { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); - if (! plugin_info.broadcast_subscriptions.empty()) - { - spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); - } - } - co_return; - } - /** * @brief Executes the modifyCall operation with the plugin. * @@ -276,7 +256,7 @@ class PluginProxy grpc::ClientContext client_context{}; prep_client_context(client_context); - auto broadcaster { details::broadcast_factory() }; + auto broadcaster{ details::broadcast_factory() }; auto request = details::broadcast_message_factory(std::forward(args)...); auto response = google::protobuf::Empty{}; status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); From 6fd8b30fdcad5235617acb5c2acd17e3c21ce712 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 17 Jul 2023 13:00:28 +0200 Subject: [PATCH 206/656] Add the GCode postprocess modify slot CURA-10625 --- src/communication/ArcusCommunication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 4cc8dcdff6..c99142c703 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -339,7 +339,7 @@ void ArcusCommunication::beginGCode() void ArcusCommunication::flushGCode() { - const std::string& message_str = private_data->gcode_output_stream.str(); + const std::string& message_str = slots::instance().invoke(private_data->gcode_output_stream.str()); if (message_str.size() == 0) { return; From 0ac5bcab123a2f0a04a66c20299f67d8a6cc3f1a Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 17 Jul 2023 13:00:04 +0200 Subject: [PATCH 207/656] Force wait on handshake result If this isn't performed in the context of the initializer. The plugin will not be validated and the Broadcast won't be subscribed to before it actual usage. Contributes to CURA-10625 and CURA-10618 --- include/plugins/pluginproxy.h | 78 ++++++++++++++--------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 244b1667b3..308d759e77 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -85,9 +85,36 @@ class PluginProxy agrpc::GrpcContext grpc_context; grpc::Status status; slots::handshake::v0::HandshakeService::Stub handshake_stub(channel); + plugin_metadata plugin_info; boost::asio::co_spawn( - grpc_context, [this, &grpc_context, &status, &handshake_stub]() { return this->handshakeCall(grpc_context, status, handshake_stub); }, boost::asio::detached); + grpc_context, + [this, &grpc_context, &status, &plugin_info, &handshake_stub]() -> boost::asio::awaitable + { + using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context); + + // Construct request + handshake_request handshake_req; + handshake_request::value_type request{ handshake_req(slot_info_) }; + + // Make unary request + handshake_response::value_type response; + status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); + handshake_response handshake_rsp; + plugin_info = handshake_rsp(response, client_context.peer()); + valid_ = validator_type{ slot_info_, plugin_info_.value() }; + if (valid_) + { + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); + if (! plugin_info.broadcast_subscriptions.empty()) + { + spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); + } + } + }, + boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes @@ -98,14 +125,9 @@ class PluginProxy } throw exceptions::RemoteException(slot_info_, status.error_message()); } - - if (! valid_) + if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) { - if (plugin_info_.has_value()) - { - throw exceptions::ValidatorException(valid_, slot_info_, plugin_info_.value()); - } - throw exceptions::ValidatorException(valid_, slot_info_); + plugin_info_ = plugin_info; } }; @@ -205,44 +227,6 @@ class PluginProxy slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake - /** - * @brief Executes the modifyCall operation with the plugin. - * - * Sends a request to the plugin and saves the response. - * - * @param grpc_context - The gRPC context to use for the call - * @param status - Status of the gRPC call which gets updated in this method - * @param ret_value - Reference to the value in which response to be stored - * @param args - Request arguments - * @return A boost::asio::awaitable indicating completion of the operation - */ - boost::asio::awaitable handshakeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, slots::handshake::v0::HandshakeService::Stub& handshake_stub) - { - using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context); - - // Construct request - handshake_request handshake_req; - handshake_request::value_type request{ handshake_req(slot_info_) }; - - // Make unary request - handshake_response::value_type response; - status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); - handshake_response handshake_rsp; - plugin_info_ = handshake_rsp(response, client_context.peer()); - valid_ = validator_type{ slot_info_, plugin_info_.value() }; - if (valid_) - { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info_->plugin_name, plugin_info_->plugin_version, plugin_info_->peer, slot_info_.slot_id); - if (! plugin_info_->broadcast_subscriptions.empty()) - { - spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info_->plugin_name, plugin_info_->broadcast_subscriptions); - } - } - co_return; - } - /** * @brief Executes the modifyCall operation with the plugin. * @@ -276,7 +260,7 @@ class PluginProxy grpc::ClientContext client_context{}; prep_client_context(client_context); - auto broadcaster { details::broadcast_factory() }; + auto broadcaster{ details::broadcast_factory() }; auto request = details::broadcast_message_factory(std::forward(args)...); auto response = google::protobuf::Empty{}; status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); From 2d2eb3e134b7922db20f833805a51ddc81686a06 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 17 Jul 2023 14:41:59 +0200 Subject: [PATCH 208/656] Make metadata plugin owning strings CURA-10625 --- include/plugins/converters.h | 4 ++-- include/plugins/metadata.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 29d961ee4c..932bf17085 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -134,8 +134,8 @@ struct handshake_response return { .slot_version = message.slot_version(), .plugin_name = message.plugin_name(), .plugin_version = message.plugin_version(), - .peer = peer, - .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; + .peer = std::string{ peer }, + .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; } }; diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 511ca734fd..6035f8617d 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -18,11 +18,11 @@ namespace cura::plugins struct plugin_metadata { - std::string_view slot_version; - std::string_view plugin_name; - std::string_view plugin_version; - std::string_view peer; - std::set broadcast_subscriptions; + std::string slot_version; + std::string plugin_name; + std::string plugin_version; + std::string peer; + std::set broadcast_subscriptions; }; struct slot_metadata From 94418fc9582aee26c8a17064126dbe1c7182b2cd Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 17 Jul 2023 14:41:59 +0200 Subject: [PATCH 209/656] Make metadata plugin owning strings CURA-10625 --- include/plugins/converters.h | 4 ++-- include/plugins/metadata.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 29d961ee4c..932bf17085 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -134,8 +134,8 @@ struct handshake_response return { .slot_version = message.slot_version(), .plugin_name = message.plugin_name(), .plugin_version = message.plugin_version(), - .peer = peer, - .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; + .peer = std::string{ peer }, + .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; } }; diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 511ca734fd..6035f8617d 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -18,11 +18,11 @@ namespace cura::plugins struct plugin_metadata { - std::string_view slot_version; - std::string_view plugin_name; - std::string_view plugin_version; - std::string_view peer; - std::set broadcast_subscriptions; + std::string slot_version; + std::string plugin_name; + std::string plugin_version; + std::string peer; + std::set broadcast_subscriptions; }; struct slot_metadata From 2ed2165306e65a835de7fbfdcdcbaea0203a94fc Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 17 Jul 2023 17:10:59 +0200 Subject: [PATCH 210/656] Use testing grpc definitions CURA-10475 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 230913ef9e..69cda0348e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10618") + self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") def generate(self): deps = CMakeDeps(self) From 8f9bbedbe72066e3f691ed59e62b390fe2cba3ff Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 18 Jul 2023 12:56:20 +0200 Subject: [PATCH 211/656] package resources headers --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 69cda0348e..34e075acd6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -149,7 +149,7 @@ def generate(self): def layout(self): cmake_layout(self) - + self.cpp.source.includedirs = ["include", "resources"] self.cpp.build.includedirs = ["."] # To package the generated headers self.cpp.package.libs = ["_CuraEngine"] From 1ce6199f38c1934e368f0f7f018749b377d7a55e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 18 Jul 2023 13:33:27 +0200 Subject: [PATCH 212/656] Include resource headers in Conan package This should in the future be a key shipped with the front-end Contributes to CURA-10806 --- conanfile.py | 1 - .../utils/resources}/certificate.pem.h | 6 +++-- resources/public_key.pem.h | 26 ------------------- src/utils/channel.cpp | 2 +- 4 files changed, 5 insertions(+), 30 deletions(-) rename {resources => include/utils/resources}/certificate.pem.h (93%) delete mode 100644 resources/public_key.pem.h diff --git a/conanfile.py b/conanfile.py index 34e075acd6..db6341e9d0 100644 --- a/conanfile.py +++ b/conanfile.py @@ -149,7 +149,6 @@ def generate(self): def layout(self): cmake_layout(self) - self.cpp.source.includedirs = ["include", "resources"] self.cpp.build.includedirs = ["."] # To package the generated headers self.cpp.package.libs = ["_CuraEngine"] diff --git a/resources/certificate.pem.h b/include/utils/resources/certificate.pem.h similarity index 93% rename from resources/certificate.pem.h rename to include/utils/resources/certificate.pem.h index 2ccb343cd8..93a1bf49c1 100644 --- a/resources/certificate.pem.h +++ b/include/utils/resources/certificate.pem.h @@ -1,9 +1,11 @@ #ifndef CERTIFICATE_H #define CERTIFICATE_H +// FIXME: Move out of the source code even though this is the public key + #include -namespace resources +namespace cura::utils::resources { constexpr std::string_view certificate = R"#(-----BEGIN CERTIFICATE----- @@ -42,7 +44,7 @@ c3dQRszg9OTAyUwNKQEAuZ9II/cCpLyE+GbnqRdkErHgO7kiKQ== -----END CERTIFICATE----- )#"; -} // namespace resources +} // namespace cura::utils::resources #endif //CERTIFICATE_H diff --git a/resources/public_key.pem.h b/resources/public_key.pem.h deleted file mode 100644 index 24f1676478..0000000000 --- a/resources/public_key.pem.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef PUBLIC_KEY_H -#define PUBLIC_KEY_H - -#include - -namespace resources -{ - constexpr std::string_view public_key = -R"#(-----BEGIN RSA PUBLIC KEY----- -MIICCgKCAgEAs1wvIbPBKmQbOyIz2lPzbUB1cmoIzsIHCzL0T1Ol/8P6IF3BCy6Q -gFweuQd/2D6r0FgMgq7T96p4TtyhCDQcAn8LGjBYACjkwJgkeoManvcgdRXG8Z61 -VWvLqvXdASmP8zO/PiefJkyXB1eNYvLP8C0NF9PSL5NvKorcFgFOzRhbf98biDgN -36/w6xA6N8MNO/mJBJo1C6JJ8JtADxsvVpeqKMVwTaKZGcsVDcjGtwtWSRcQDjEC -mznfhpUpUFcUBLtQgjQyZO+GYdJnpWXbV8CGYbAkY+tBKVJDwmFW4izirtN+nVId -6gf6WaJ31f3aJaWsz+ANkuIoyTxUeW31jWRpg9RQqrWY2gcYnwjHgny6YJUJoh7N -hjlCqYIsqeI/hW4Iahhpe6JZ/8Q5r5gYwZhooH3IkuxCm6i6VZVOsrKuK/lhbrwJ -PS6OiQIOZ/fetBzhrHGe7Si35SnqeZXtdwYCktBbEl9vH2GEU5h+KlbOC1fUWVdv -HrbmAbxZbDQ2MIqlM3f1CrvLxskgmgIxeUqtm5nnIMgmyBOxVhfdEs7D4K46JqCM -3R/1u0vhfu47Rvfi8WNyR3UYOsOA21aMQmIUFCozzZ7cHPARwbUaSHoupoXMmmqj -xCW0DCv7S5ZM3mMJ7n4I6JIp/evtgGEJ1+w97ZT3n6DoYarb4ncg+VsCAwEAAQ== ------END RSA PUBLIC KEY----- -)#"; - -} // namespace resources - -#endif //PUBLIC_KEY_H diff --git a/src/utils/channel.cpp b/src/utils/channel.cpp index 1cbcf017b1..cc04e25834 100644 --- a/src/utils/channel.cpp +++ b/src/utils/channel.cpp @@ -6,7 +6,7 @@ #include #include -#include "../resources/certificate.pem.h" +#include "utils/resources/certificate.pem.h" namespace cura::utils { From 3aba9a5bdad5fb14f58e683ddbfc04948cd0386a Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 18 Jul 2023 15:13:58 +0200 Subject: [PATCH 213/656] Brim lines generation was terminated because of break statement. CURA-10758 --- src/SkirtBrim.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 48046bb322..642dd6aa2f 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -221,7 +221,10 @@ std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_bri } SkirtBrimLine& output_location = storage.skirt_brim[offset.extruder_nr][offset.inset_idx]; coord_t added_length = generateOffset(offset, covered_area, allowed_areas_per_extruder, output_location); - if (! added_length) + + const Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + const bool support_brim_enable = global_settings.get("support_brim_enable"); + if (! added_length && !support_brim_enable) { // no more place for more brim. Trying to satisfy minimum length constraint with generateSecondarySkirtBrim break; } From 6338fbaecff031f80b16ba1f587add09a50b6484 Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sat, 8 Jul 2023 14:39:13 +0200 Subject: [PATCH 214/656] Replace size_t format string in InfillTest.cpp This will fix an incorrect format causing test failure on 32-bit architectures. --- tests/InfillTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/InfillTest.cpp b/tests/InfillTest.cpp index 1560d1a2f2..81e7dd7d71 100644 --- a/tests/InfillTest.cpp +++ b/tests/InfillTest.cpp @@ -100,7 +100,7 @@ class InfillTestParameters , result_polygons(std::move(result_polygons)) { // FIXME: Once we are using spdlog as logger, we'll also use fmt::format() here, see CURA-8258. - name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%lld", static_cast(params.pattern), static_cast(params.zig_zagify), static_cast(params.connect_polygons), params.line_distance, test_polygon_id); + name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%zu", static_cast(params.pattern), static_cast(params.zig_zagify), static_cast(params.connect_polygons), params.line_distance, test_polygon_id); } friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params) From bc050f26637be838027e5c86167a6f49dd568322 Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sun, 9 Jul 2023 00:41:15 +0200 Subject: [PATCH 215/656] Replace custom sprintf formatter with fmt::format --- tests/InfillTest.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/tests/InfillTest.cpp b/tests/InfillTest.cpp index 81e7dd7d71..88355bac18 100644 --- a/tests/InfillTest.cpp +++ b/tests/InfillTest.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -20,16 +21,6 @@ // NOLINTBEGIN(*-magic-numbers) namespace cura { -template -std::string makeName(const std::string& format_string, Ts... args) -{ - // FIXME: once we use spdlog, we can use fmt::format instead, see CURA-8258 - constexpr int buff_size = 1024; - char buff[buff_size]; - std::snprintf(buff, buff_size, format_string.c_str(), args...); - return std::string(buff); -} - coord_t getPatternMultiplier(const EFillMethod& pattern) { switch (pattern) @@ -66,8 +57,7 @@ struct InfillParameters , connect_polygons(connect_polygons) , line_distance(line_distance) { - // FIXME: Once we are using spdlog as logger, we'll also use fmt::format() here, see CURA-8258. - name = makeName("InfillParameters_%d_%d_%d_%lld", static_cast(pattern), static_cast(zig_zagify), static_cast(connect_polygons), line_distance); + name = fmt::format("InfillParameters_{d}_{d}_{d}_{d}", static_cast(pattern), zig_zagify, connect_polygons, line_distance); } }; @@ -99,8 +89,7 @@ class InfillTestParameters , result_lines(std::move(result_lines)) , result_polygons(std::move(result_polygons)) { - // FIXME: Once we are using spdlog as logger, we'll also use fmt::format() here, see CURA-8258. - name = makeName("InfillTestParameters_P%d_Z%d_C%d_L%lld__%zu", static_cast(params.pattern), static_cast(params.zig_zagify), static_cast(params.connect_polygons), params.line_distance, test_polygon_id); + name = fmt::format("InfillTestParameters_P{d}_Z{d}_C{d}_L{d}__{d}", static_cast(params.pattern), params.zig_zagify, params.connect_polygons, params.line_distance, test_polygon_id); } friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params) From bb128829d13d85f2dd48b263636eec2c9e2b7b2e Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sun, 9 Jul 2023 00:48:04 +0200 Subject: [PATCH 216/656] Use correct fmt syntax --- tests/InfillTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/InfillTest.cpp b/tests/InfillTest.cpp index 88355bac18..8d95705934 100644 --- a/tests/InfillTest.cpp +++ b/tests/InfillTest.cpp @@ -57,7 +57,7 @@ struct InfillParameters , connect_polygons(connect_polygons) , line_distance(line_distance) { - name = fmt::format("InfillParameters_{d}_{d}_{d}_{d}", static_cast(pattern), zig_zagify, connect_polygons, line_distance); + name = fmt::format("InfillParameters_{:d}_{:d}_{:d}_{:d}", static_cast(pattern), zig_zagify, connect_polygons, line_distance); } }; @@ -89,7 +89,7 @@ class InfillTestParameters , result_lines(std::move(result_lines)) , result_polygons(std::move(result_polygons)) { - name = fmt::format("InfillTestParameters_P{d}_Z{d}_C{d}_L{d}__{d}", static_cast(params.pattern), params.zig_zagify, params.connect_polygons, params.line_distance, test_polygon_id); + name = fmt::format("InfillTestParameters_P{:d}_Z{:d}_C{:d}_L{:d}__{:d}", static_cast(params.pattern), params.zig_zagify, params.connect_polygons, params.line_distance, test_polygon_id); } friend std::ostream& operator<<(std::ostream& os, const InfillTestParameters& params) From 0f2295376510334f1e4fe122efc5617ab79fe4c9 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 18 Jul 2023 16:17:34 +0200 Subject: [PATCH 217/656] Added robustness for performance. If user tries to give skirt height more than the model_height. CURA-10728 --- src/SkirtBrim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index f25bb822e8..f2c4c41aa3 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -391,9 +391,9 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) int skirt_height = 0; for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) { - skirt_height = std::max(skirt_height, extruder.settings.get("skirt_height")); + skirt_height = std::min(std::max(skirt_height, extruder.settings.get("skirt_height")), static_cast(storage.print_layer_count)); } - for (int i_layer = layer_nr; i_layer <= skirt_height; ++i_layer) + for (int i_layer = layer_nr; i_layer < skirt_height; ++i_layer) { for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) { From f0c132d0296e2c50889d9fe032f35b9d73155088 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 19 Jul 2023 11:26:50 +0200 Subject: [PATCH 218/656] Postprocess the header The header is generated last during slicing. Keep in mind that Cura also adds the footer with the comments `;SETTINGS_` this isn't something that the engine handles CURA-10625 --- src/communication/ArcusCommunication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index c99142c703..a074dce00d 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -372,7 +372,7 @@ void ArcusCommunication::sendCurrentPosition(const Point& position) void ArcusCommunication::sendGCodePrefix(const std::string& prefix) const { std::shared_ptr message = std::make_shared(); - message->set_data(prefix); + message->set_data(slots::instance().invoke(prefix)); private_data->socket->sendMessage(message); } From 2ffffac40e8bee8a0dde973d316b4e0839834acc Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 19 Jul 2023 12:39:59 +0200 Subject: [PATCH 219/656] Brim lines fix based on other use-cases CURA-10758 --- include/utils/polygon.h | 16 +++++++++-- src/SkirtBrim.cpp | 62 ++++++++++++++++++++--------------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/include/utils/polygon.h b/include/utils/polygon.h index 8bd2303429..797a41e2a0 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -1040,11 +1040,23 @@ class Polygons Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const; - Polygons offsetPolyLine(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const + Polygons offsetPolyLine(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, bool inputPolyIsClosed = false) const { Polygons ret; double miterLimit = 1.2; - ClipperLib::EndType end_type = (joinType == ClipperLib::jtMiter)? ClipperLib::etOpenSquare : ClipperLib::etOpenRound; + ClipperLib::EndType end_type; + if (inputPolyIsClosed) + { + end_type = ClipperLib::etClosedLine; + } + else if (joinType == ClipperLib::jtMiter) + { + end_type = ClipperLib::etOpenSquare; + } + else + { + end_type = ClipperLib::etOpenRound; + } ClipperLib::ClipperOffset clipper(miterLimit, 10.0); clipper.AddPaths(paths, joinType, end_type); clipper.MiterLimit = miterLimit; diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 642dd6aa2f..5e1b31bee8 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -42,7 +42,7 @@ SkirtBrim::SkirtBrim(SliceDataStorage& storage) : // NOTE: the line count will only be satisfied for the first extruder used. skirt_brim_extruder_nr = first_used_extruder_nr; } - + line_widths.resize(extruder_count); skirt_brim_minimal_length.resize(extruder_count); external_polys_only.resize(extruder_count); @@ -139,7 +139,7 @@ void SkirtBrim::generate() std::vector starting_outlines(extruder_count); std::vector all_brim_offsets = generateBrimOffsetPlan(starting_outlines); std::vector prime_brim_offsets_for_skirt = generatePrimeTowerBrimForSkirtAdhesionOffsetPlan(); - + constexpr LayerIndex layer_nr = 0; constexpr bool include_support = true; Polygons covered_area = storage.getLayerOutlines(layer_nr, include_support, /*include_prime_tower*/ true, /*external_polys_only*/ false); @@ -192,7 +192,7 @@ void SkirtBrim::generate() // Secondary brim of all other materials which don;t meet minimum length constriant yet generateSecondarySkirtBrim(covered_area, allowed_areas_per_extruder, total_length); - + // simplify paths to prevent buffer unnerruns in firmware const Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const coord_t maximum_resolution = global_settings.get("meshfix_maximum_resolution"); @@ -222,33 +222,31 @@ std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_bri SkirtBrimLine& output_location = storage.skirt_brim[offset.extruder_nr][offset.inset_idx]; coord_t added_length = generateOffset(offset, covered_area, allowed_areas_per_extruder, output_location); - const Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const bool support_brim_enable = global_settings.get("support_brim_enable"); - if (! added_length && !support_brim_enable) + if (! added_length) { // no more place for more brim. Trying to satisfy minimum length constraint with generateSecondarySkirtBrim - break; + continue; } total_length[offset.extruder_nr] += added_length; - + if - ( - offset.is_last && - total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet - total_length[offset.extruder_nr] > 0u // No lines got added; we have no extrusion lines to build on - ) + ( + offset.is_last && + total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet + total_length[offset.extruder_nr] > 0u // No lines got added; we have no extrusion lines to build on + ) { offset.is_last = false; constexpr bool is_last = true; all_brim_offsets.emplace_back - ( - offset.inset_idx, - external_polys_only[offset.extruder_nr], - line_widths[offset.extruder_nr], - offset.total_offset + line_widths[offset.extruder_nr], - offset.inset_idx + 1, - offset.extruder_nr, - is_last - ); + ( + offset.inset_idx, + external_polys_only[offset.extruder_nr], + line_widths[offset.extruder_nr], + offset.total_offset + line_widths[offset.extruder_nr], + offset.inset_idx + 1, + offset.extruder_nr, + is_last + ); std::sort(all_brim_offsets.begin() + offset_idx + 1, all_brim_offsets.end(), OffsetSorter); // reorder remaining offsets } } @@ -319,7 +317,7 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, Polygons local_brim; auto closed_polygons_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].closed_polygons - .offset(offset_dist, ClipperLib::jtRound); + .offsetPolyLine(offset_dist, ClipperLib::jtRound, true); local_brim.add(closed_polygons_brim); auto open_polylines_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].open_polylines @@ -341,7 +339,7 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, const coord_t max_stitch_distance = line_widths[offset.extruder_nr]; PolylineStitcher::stitch(brim_lines, result.open_polylines, result.closed_polygons, max_stitch_distance); - + // clean up too small lines for (size_t line_idx = 0; line_idx < result.open_polylines.size(); ) { @@ -356,7 +354,7 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, } } } - + { // update allowed_areas_per_extruder for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { @@ -478,11 +476,11 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline.add(support_layer.support_roof); } if - ( - storage.primeTower.enabled && - global_settings.get("prime_tower_brim_enable") && - (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr) - ) + ( + storage.primeTower.enabled && + global_settings.get("prime_tower_brim_enable") && + (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr) + ) { first_layer_outline.add(storage.primeTower.outer_poly); // don't remove parts of the prime tower, but make a brim for it } @@ -591,7 +589,7 @@ void SkirtBrim::generateSecondarySkirtBrim(Polygons& covered_area, std::vector

Date: Wed, 19 Jul 2023 17:32:12 +0200 Subject: [PATCH 220/656] Fixes based on review comments CURA-10728 --- src/SkirtBrim.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index f2c4c41aa3..607a3f9e27 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -391,8 +391,13 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) int skirt_height = 0; for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) { - skirt_height = std::min(std::max(skirt_height, extruder.settings.get("skirt_height")), static_cast(storage.print_layer_count)); + if (extruder_nr == -1 || extruder_nr == extruder.extruder_nr) + { + skirt_height = std::max(skirt_height, extruder.settings.get("skirt_height")); + } } + skirt_height = std::min(skirt_height, static_cast(storage.print_layer_count)); + for (int i_layer = layer_nr; i_layer < skirt_height; ++i_layer) { for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) From c30673040164e46c75e52ec751cf9c7af25410ce Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 19 Jul 2023 18:01:32 +0200 Subject: [PATCH 221/656] Update clang-formatting rules Changed multiple rules in .clang-format for better readability and up-to-date standard usage. Primarily, alignment rules were adjusted, binary operators now break before all, column limit was reduced for tighter code blocks, and the c++ standard was updated to c++20. These amendments sought to improve overall code legibility and ensure adherence to modern c++ standards. Contributes to CURA-10732 --- .clang-format | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.clang-format b/.clang-format index aafa8a0420..4196ee4bd5 100644 --- a/.clang-format +++ b/.clang-format @@ -1,16 +1,18 @@ AccessModifierOffset: -4 -AlignAfterOpenBracket: Align +AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: None AlignConsecutiveDeclarations: None AlignEscapedNewlines: DontAlign AlignOperands: AlignAfterOperator AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Never +AllowAllArgumentsOnNextLine: false +AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false +AllowShortLambdasOnASingleLine: Empty AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false @@ -33,15 +35,15 @@ BraceWrapping: SplitEmptyNamespace: true SplitEmptyRecord: true BreakAfterJavaFieldAnnotations: true -BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBinaryOperators: All BreakBeforeBraces: Allman BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma BreakStringLiterals: true -ColumnLimit: 240 +ColumnLimit: 180 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false -PackConstructorInitializers: CurrentLine +PackConstructorInitializers: Never ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: false @@ -54,9 +56,10 @@ ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH +IncludeBlocks: Regroup IncludeCategories: - Priority: 2 - Regex: ^"(llvm|llvm-c|clang|clang-c)/ + Regex: ^<(scripta|spdlog|range|fmt|Arcus)/ - Priority: 3 Regex: ^(<|"(gtest|gmock|isl|json)/) - Priority: 1 @@ -86,11 +89,14 @@ SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 -SpacesInAngles: false +SpacesInAngles: Never SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: c++17 +Standard: c++20 TabWidth: 4 UseTab: Never From 3481c2825477dca13461376e3e9b0c9f737b4e10 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 20 Jul 2023 07:35:26 +0200 Subject: [PATCH 222/656] Don't format concepts (yet) Our current used clang-format (14) doesn't have full support for it yet since 14 is the current standard on Ubuntu and on our GH runners added `clang-format off/on` guards around those code blocks. CURA-10732 --- include/utils/types/arachne.h | 3 ++- include/utils/types/generic.h | 3 ++- include/utils/types/geometry.h | 3 ++- include/utils/types/graph.h | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/utils/types/arachne.h b/include/utils/types/arachne.h index 4a2808dc14..94aa3312cb 100644 --- a/include/utils/types/arachne.h +++ b/include/utils/types/arachne.h @@ -15,6 +15,7 @@ namespace cura::utils { +// clang-format off template concept st_storable_data = requires(T val) { @@ -139,7 +140,7 @@ concept toolpaths = requires(T tp) requires ranges::range; requires toolpath; }; - +// clang-format on } // namespace cura::utils #endif // UTILS_TYPES_ARACHNE_H \ No newline at end of file diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index b6a3c49b3e..751a47342d 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -10,6 +10,7 @@ namespace cura::utils { +// clang-format off template concept hashable = requires(T value) { @@ -51,7 +52,7 @@ concept integral = std::integral; template concept floating_point = std::floating_point; #endif - +// clang-format on } // namespace cura::utils #endif // CURAENGINE_GENERIC_H diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 4a8eedfb66..390606962c 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -20,6 +20,7 @@ namespace cura::utils { +// clang-format off /*! * @concept point2d_tuple @@ -150,7 +151,7 @@ concept segment_range = ranges::range && requires(T segment_range) { requires segment; }; - +// clang-format on } // namespace cura::utils #endif // UTILS_TYPES_GEOMETRY_H \ No newline at end of file diff --git a/include/utils/types/graph.h b/include/utils/types/graph.h index 200a575c4e..91c436b1f3 100644 --- a/include/utils/types/graph.h +++ b/include/utils/types/graph.h @@ -11,6 +11,7 @@ namespace cura::utils { +// clang-format off /* # nodable * Describing the basic requirement for a node in a graph. @@ -32,6 +33,8 @@ concept graphable = template concept setable = nodeable && (std::is_same>::value || std::is_same>::value || std::is_same>::value); + +// clang-format off } // namespace cura #endif // UTILS_CONCEPTS_GRAPH_H From 9915fde50cb17552c30c66793944b874c78a0c1e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 20 Jul 2023 09:44:46 +0200 Subject: [PATCH 223/656] Update clang-tidy CURA-10732 --- .clang-tidy | 55 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 77b35faafa..e516426d03 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,3 @@ ---- Checks: > *, -llvmlibc-*, @@ -10,21 +9,47 @@ Checks: > -altera-struct-pack-align, -android-*, -misc-non-private-member-variables-in-classes, - -fuchsia-overloaded-operator + -fuchsia-overloaded-operator, + -cppcoreguidelines-avoid-capturing-lambda-coroutines, + -llvm-header-guard, + -bugprone-easily-swappable-parameters WarningsAsErrors: '-*' HeaderFilterRegex: '' FormatStyle: none CheckOptions: - - { key: readability-identifier-naming.NamespaceCase, value: lower_case } - - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } - - { key: readability-identifier-naming.ClassCase, value: CamelCase } - - { key: readability-identifier-naming.FunctionCase, value: camelBack } - - { key: readability-identifier-naming.MethodCase, value: camelBack } - - { key: readability-identifier-naming.ParameterCase, value: lower_case } - - { key: readability-identifier-naming.VariableCase, value: lower_case } - - { key: readability-identifier-naming.ClassConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.GlobalVariableCase, value: UPPER_CASE } - - { key: readability-identifier-length.IgnoredParameterNames, value: 'i|j|k|x|y|z|a|b|aa|bb|ip|os' } - - { key: readability-identifier-length.IgnoredVariableNames, value: 'i|j|k|x|y|z|a|b|aa|bb|ip|os' } - - { key: readability-identifier-length.IgnoredLoopCounterNames, value: 'i|j|k|x|y|z|a|b|aa|bb|ip' } \ No newline at end of file + - key: google-build-namespaces.HeaderFileExtensions + value: h + - key: readability-function-size.LineThreshold + value: 100 + - key: readability-function-size.BranchThreshold + value: 10 + - key: readability-function-size.ParameterThreshold + value: 6 + - key: readability-function-size.NestingThreshold + value: 4 + - key: readability-identifier-naming.NamespaceCase + value: lower_case + - key: readability-identifier-naming.MacroDefinitionCase + value: UPPER_CASE + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.MethodCase + value: camelBack + - key: readability-identifier-naming.ParameterCase + value: lower_case + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.ClassConstantCase + value: UPPER_CASE + - key: readability-identifier-naming.GlobalConstantCase + value: lower_case + - key: readability-identifier-naming.GlobalVariableCase + value: UPPER_CASE + - key: readability-identifier-length.IgnoredParameterNames + value: 'p|p0|p1|i|j|k|x|X|y|Y|z|Z|a|A|b|B|c|C|d|D|ab|AB|ba|BA|bc|BC|cb|CB|cd|CD|dc|DC|ad|AD|da|DA|ip|os' + - key: readability-identifier-length.IgnoredVariableNames + value: '_p|p0|p1|i|j|k|x|X|y|Y|z|Z|a|A|b|B|c|C|d|D|ab|AB|ba|BA|bc|BC|cb|CB|cd|CD|dc|DC|ad|AD|da|DA|ip|os' + - key: readability-identifier-length.IgnoredLoopCounterNames + value: '_p|p0|p1|i|j|k|x|X|y|Y|z|Z|a|A|b|B|c|C|d|D|ab|AB|ba|BA|bc|BC|cb|CB|cd|CD|dc|DC|ad|AD|da|DA|ip|os' \ No newline at end of file From 4c311c32106829418ae3e2410fb70b7f1cfcdd2b Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 20 Jul 2023 07:45:32 +0000 Subject: [PATCH 224/656] Applied clang-format. --- include/utils/types/arachne.h | 10 +++++----- include/utils/types/geometry.h | 10 +++++----- include/utils/types/graph.h | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/utils/types/arachne.h b/include/utils/types/arachne.h index 94aa3312cb..ae96a13ca9 100644 --- a/include/utils/types/arachne.h +++ b/include/utils/types/arachne.h @@ -4,14 +4,14 @@ #ifndef UTILS_TYPES_ARACHNE_H #define UTILS_TYPES_ARACHNE_H -#include -#include -#include +#include "utils/types/generic.h" +#include "utils/types/geometry.h" #include -#include "utils/types/generic.h" -#include "utils/types/geometry.h" +#include +#include +#include namespace cura::utils { diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index 390606962c..b87880422c 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -8,15 +8,15 @@ #ifndef UTILS_TYPES_GEOMETRY_H #define UTILS_TYPES_GEOMETRY_H -#include -#include -#include -#include +#include "utils/types/generic.h" #include #include -#include "utils/types/generic.h" +#include +#include +#include +#include namespace cura::utils { diff --git a/include/utils/types/graph.h b/include/utils/types/graph.h index 91c436b1f3..ea74ab65f6 100644 --- a/include/utils/types/graph.h +++ b/include/utils/types/graph.h @@ -4,11 +4,11 @@ #ifndef UTILS_CONCEPTS_GRAPH_H #define UTILS_CONCEPTS_GRAPH_H +#include "utils/types/generic.h" + #include #include -#include "utils/types/generic.h" - namespace cura::utils { // clang-format off From 7d305adfcce4e685ca70d7704c11f7f8cb00a059 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 20 Jul 2023 07:58:34 +0000 Subject: [PATCH 225/656] Applied clang-format. --- include/Application.h | 14 +- include/plugins/broadcasts.h | 6 +- include/plugins/converters.h | 20 +- include/plugins/exception.h | 31 +- include/plugins/metadata.h | 7 +- include/plugins/pluginproxy.h | 61 +- include/plugins/slotproxy.h | 20 +- include/plugins/slots.h | 14 +- include/plugins/types.h | 13 +- include/plugins/validator.h | 7 +- include/utils/channel.h | 16 +- include/utils/format/thread_id.h | 4 +- include/utils/gettime.h | 47 +- include/utils/resources/certificate.pem.h | 7 +- include/utils/types/generic.h | 10 +- src/Application.cpp | 43 +- src/FffGcodeWriter.cpp | 1246 +++++++++++++-------- src/InsetOrderOptimizer.cpp | 129 ++- src/SkeletalTrapezoidation.cpp | 409 +++++-- src/communication/ArcusCommunication.cpp | 34 +- src/slicer.cpp | 340 +++--- src/utils/channel.cpp | 57 +- 22 files changed, 1569 insertions(+), 966 deletions(-) diff --git a/include/Application.h b/include/Application.h index 9bcf4fdb28..4a3f793daf 100644 --- a/include/Application.h +++ b/include/Application.h @@ -4,12 +4,12 @@ #ifndef APPLICATION_H #define APPLICATION_H -#include +#include "utils/NoCopy.h" + #include +#include #include -#include "utils/NoCopy.h" - namespace cura { @@ -90,7 +90,7 @@ class Application : NoCopy * * \param nworkers The number of workers (including the main thread) that are ran. */ - void startThreadPool(int nworkers=0); + void startThreadPool(int nworkers = 0); std::string instance_uuid; @@ -102,7 +102,7 @@ class Application : NoCopy * \param argv The arguments provided to the application. */ void connect(); -#endif //ARCUS +#endif // ARCUS /*! * \brief Print the header and license to the stderr channel. @@ -145,6 +145,6 @@ class Application : NoCopy void registerPlugins(const PluginSetupConfiguration& plugins_config); }; -} //Cura namespace. +} // namespace cura -#endif //APPLICATION_H \ No newline at end of file +#endif // APPLICATION_H \ No newline at end of file diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h index a4de7567c4..256edc7d37 100644 --- a/include/plugins/broadcasts.h +++ b/include/plugins/broadcasts.h @@ -12,14 +12,16 @@ namespace cura::plugins::details { template -requires utils::is_broadcast_channel_v constexpr auto broadcast_message_factory(auto&&... args) +requires utils::is_broadcast_channel_v +constexpr auto broadcast_message_factory(auto&&... args) { return broadcast_settings_request{}(std::forward(args)...); }; template -requires utils::is_broadcast_channel_v constexpr auto broadcast_factory() +requires utils::is_broadcast_channel_v +constexpr auto broadcast_factory() { return agrpc::RPC<&Stub::PrepareAsyncBroadcastSettings>{}; } diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 932bf17085..5260d33731 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -4,16 +4,7 @@ #ifndef PLUGINS_CONVERTERS_H #define PLUGINS_CONVERTERS_H -#include -#include - -#include -#include -#include - -#include "plugins/metadata.h" -#include "plugins/types.h" - +#include "Cura.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" @@ -22,8 +13,15 @@ #include "cura/plugins/slots/postprocess/v0/postprocess.pb.h" #include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/simplify.pb.h" +#include "plugins/metadata.h" +#include "plugins/types.h" -#include "Cura.pb.h" +#include +#include + +#include +#include +#include namespace cura::plugins { diff --git a/include/plugins/exception.h b/include/plugins/exception.h index a5328a3357..93f05638eb 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -4,16 +4,16 @@ #ifndef UTILS_CONCEPTS_GRAPH_H #define UTILS_CONCEPTS_GRAPH_H -#include +#include "plugins/metadata.h" +#include "plugins/types.h" #include + +#include #include #include #include -#include "plugins/metadata.h" -#include "plugins/types.h" - namespace cura::plugins::exceptions { @@ -22,10 +22,18 @@ class ValidatorException : public std::exception std::string msg_; public: - ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept : msg_(fmt::format("Failed to validation plugin on Slot '{}'", slot_info.slot_id)){}; + ValidatorException(const auto& validator, const slot_metadata& slot_info) noexcept + : msg_(fmt::format("Failed to validation plugin on Slot '{}'", slot_info.slot_id)){}; ValidatorException(const auto& validator, const slot_metadata& slot_info, const plugin_metadata& plugin_info) noexcept - : msg_(fmt::format("Failed to validate plugin '{}-{}' running at [{}] for slot '{}', slot range '{}' incompatible with plugin slot version '{}'", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info.slot_id, slot_info.version_range, plugin_info.slot_version)) + : msg_(fmt::format( + "Failed to validate plugin '{}-{}' running at [{}] for slot '{}', slot range '{}' incompatible with plugin slot version '{}'", + plugin_info.plugin_name, + plugin_info.plugin_version, + plugin_info.peer, + slot_info.slot_id, + slot_info.version_range, + plugin_info.slot_version)) { } @@ -40,10 +48,17 @@ class RemoteException : public std::exception std::string msg_; public: - RemoteException(const slot_metadata& slot_info, std::string_view error_msg) noexcept : msg_(fmt::format("Remote exception on Slot '{}': {}", slot_info.slot_id, error_msg)){}; + RemoteException(const slot_metadata& slot_info, std::string_view error_msg) noexcept + : msg_(fmt::format("Remote exception on Slot '{}': {}", slot_info.slot_id, error_msg)){}; RemoteException(const slot_metadata& slot_info, const plugin_metadata& plugin_info, std::string_view error_msg) noexcept - : msg_(fmt::format("Remote exception for plugin '{}-{}' running at [{}] for slot '{}': {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info.slot_id, error_msg)) + : msg_(fmt::format( + "Remote exception for plugin '{}-{}' running at [{}] for slot '{}': {}", + plugin_info.plugin_name, + plugin_info.plugin_version, + plugin_info.peer, + slot_info.slot_id, + error_msg)) { } diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 6035f8617d..7f8b11062a 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -4,15 +4,14 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_METADATA_H #define CURAENGINE_INCLUDE_PLUGINS_METADATA_H +#include "plugins/types.h" + #include +#include #include #include - -#include #include -#include "plugins/types.h" - namespace cura::plugins { diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index fab675a106..b70e5482a9 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -4,22 +4,12 @@ #ifndef PLUGINS_PLUGINPROXY_H #define PLUGINS_PLUGINPROXY_H -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "Application.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" +#include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" +#include "cura/plugins/slots/handshake/v0/handshake.pb.h" +#include "cura/plugins/v0/slot_id.pb.h" #include "plugins/broadcasts.h" #include "plugins/exception.h" #include "plugins/metadata.h" @@ -27,11 +17,20 @@ #include "utils/types/char_range_literal.h" #include "utils/types/generic.h" -#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" -#include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" -#include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" -#include "cura/plugins/slots/handshake/v0/handshake.pb.h" -#include "cura/plugins/v0/slot_id.pb.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace cura::plugins { @@ -79,7 +78,9 @@ class PluginProxy */ constexpr PluginProxy() = default; - explicit PluginProxy(std::shared_ptr channel) : modify_stub_(channel), broadcast_stub_(channel) + explicit PluginProxy(std::shared_ptr channel) + : modify_stub_(channel) + , broadcast_stub_(channel) { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -174,7 +175,12 @@ class PluginProxy grpc::Status status; boost::asio::co_spawn( - grpc_context, [this, &grpc_context, &status, &ret_value, &args...]() { return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); }, boost::asio::detached); + grpc_context, + [this, &grpc_context, &status, &ret_value, &args...]() + { + return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); + }, + boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes @@ -199,7 +205,12 @@ class PluginProxy grpc::Status status; boost::asio::co_spawn( - grpc_context, [this, &grpc_context, &status, &args...]() { return this->broadcastCall(grpc_context, status, std::forward(args)...); }, boost::asio::detached); + grpc_context, + [this, &grpc_context, &status, &args...]() + { + return this->broadcastCall(grpc_context, status, std::forward(args)...); + }, + boost::asio::detached); grpc_context.run(); if (! status.ok()) // TODO: handle different kind of status codes @@ -220,7 +231,9 @@ class PluginProxy ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. - slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. + slot_metadata slot_info_{ .slot_id = SlotID, + .version_range = SlotVersionRng.value, + .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake /** diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 37307a9ce1..c674ef48f6 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -4,20 +4,19 @@ #ifndef PLUGINS_SLOTPROXY_H #define PLUGINS_SLOTPROXY_H -#include -#include -#include -#include - -#include -#include - #include "plugins/converters.h" #include "plugins/pluginproxy.h" #include "plugins/types.h" #include "plugins/validator.h" #include "utils/types/char_range_literal.h" +#include +#include +#include +#include +#include +#include + namespace cura::plugins { @@ -58,7 +57,8 @@ class SlotProxy * * @param channel A shared pointer to the gRPC channel for communication with the plugin. */ - SlotProxy(std::shared_ptr channel) : plugin_{ std::move(channel) } {}; + SlotProxy(std::shared_ptr channel) + : plugin_{ std::move(channel) } {}; /** * @brief Executes the plugin operation. @@ -81,7 +81,7 @@ class SlotProxy } template - void broadcast(auto&&...args) + void broadcast(auto&&... args) { if (plugin_.has_value()) { diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 50743df859..0117ccc7c0 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -4,9 +4,9 @@ #ifndef PLUGINS_SLOTS_H #define PLUGINS_SLOTS_H -#include -#include - +#include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" +#include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" +#include "cura/plugins/v0/slot_id.pb.h" #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" @@ -15,9 +15,8 @@ #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed #include "utils/types/char_range_literal.h" -#include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" -#include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" -#include "cura/plugins/v0/slot_id.pb.h" +#include +#include namespace cura { @@ -60,7 +59,8 @@ using slot_simplify_ = SlotProxy -using slot_postprocess_ = SlotProxy; +using slot_postprocess_ + = SlotProxy; template struct Typelist diff --git a/include/plugins/types.h b/include/plugins/types.h index b24dc4f0b1..e46c2562b8 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -4,16 +4,15 @@ #ifndef PLUGINS_TYPES_H #define PLUGINS_TYPES_H -#include -#include - -#include -#include - +#include "cura/plugins/v0/slot_id.pb.h" #include "utils/IntPoint.h" #include "utils/polygon.h" -#include "cura/plugins/v0/slot_id.pb.h" +#include + +#include +#include +#include namespace fmt { diff --git a/include/plugins/validator.h b/include/plugins/validator.h index fb61db1cf8..0d18c074a3 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -4,12 +4,13 @@ #ifndef PLUGINS_VALIDATOR_H #define PLUGINS_VALIDATOR_H +#include "plugins/metadata.h" +#include "plugins/types.h" + #include -#include #include -#include "plugins/metadata.h" -#include "plugins/types.h" +#include namespace cura::plugins { diff --git a/include/utils/channel.h b/include/utils/channel.h index 30dbb5dff2..4ff915ffe7 100644 --- a/include/utils/channel.h +++ b/include/utils/channel.h @@ -14,15 +14,15 @@ namespace cura::utils { - struct ChannelSetupConfiguration - { - public: - std::string host; - uint64_t port; - uint64_t shibolet = 0UL; //TODO: Either get from startup (server would receive this as well) or remove completely. - }; +struct ChannelSetupConfiguration +{ +public: + std::string host; + uint64_t port; + uint64_t shibolet = 0UL; // TODO: Either get from startup (server would receive this as well) or remove completely. +}; - std::shared_ptr createChannel(const ChannelSetupConfiguration& config = { "localhost", 50010UL }); +std::shared_ptr createChannel(const ChannelSetupConfiguration& config = { "localhost", 50010UL }); } // namespace cura::utils diff --git a/include/utils/format/thread_id.h b/include/utils/format/thread_id.h index 48bf1158b1..0806144200 100644 --- a/include/utils/format/thread_id.h +++ b/include/utils/format/thread_id.h @@ -4,12 +4,12 @@ #ifndef UTILS_FORMAT_THREAD_ID_H #define UTILS_FORMAT_THREAD_ID_H +#include + #include #include #include -#include - namespace fmt { template<> diff --git a/include/utils/gettime.h b/include/utils/gettime.h index 52bc73556a..319f5d750c 100644 --- a/include/utils/gettime.h +++ b/include/utils/gettime.h @@ -1,20 +1,20 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2020 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef GETTIME_H #define GETTIME_H #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN 1 - #include +#define WIN32_LEAN_AND_MEAN 1 +#include #else - #ifdef USE_CPU_TIME - #include +#ifdef USE_CPU_TIME +#include #endif -#include -#include #include +#include +#include #endif namespace cura @@ -24,23 +24,23 @@ static inline double getTime() #ifdef _WIN32 return double(GetTickCount()) / 1000.0; #else // not __WIN32 - #if USE_CPU_TIME // Use cpu usage time if available, otherwise wall clock time +#if USE_CPU_TIME // Use cpu usage time if available, otherwise wall clock time struct rusage usage; - #ifdef DEBUG - int ret = getrusage(RUSAGE_SELF, &usage); - assert(ret == 0); - ((void)ret); - #else - getrusage(RUSAGE_SELF, &usage); - #endif - double user_time = double(usage.ru_utime.tv_sec) + double(usage.ru_utime.tv_usec) / 1000000.0; - double sys_time = double(usage.ru_stime.tv_sec) + double(usage.ru_stime.tv_usec) / 1000000.0; +#ifdef DEBUG + int ret = getrusage(RUSAGE_SELF, &usage); + assert(ret == 0); + ((void)ret); +#else + getrusage(RUSAGE_SELF, &usage); +#endif + double user_time = double(usage.ru_utime.tv_sec) + double(usage.ru_utime.tv_usec) / 1000000.0; + double sys_time = double(usage.ru_stime.tv_sec) + double(usage.ru_stime.tv_usec) / 1000000.0; return user_time + sys_time; - #else // not USE_CPU_TIME +#else // not USE_CPU_TIME struct timeval tv; gettimeofday(&tv, nullptr); return double(tv.tv_sec) + double(tv.tv_usec) / 1000000.0; - #endif // USE_CPU_TIME +#endif // USE_CPU_TIME #endif // __WIN32 } @@ -48,11 +48,12 @@ class TimeKeeper { private: double startTime; + public: TimeKeeper(); - + double restart(); }; -}//namespace cura -#endif//GETTIME_H +} // namespace cura +#endif // GETTIME_H diff --git a/include/utils/resources/certificate.pem.h b/include/utils/resources/certificate.pem.h index 93a1bf49c1..534492366c 100644 --- a/include/utils/resources/certificate.pem.h +++ b/include/utils/resources/certificate.pem.h @@ -7,8 +7,8 @@ namespace cura::utils::resources { - constexpr std::string_view certificate = -R"#(-----BEGIN CERTIFICATE----- +constexpr std::string_view certificate = + R"#(-----BEGIN CERTIFICATE----- MIIF8TCCA9mgAwIBAgIUd6fjXFbNBMZOxfBnv4FOokHlOC4wDQYJKoZIhvcNAQEL BQAwgYYxCzAJBgNVBAYTAk5MMRMwEQYDVQQIDApHZWxkZXJsYW5kMRUwEwYDVQQH DAxHZWxkZXJtYWxzZW4xEjAQBgNVBAoMCVVsdGlNYWtlcjEbMBkGA1UECwwSQ29t @@ -46,5 +46,4 @@ c3dQRszg9OTAyUwNKQEAuZ9II/cCpLyE+GbnqRdkErHgO7kiKQ== } // namespace cura::utils::resources -#endif //CERTIFICATE_H - +#endif // CERTIFICATE_H diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 7f04d154cf..73e176d996 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -4,14 +4,14 @@ #ifndef CURAENGINE_GENERIC_H #define CURAENGINE_GENERIC_H -#include -#include -#include +#include "utils/types/char_range_literal.h" -#include #include -#include "utils/types/char_range_literal.h" +#include +#include +#include +#include namespace cura::utils { diff --git a/src/Application.cpp b/src/Application.cpp index a31577ff70..acd28f0007 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -3,40 +3,41 @@ #include "Application.h" -#include -#include -#include - -#include //For generating a UUID. -#include //For generating a UUID. -#include -#include -#include -#include -#include -#include -#include -#include - #include "FffProcessor.h" #include "communication/ArcusCommunication.h" //To connect via Arcus to the front-end. #include "communication/CommandLine.h" //To use the command line to slice stuff. +#include "plugins/slots.h" #include "progress/Progress.h" #include "utils/ThreadPool.h" #include "utils/string.h" //For stringcasecompare. -#include "plugins/slots.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include //For generating a UUID. +#include //For generating a UUID. +#include +#include +#include namespace cura { -Application::Application() : instance_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) +Application::Application() + : instance_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) { auto dup_sink = std::make_shared(std::chrono::seconds{ 10 }); auto base_sink = std::make_shared(); dup_sink->add_sink(base_sink); - spdlog::default_logger()->sinks() = std::vector>{ dup_sink }; // replace default_logger sinks with the duplicating filtering sink to avoid spamming + spdlog::default_logger()->sinks() + = std::vector>{ dup_sink }; // replace default_logger sinks with the duplicating filtering sink to avoid spamming if (auto spdlog_val = spdlog::details::os::getenv("CURAENGINE_LOG_LEVEL"); ! spdlog_val.empty()) { @@ -139,9 +140,11 @@ void Application::printHelp() const fmt::print(" -o \n\tSpecify a file to which to write the generated gcode.\n"); fmt::print("\n"); fmt::print("The settings are appended to the last supplied object:\n"); - fmt::print("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e0 [extruder train 0 settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n"); + fmt::print("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e0 [extruder train 0 settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object " + "settings] \n\t--next [next group settings]\n\t... etc.\n"); fmt::print("\n"); - fmt::print("In order to load machine definitions from custom locations, you need to create the environment variable CURA_ENGINE_SEARCH_PATH, which should contain all search paths delimited by a (semi-)colon.\n"); + fmt::print("In order to load machine definitions from custom locations, you need to create the environment variable CURA_ENGINE_SEARCH_PATH, which should contain all search " + "paths delimited by a (semi-)colon.\n"); fmt::print("\n"); } diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 708c84f9c1..9586059a53 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1,18 +1,10 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include // numeric_limits -#include -#include -#include - -#include -#include +#include "FffGcodeWriter.h" #include "Application.h" #include "ExtruderTrain.h" -#include "FffGcodeWriter.h" #include "FffProcessor.h" #include "InsetOrderOptimizer.h" #include "LayerPlan.h" @@ -29,10 +21,22 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include +#include + +#include +#include // numeric_limits +#include +#include +#include + namespace cura { -FffGcodeWriter::FffGcodeWriter() : max_object_height(0), layer_plan_buffer(gcode), slice_uuid(Application::getInstance().instance_uuid) +FffGcodeWriter::FffGcodeWriter() + : max_object_height(0) + , layer_plan_buffer(gcode) + , slice_uuid(Application::getInstance().instance_uuid) { for (unsigned int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++) { // initialize all as max layer_nr, so that they get updated to the lowest layer on which they are used. @@ -89,7 +93,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep { auto should_prime_extruder = gcode.initializeExtruderTrains(storage, start_extruder_nr); - if (!should_prime_extruder) + if (! should_prime_extruder) { // set to most negative number so that layer processing never primes this extruder anymore. extruder_prime_layer_nr[start_extruder_nr] = std::numeric_limits::min(); @@ -157,7 +161,10 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep run_multiple_producers_ordered_consumer( process_layer_starting_layer_nr, total_layers, - [&storage, total_layers, this](int layer_nr) { return &processLayer(storage, layer_nr, total_layers); }, + [&storage, total_layers, this](int layer_nr) + { + return &processLayer(storage, layer_nr, total_layers); + }, [this, total_layers](LayerPlan* gcode_layer) { Progress::messageProgress(Progress::Stage::EXPORT, std::max(0, gcode_layer->getLayerNr()) + 1, total_layers); @@ -326,7 +333,8 @@ static void retractionAndWipeConfigFromSettings(const Settings& settings, Retrac switch_retraction_config.primeSpeed = settings.get("switch_extruder_prime_speed"); switch_retraction_config.zHop = settings.get("retraction_hop_after_extruder_switch_height"); switch_retraction_config.retraction_min_travel_distance = 0; // No limitation on travel distance for an extruder switch retract. - switch_retraction_config.retraction_extrusion_window = 99999.9; // So that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions). + switch_retraction_config.retraction_extrusion_window + = 99999.9; // So that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions). switch_retraction_config.retraction_count_max = 9999999; // Extruder switch retraction is never limited. WipeScriptConfig& wipe_config = config->wipe_config; @@ -362,7 +370,7 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for(SliceMeshStorage& mesh: storage.meshes) + for (SliceMeshStorage& mesh : storage.meshes) { retractionAndWipeConfigFromSettings(mesh.settings, &mesh.retraction_wipe_config); } @@ -373,24 +381,21 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const EPlatformAdhesion adhesion_type = mesh_group_settings.get("adhesion_type"); const int skirt_brim_extruder_nr = mesh_group_settings.get("skirt_brim_extruder_nr"); - const ExtruderTrain* skirt_brim_extruder = (skirt_brim_extruder_nr < 0)? nullptr : &mesh_group_settings.get("skirt_brim_extruder_nr"); + const ExtruderTrain* skirt_brim_extruder = (skirt_brim_extruder_nr < 0) ? nullptr : &mesh_group_settings.get("skirt_brim_extruder_nr"); size_t start_extruder_nr; - if (adhesion_type == EPlatformAdhesion::SKIRT - && skirt_brim_extruder + if (adhesion_type == EPlatformAdhesion::SKIRT && skirt_brim_extruder && (skirt_brim_extruder->settings.get("skirt_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } - else if ((adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) - && skirt_brim_extruder + else if ( + (adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) && skirt_brim_extruder && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } - else if (adhesion_type == EPlatformAdhesion::RAFT - && skirt_brim_extruder - ) + else if (adhesion_type == EPlatformAdhesion::RAFT && skirt_brim_extruder) { start_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; } @@ -482,7 +487,8 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) storage.support.support_infill_angles_layer_0.push_back(0); } - auto getInterfaceAngles = [&storage](const ExtruderTrain& extruder, const std::string& interface_angles_setting, const EFillMethod pattern, const std::string& interface_height_setting) + auto getInterfaceAngles + = [&storage](const ExtruderTrain& extruder, const std::string& interface_angles_setting, const EFillMethod pattern, const std::string& interface_height_setting) { std::vector angles = extruder.settings.get>(interface_angles_setting); if (angles.empty()) @@ -499,7 +505,8 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) { for (const SliceMeshStorage& mesh : storage.meshes) { - if (mesh.settings.get(interface_height_setting) >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) + if (mesh.settings.get(interface_height_setting) + >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) { // Some roofs are quite thick. // Alternate between the two kinds of diagonal: / and \ . @@ -517,10 +524,12 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) }; const ExtruderTrain& roof_extruder = mesh_group_settings.get("support_roof_extruder_nr"); - storage.support.support_roof_angles = getInterfaceAngles(roof_extruder, "support_roof_angles", roof_extruder.settings.get("support_roof_pattern"), "support_roof_height"); + storage.support.support_roof_angles + = getInterfaceAngles(roof_extruder, "support_roof_angles", roof_extruder.settings.get("support_roof_pattern"), "support_roof_height"); const ExtruderTrain& bottom_extruder = mesh_group_settings.get("support_bottom_extruder_nr"); - storage.support.support_bottom_angles = getInterfaceAngles(bottom_extruder, "support_bottom_angles", bottom_extruder.settings.get("support_bottom_pattern"), "support_bottom_height"); + storage.support.support_bottom_angles + = getInterfaceAngles(bottom_extruder, "support_bottom_angles", bottom_extruder.settings.get("support_bottom_pattern"), "support_bottom_height"); } void FffGcodeWriter::processNextMeshGroupCode(const SliceDataStorage& storage) @@ -568,7 +577,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) z += layer_height; const coord_t comb_offset = base_settings.get("raft_base_line_spacing"); - std::vector fan_speed_layer_time_settings_per_extruder_raft_base = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_base + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_base) { double regular_fan_speed = base_settings.get("raft_base_fan_speed") * 100.0; @@ -578,7 +588,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const coord_t line_width = base_settings.get("raft_base_line_width"); const coord_t avoid_distance = base_settings.get("travel_avoid_distance"); - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); + LayerPlan& gcode_layer + = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); gcode_layer.setIsInside(true); gcode_layer.setExtruder(base_extruder_nr); @@ -614,29 +625,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::LINES, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - gcode_layer.configs_storage.raft_base_config.getLineWidth(), - line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::LINES, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + gcode_layer.configs_storage.raft_base_config.getLineWidth(), + line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); if (! raft_paths.empty()) @@ -644,7 +656,22 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, base_settings, base_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, base_extruder_nr, base_extruder_nr, z_seam_config, raft_paths); + *this, + storage, + gcode_layer, + base_settings, + base_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + base_extruder_nr, + base_extruder_nr, + z_seam_config, + raft_paths); wall_orderer.addToLayer(); } gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); @@ -670,7 +697,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const LayerIndex layer_nr = initial_raft_layer_nr + raft_interface_layer; z += interface_layer_height; - std::vector fan_speed_layer_time_settings_per_extruder_raft_interface = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_interface + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_interface) { const double regular_fan_speed = interface_fan_speed * 100.0; @@ -679,7 +707,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } const coord_t comb_offset = interface_line_spacing; - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, interface_layer_height, current_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_interface, comb_offset, interface_line_width, interface_avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + interface_layer_height, + current_extruder_nr, + fan_speed_layer_time_settings_per_extruder_raft_interface, + comb_offset, + interface_line_width, + interface_avoid_distance); gcode_layer.setIsInside(true); current_extruder_nr = interface_extruder_nr; @@ -687,7 +724,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, interface_layer_height); std::vector raft_outline_paths; - const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. + const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() + / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. if (storage.primeRaftOutline.area() > 0) { raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); @@ -713,29 +751,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - interface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - interface_max_resolution, - interface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + interface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + interface_max_resolution, + interface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); @@ -761,7 +800,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const LayerIndex layer_nr = initial_raft_layer_nr + 1 + num_interface_layers + raft_surface_layer - 1; // +1: 1 base layer z += surface_layer_height; - std::vector fan_speed_layer_time_settings_per_extruder_raft_surface = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_surface + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_surface) { const double regular_fan_speed = surface_fan_speed * 100.0; @@ -770,7 +810,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } const coord_t comb_offset = surface_line_spacing; - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, surface_layer_height, current_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_surface, comb_offset, surface_line_width, surface_avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + surface_layer_height, + current_extruder_nr, + fan_speed_layer_time_settings_per_extruder_raft_surface, + comb_offset, + surface_line_width, + surface_avoid_distance); gcode_layer.setIsInside(true); // make sure that we are using the correct extruder to print raft @@ -779,7 +828,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, surface_layer_height); std::vector raft_outline_paths; - const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. + const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() + / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. if (storage.primeRaftOutline.area() > 0) { raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); @@ -789,7 +839,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); Polygons raft_lines; - AngleDegrees fill_angle = (num_surface_layers - raft_surface_layer) % 2 ? 45 : 135; // Alternate between -45 and +45 degrees, ending up 90 degrees rotated from the default skin angle. + AngleDegrees fill_angle + = (num_surface_layers - raft_surface_layer) % 2 ? 45 : 135; // Alternate between -45 and +45 degrees, ending up 90 degrees rotated from the default skin angle. constexpr bool zig_zaggify_infill = true; constexpr size_t wall_line_count = 0; @@ -805,29 +856,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - surface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - surface_max_resolution, - surface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + surface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + surface_max_resolution, + surface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); @@ -863,8 +915,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn // find printZ of first actual printed mesh for (const SliceMeshStorage& mesh : storage.meshes) { - if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("cutting_mesh") - || mesh.settings.get("infill_mesh")) + if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") + || mesh.settings.get("cutting_mesh") || mesh.settings.get("infill_mesh")) { continue; } @@ -909,11 +961,22 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } const coord_t comb_offset_from_outlines = max_inner_wall_width * 2; - assert(static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); - const std::vector& extruder_order = (layer_nr < 0) ? extruder_order_per_layer_negative_layers[extruder_order_per_layer_negative_layers.size() + layer_nr] : extruder_order_per_layer[layer_nr]; + assert( + static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); + const std::vector& extruder_order + = (layer_nr < 0) ? extruder_order_per_layer_negative_layers[extruder_order_per_layer_negative_layers.size() + layer_nr] : extruder_order_per_layer[layer_nr]; const coord_t first_outer_wall_line_width = scene.extruders[extruder_order.front()].settings.get("wall_line_width_0"); - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, layer_thickness, extruder_order.front(), fan_speed_layer_time_settings_per_extruder, comb_offset_from_outlines, first_outer_wall_line_width, avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + layer_thickness, + extruder_order.front(), + fan_speed_layer_time_settings_per_extruder, + comb_offset_from_outlines, + first_outer_wall_line_width, + avoid_distance); if (include_helper_parts) { @@ -931,7 +994,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - const size_t support_infill_extruder_nr = (layer_nr <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + const size_t support_infill_extruder_nr = (layer_nr <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; for (const size_t& extruder_nr : extruder_order) { @@ -959,7 +1023,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; const PathConfigStorage::MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; if (mesh.settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE - && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! + && extruder_nr + == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! ) { addMeshLayerToGCode_meshSurfaceMode(storage, mesh, mesh_config, gcode_layer); @@ -1005,7 +1070,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const int skirt_height = train.settings.get("skirt_height"); const bool is_skirt = train.settings.get("adhesion_type") == EPlatformAdhesion::SKIRT; // only create a multilayer SkirtBrim for a skirt for the height of skirt_height - if (layer_nr != 0 && (layer_nr >= skirt_height || !is_skirt)) + if (layer_nr != 0 && (layer_nr >= skirt_height || ! is_skirt)) { return; } @@ -1014,7 +1079,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan return; } gcode_layer.setSkirtBrimIsPlanned(extruder_nr); - + const auto& original_skirt_brim = storage.skirt_brim[extruder_nr]; if (original_skirt_brim.size() == 0) { @@ -1040,7 +1105,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const size_t inset_idx; ConstPolygonPointer poly; }; - + size_t total_line_count = 0; for (const SkirtBrimLine& line : storage.skirt_brim[extruder_nr]) { @@ -1048,7 +1113,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan total_line_count += line.open_polylines.size(); } Polygons all_brim_lines; - + // Add the support brim before the below algorithm which takes order requirements into account // For support brim we don't care about the order, because support doesn't need to be accurate. const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; @@ -1059,7 +1124,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan support_brim_lines.toPolylines(); all_brim_lines = support_brim_lines; } - + all_brim_lines.reserve(total_line_count); const coord_t line_w = train.settings.get("skirt_brim_line_width") * train.settings.get("initial_layer_line_width_factor"); @@ -1089,7 +1154,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan ConstPolygonPointer pp(all_brim_lines.back()); for (Point p : line) { - grid.insert(p, BrimLineReference{inset_idx, pp}); + grid.insert(p, BrimLineReference{ inset_idx, pp }); } } } @@ -1143,7 +1208,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan } } assert(all_brim_lines.size() == total_line_count); // Otherwise pointers would have gotten invalidated - + const bool enable_travel_optimization = true; // Use the combing outline while deciding in which order to print the lines. Can't hurt for only one layer. const coord_t wipe_dist = 0u; const Ratio flow_ratio = 1.0; @@ -1154,8 +1219,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan Polygons inner_brim_line; inner_brim_line.add(all_brim_lines[0]); - gcode_layer.addLinesByOptimizer - ( + gcode_layer.addLinesByOptimizer( layer_nr == 0 ? all_brim_lines : inner_brim_line, gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], SpaceFillType::PolyLines, @@ -1165,8 +1229,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan start_close_to, fan_speed, reverse_print_direction, - order_requirements - ); + order_requirements); } void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const @@ -1325,7 +1388,11 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s return ret; } -void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { @@ -1348,7 +1415,11 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& polygons = Simplify(mesh.settings).polygon(polygons); - ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); const bool spiralize = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("magic_spiralize"); gcode_layer.addPolygonsByOptimizer(polygons, mesh_config.inset0_config, z_seam_config, mesh.settings.get("wall_0_wipe_dist"), spiralize); @@ -1362,7 +1433,12 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, c gcode_layer.addLinesByOptimizer(layer->openPolyLines, mesh_config.inset0_config, SpaceFillType::PolyLines); } -void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { @@ -1386,7 +1462,11 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const ZSeamConfig z_seam_config; if (mesh.isPrinted()) //"normal" meshes with walls, skin, infill, etc. get the traditional part ordering based on the z-seam settings. { - z_seam_config = ZSeamConfig(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + z_seam_config = ZSeamConfig( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); } PathOrderOptimizer part_order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config); for (const SliceLayerPart& part : layer.parts) @@ -1411,8 +1491,13 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const gcode_layer.setMesh(nullptr); } -void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) - const +void FffGcodeWriter::addMeshPartToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + LayerPlan& gcode_layer) const { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; @@ -1433,7 +1518,8 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S added_something = added_something | processSkin(storage, gcode_layer, mesh, extruder_nr, mesh_config, part); // After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. - if (added_something && (! mesh_group_settings.get("magic_spiralize") || gcode_layer.getLayerNr() < static_cast(mesh.settings.get("initial_bottom_layers")))) + if (added_something + && (! mesh_group_settings.get("magic_spiralize") || gcode_layer.getLayerNr() < static_cast(mesh.settings.get("initial_bottom_layers")))) { coord_t innermost_wall_line_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (gcode_layer.getLayerNr() == 0) @@ -1446,7 +1532,13 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S gcode_layer.setIsInside(false); } -bool FffGcodeWriter::processInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1457,8 +1549,13 @@ bool FffGcodeWriter::processInfill(const SliceDataStorage& storage, LayerPlan& g return added_something; } -bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) - const +bool FffGcodeWriter::processMultiLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1474,7 +1571,8 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La AngleDegrees infill_angle = 45; // Original default. This will get updated to an element from mesh->infill_angles. if (! mesh.infill_angles.empty()) { - const size_t combined_infill_layers = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); + const size_t combined_infill_layers + = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); infill_angle = mesh.infill_angles.at((gcode_layer.getLayerNr() / combined_infill_layers) % mesh.infill_angles.size()); } const Point3 mesh_middle = mesh.bounding_box.getMiddle(); @@ -1517,30 +1615,40 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La { lightning_layer = &mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr()); } - Infill infill_comp(infill_pattern, - zig_zaggify_infill, - connect_polygons, - part.infill_area_per_combine_per_density[density_idx][combine_idx], - infill_line_width, - infill_line_distance_here, - infill_overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - mesh.settings.get("cross_infill_pocket_size")); - infill_comp.generate(infill_paths, infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + infill_pattern, + zig_zaggify_infill, + connect_polygons, + part.infill_area_per_combine_per_density[density_idx][combine_idx], + infill_line_width, + infill_line_distance_here, + infill_overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + mesh.settings.get("cross_infill_pocket_size")); + infill_comp.generate( + infill_paths, + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); } if (! infill_lines.empty() || ! infill_polygons.empty()) { @@ -1564,25 +1672,27 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); - gcode_layer.addLinesByOptimizer(infill_lines, - mesh_config.infill_config[combine_idx], - zig_zaggify_infill ? SpaceFillType::PolyLines : SpaceFillType::Lines, - enable_travel_optimization, - /*wipe_dist = */ 0, - /* flow = */ 1.0, - near_start_location); + gcode_layer.addLinesByOptimizer( + infill_lines, + mesh_config.infill_config[combine_idx], + zig_zaggify_infill ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + /*wipe_dist = */ 0, + /* flow = */ 1.0, + near_start_location); } } } return added_something; } -bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SliceLayerPart& part) const +bool FffGcodeWriter::processSingleLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1613,7 +1723,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, AngleDegrees infill_angle = 45; // Original default. This will get updated to an element from mesh->infill_angles. if (! mesh.infill_angles.empty()) { - const size_t combined_infill_layers = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); + const size_t combined_infill_layers + = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); infill_angle = mesh.infill_angles.at((static_cast(gcode_layer.getLayerNr()) / combined_infill_layers) % mesh.infill_angles.size()); } const Point3 mesh_middle = mesh.bounding_box.getMiddle(); @@ -1710,30 +1821,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. wall_tool_paths.emplace_back(std::vector()); const coord_t overlap = infill_overlap - (density_idx == last_idx ? 0 : wall_line_count * infill_line_width); - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - infill_below_skin, - infill_line_width, - infill_line_distance_here, - overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - skin_below_wall_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_tool_paths.back(), infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_below_skin, + infill_line_width, + infill_line_distance_here, + overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + skin_below_wall_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_tool_paths.back(), + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, min_skin_below_wall_count); @@ -1765,30 +1886,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, constexpr coord_t overlap = 0; // overlap is already applied for the sparsest density in the generateGradualInfill wall_tool_paths.emplace_back(); - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - in_outline, - infill_line_width, - infill_line_distance_here, - overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - wall_line_count_here, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_tool_paths.back(), infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + in_outline, + infill_line_width, + infill_line_distance_here, + overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + wall_line_count_here, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_tool_paths.back(), + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, wall_line_count); @@ -1800,8 +1931,21 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, } wall_tool_paths.emplace_back(part.infill_wall_toolpaths); // The extra infill walls were generated separately. Add these too. - const bool walls_generated = - std::any_of(wall_tool_paths.cbegin(), wall_tool_paths.cend(), [](const std::vector& tp) { return ! (tp.empty() || std::all_of(tp.begin(), tp.end(), [](const VariableWidthLines& vwl) { return vwl.empty(); })); }); + const bool walls_generated = std::any_of( + wall_tool_paths.cbegin(), + wall_tool_paths.cend(), + [](const std::vector& tp) + { + return ! ( + tp.empty() + || std::all_of( + tp.begin(), + tp.end(), + [](const VariableWidthLines& vwl) + { + return vwl.empty(); + })); + }); if (! infill_lines.empty() || ! infill_polygons.empty() || walls_generated) { added_something = true; @@ -1822,7 +1966,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, else // So walls_generated must be true. { std::vector* start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; - while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. Should almost always be very quick. + while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. + // Should almost always be very quick. { start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; } @@ -1835,23 +1980,28 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh_config.infill_config[0].getLineWidth() * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.infill_config[0], - mesh_config.infill_config[0], - mesh_config.infill_config[0], - mesh_config.infill_config[0], - retract_before_outer_wall, - wipe_dist, - wipe_dist, - extruder_nr, - extruder_nr, - z_seam_config, - tool_paths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh_config.infill_config[0].getLineWidth() * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + tool_paths); added_something |= wall_orderer.addToLayer(); } } @@ -1863,21 +2013,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[0], ZSeamConfig(), 0, false, 1.0_r, false, false, near_start_location); } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesByOptimizer(infill_lines, mesh_config.infill_config[0], SpaceFillType::Lines, enable_travel_optimization, mesh.settings.get("infill_wipe_dist"), /*float_ratio = */ 1.0, near_start_location); + gcode_layer.addLinesByOptimizer( + infill_lines, + mesh_config.infill_config[0], + SpaceFillType::Lines, + enable_travel_optimization, + mesh.settings.get("infill_wipe_dist"), + /*float_ratio = */ 1.0, + near_start_location); } else { gcode_layer.addLinesByOptimizer( - infill_lines, mesh_config.infill_config[0], (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, enable_travel_optimization, /* wipe_dist = */ 0, /*float_ratio = */ 1.0, near_start_location); + infill_lines, + mesh_config.infill_config[0], + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + /* wipe_dist = */ 0, + /*float_ratio = */ 1.0, + near_start_location); } } return added_something; } -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) +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; const auto skin_edge_support_layers = mesh.settings.get("skin_edge_support_layers"); @@ -1962,7 +2131,8 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol } } - // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a bump + // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a + // bump infill_below_skin = skin_above_combined.intersection(part.infill_area_per_combine_per_density.back().front()).offset(-infill_line_width).offset(infill_line_width); constexpr bool remove_small_holes_from_infill_below_skin = true; @@ -1982,7 +2152,12 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol return ! infill_below_skin_overlap.empty() && ! infill_not_below_skin.empty(); } -void FffGcodeWriter::processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const +void FffGcodeWriter::processSpiralizedWall( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + const SliceMeshStorage& mesh) const { if (part.spiral_wall.empty()) { @@ -2008,11 +2183,18 @@ void FffGcodeWriter::processSpiralizedWall(const SliceDataStorage& storage, Laye // output a wall slice that is interpolated between the last and current walls for (const ConstPolygonRef& wall_outline : part.spiral_wall) { - gcode_layer.spiralizeWallSlice(mesh_config.inset0_config, wall_outline, ConstPolygonRef(*last_wall_outline), seam_vertex_idx, last_seam_vertex_idx, is_top_layer, is_bottom_layer); + gcode_layer + .spiralizeWallSlice(mesh_config.inset0_config, wall_outline, ConstPolygonRef(*last_wall_outline), seam_vertex_idx, last_seam_vertex_idx, is_top_layer, is_bottom_layer); } } -bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processInsets( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { bool added_something = false; if (extruder_nr != mesh.settings.get("wall_0_extruder_nr").extruder_nr && extruder_nr != mesh.settings.get("wall_x_extruder_nr").extruder_nr) @@ -2029,7 +2211,8 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { const size_t initial_bottom_layers = mesh.settings.get("initial_bottom_layers"); const int layer_nr = gcode_layer.getLayerNr(); - if ((layer_nr < static_cast(initial_bottom_layers) && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths + if ((layer_nr < static_cast(initial_bottom_layers) + && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths || (layer_nr >= static_cast(initial_bottom_layers) && part.spiral_wall.empty())) // The rest of the layers in spiralize mode are using the spiral wall { // nothing to do @@ -2039,7 +2222,8 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { spiralize = true; } - if (spiralize && gcode_layer.getLayerNr() == static_cast(initial_bottom_layers) && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr) + if (spiralize && gcode_layer.getLayerNr() == static_cast(initial_bottom_layers) + && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr) { // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward added_something = true; gcode_layer.setIsInside(true); // going to print stuff inside print object @@ -2189,23 +2373,28 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { // Main case: Optimize the insets with the InsetOrderOptimizer. const coord_t wall_x_wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.inset0_config, - mesh_config.insetX_config, - mesh_config.bridge_inset0_config, - mesh_config.bridge_insetX_config, - mesh.settings.get("travel_retract_before_outer_wall"), - mesh.settings.get("wall_0_wipe_dist"), - wall_x_wipe_dist, - mesh.settings.get("wall_0_extruder_nr").extruder_nr, - mesh.settings.get("wall_x_extruder_nr").extruder_nr, - z_seam_config, - part.wall_toolpaths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.inset0_config, + mesh_config.insetX_config, + mesh_config.bridge_inset0_config, + mesh_config.bridge_insetX_config, + mesh.settings.get("travel_retract_before_outer_wall"), + mesh.settings.get("wall_0_wipe_dist"), + wall_x_wipe_dist, + mesh.settings.get("wall_0_extruder_nr").extruder_nr, + mesh.settings.get("wall_x_extruder_nr").extruder_nr, + z_seam_config, + part.wall_toolpaths); added_something |= wall_orderer.addToLayer(); } return added_something; @@ -2244,7 +2433,13 @@ std::optional FffGcodeWriter::getSeamAvoidingLocation(const Polygons& fil } } -bool FffGcodeWriter::processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processSkin( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { const size_t top_bottom_extruder_nr = mesh.settings.get("top_bottom_extruder_nr").extruder_nr; const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr; @@ -2273,7 +2468,13 @@ bool FffGcodeWriter::processSkin(const SliceDataStorage& storage, LayerPlan& gco return added_something; } -bool FffGcodeWriter::processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part) const +bool FffGcodeWriter::processSkinPart( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part) const { bool added_something = false; @@ -2286,13 +2487,14 @@ bool FffGcodeWriter::processSkinPart(const SliceDataStorage& storage, LayerPlan& return added_something; } -void FffGcodeWriter::processRoofing(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SkinPart& skin_part, - bool& added_something) const +void FffGcodeWriter::processRoofing( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const { const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr; if (extruder_nr != roofing_extruder_nr) @@ -2310,16 +2512,30 @@ void FffGcodeWriter::processRoofing(const SliceDataStorage& storage, const Ratio skin_density = 1.0; const coord_t skin_overlap = 0; // skinfill already expanded over the roofing areas; don't overlap with perimeters const bool monotonic = mesh.settings.get("roofing_monotonic"); - processSkinPrintFeature(storage, gcode_layer, mesh, mesh_config, extruder_nr, skin_part.roofing_fill, mesh_config.roofing_config, pattern, roofing_angle, skin_overlap, skin_density, monotonic, added_something); + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + mesh_config, + extruder_nr, + skin_part.roofing_fill, + mesh_config.roofing_config, + pattern, + roofing_angle, + skin_overlap, + skin_density, + monotonic, + added_something); } -void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SkinPart& skin_part, - bool& added_something) const +void FffGcodeWriter::processTopBottom( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const { if (skin_part.skin_fill.empty()) { @@ -2345,7 +2561,7 @@ void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, // generate skin_polygons and skin_lines const GCodePathConfig* skin_config = &mesh_config.skin_config; Ratio skin_density = 1.0; - const coord_t skin_overlap = 0; // Skin overlap offset is applied in skin.cpp more overlap might be beneficial for curved bridges, but makes it worse in general. + const coord_t skin_overlap = 0; // Skin overlap offset is applied in skin.cpp more overlap might be beneficial for curved bridges, but makes it worse in general. const bool bridge_settings_enabled = mesh.settings.get("bridge_settings_enabled"); const bool bridge_enable_more_layers = bridge_settings_enabled && mesh.settings.get("bridge_enable_more_layers"); const Ratio support_threshold = bridge_settings_enabled ? mesh.settings.get("bridge_skin_support_threshold") : 0.0_r; @@ -2477,23 +2693,38 @@ void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, } } const bool monotonic = mesh.settings.get("skin_monotonic"); - processSkinPrintFeature(storage, gcode_layer, mesh, mesh_config, extruder_nr, skin_part.skin_fill, *skin_config, pattern, skin_angle, skin_overlap, skin_density, monotonic, added_something, fan_speed); + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + mesh_config, + extruder_nr, + skin_part.skin_fill, + *skin_config, + pattern, + skin_angle, + skin_overlap, + skin_density, + monotonic, + added_something, + fan_speed); } -void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const size_t extruder_nr, - const Polygons& area, - const GCodePathConfig& config, - EFillMethod pattern, - const AngleDegrees skin_angle, - const coord_t skin_overlap, - const Ratio skin_density, - const bool monotonic, - bool& added_something, - double fan_speed) const +void FffGcodeWriter::processSkinPrintFeature( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const size_t extruder_nr, + const Polygons& area, + const GCodePathConfig& config, + EFillMethod pattern, + const AngleDegrees skin_angle, + const coord_t skin_overlap, + const Ratio skin_density, + const bool monotonic, + bool& added_something, + double fan_speed) const { Polygons skin_polygons; Polygons skin_lines; @@ -2516,29 +2747,30 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - area, - config.getLineWidth(), - config.getLineWidth() / skin_density, - skin_overlap, - infill_multiplier, - skin_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_line_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + area, + config.getLineWidth(), + config.getLineWidth() / skin_density, + skin_overlap, + infill_multiplier, + skin_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_line_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN); // add paths @@ -2554,23 +2786,28 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), config.getLineWidth() * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - skin_extruder_nr, - skin_extruder_nr, - z_seam_config, - skin_paths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + config.getLineWidth() * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.skin_config, + mesh_config.skin_config, + mesh_config.skin_config, + mesh_config.skin_config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + skin_extruder_nr, + skin_extruder_nr, + z_seam_config, + skin_paths); added_something |= wall_orderer.addToLayer(); } } @@ -2587,11 +2824,23 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, const AngleRadians monotonic_direction = AngleRadians(skin_angle); constexpr Ratio flow = 1.0_r; - const coord_t max_adjacent_distance = config.getLineWidth() * 1.1; // Lines are considered adjacent if they are 1 line width apart, with 10% extra play. The monotonic order is enforced if they are adjacent. - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + const coord_t max_adjacent_distance + = config.getLineWidth() + * 1.1; // Lines are considered adjacent if they are 1 line width apart, with 10% extra play. The monotonic order is enforced if they are adjacent. + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesMonotonic(area, skin_lines, config, SpaceFillType::Lines, monotonic_direction, max_adjacent_distance, exclude_distance, mesh.settings.get("infill_wipe_dist"), flow, fan_speed); + gcode_layer.addLinesMonotonic( + area, + skin_lines, + config, + SpaceFillType::Lines, + monotonic_direction, + max_adjacent_distance, + exclude_distance, + mesh.settings.get("infill_wipe_dist"), + flow, + fan_speed); } else { @@ -2603,7 +2852,8 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, else { std::optional near_start_location; - const EFillMethod pattern = (gcode_layer.getLayerNr() == 0) ? mesh.settings.get("top_bottom_pattern_0") : mesh.settings.get("top_bottom_pattern"); + const EFillMethod pattern + = (gcode_layer.getLayerNr() == 0) ? mesh.settings.get("top_bottom_pattern_0") : mesh.settings.get("top_bottom_pattern"); if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG) { // update near_start_location to a location which tries to avoid seams in skin near_start_location = getSeamAvoidingLocation(area, skin_angle, gcode_layer.getLastPlannedPositionOrStartingPosition()); @@ -2611,10 +2861,18 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, constexpr bool enable_travel_optimization = false; constexpr float flow = 1.0; - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesByOptimizer(skin_lines, config, SpaceFillType::Lines, enable_travel_optimization, mesh.settings.get("infill_wipe_dist"), flow, near_start_location, fan_speed); + gcode_layer.addLinesByOptimizer( + skin_lines, + config, + SpaceFillType::Lines, + enable_travel_optimization, + mesh.settings.get("infill_wipe_dist"), + flow, + near_start_location, + fan_speed); } else { @@ -2626,7 +2884,12 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, } } -bool FffGcodeWriter::processIroning(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const SliceLayer& layer, const GCodePathConfig& line_config, LayerPlan& gcode_layer) const +bool FffGcodeWriter::processIroning( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const SliceLayer& layer, + const GCodePathConfig& line_config, + LayerPlan& gcode_layer) const { bool added_something = false; const bool ironing_enabled = mesh.settings.get("ironing_enabled"); @@ -2655,8 +2918,8 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - size_t support_infill_extruder_nr = - (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + size_t support_infill_extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; if (support_layer.support_bottom.empty() && support_layer.support_roof.empty() && support_layer.support_infill_parts.empty()) @@ -2691,7 +2954,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const size_t extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + const size_t extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; const ExtruderTrain& infill_extruder = Application::getInstance().current_slice->scene.extruders[extruder_nr]; coord_t default_support_line_distance = infill_extruder.settings.get("support_line_distance"); @@ -2779,7 +3043,22 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr coord_t wipe_dist = 0; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, infill_extruder.settings, extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, wall_toolpaths); + *this, + storage, + gcode_layer, + infill_extruder.settings, + extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + wall_toolpaths); added_something |= wall_orderer.addToLayer(); } @@ -2804,7 +3083,10 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } const unsigned int density_factor = 2 << density_idx; // == pow(2, density_idx + 1) - int support_line_distance_here = (part.custom_line_distance > 0 ? part.custom_line_distance : default_support_line_distance * density_factor); // the highest density infill combines with the next to create a grid with density_factor 1 + int support_line_distance_here + = (part.custom_line_distance > 0 + ? part.custom_line_distance + : default_support_line_distance * density_factor); // the highest density infill combines with the next to create a grid with density_factor 1 const int support_shift = support_line_distance_here / 2; if (part.custom_line_distance == 0 && (density_idx == max_density_idx || support_pattern == EFillMethod::CROSS || support_pattern == EFillMethod::CROSS_3D)) { @@ -2813,33 +3095,42 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const Polygons& area = part.infill_area_per_combine_per_density[density_idx][combine_idx]; constexpr size_t wall_count = 0; // Walls are generated somewhere else, so their layers aren't vertically combined. - const coord_t small_area_width = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width + = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. constexpr bool skip_stitching = false; const bool fill_gaps = density_idx == 0; // Only fill gaps for one of the densities. - Infill infill_comp(support_pattern, - zig_zaggify_infill, - connect_polygons, - area, - support_line_width, - support_line_distance_here, - current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), - infill_multiplier, - support_infill_angle, - gcode_layer.z, - support_shift, - max_resolution, - max_deviation, - wall_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - support_connect_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_toolpaths_here, support_polygons, support_lines, infill_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT, storage.support.cross_fill_provider); + Infill infill_comp( + support_pattern, + zig_zaggify_infill, + connect_polygons, + area, + support_line_width, + support_line_distance_here, + current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), + infill_multiplier, + support_infill_angle, + gcode_layer.z, + support_shift, + max_resolution, + max_deviation, + wall_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + support_connect_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_toolpaths_here, + support_polygons, + support_lines, + infill_extruder.settings, + gcode_layer.getLayerNr(), + SectionType::SUPPORT, + storage.support.cross_fill_provider); } if (need_travel_to_end_of_last_spiral && infill_extruder.settings.get("magic_spiralize")) @@ -2881,7 +3172,15 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const std::optional start_near_location = std::optional(); gcode_layer.addPolygonsByOptimizer( - support_polygons, gcode_layer.configs_storage.support_infill_config[combine_idx], z_seam_config, wall_0_wipe_dist, spiralize, flow_ratio, always_retract, alternate_layer_print_direction, start_near_location); + support_polygons, + gcode_layer.configs_storage.support_infill_config[combine_idx], + z_seam_config, + wall_0_wipe_dist, + spiralize, + flow_ratio, + always_retract, + alternate_layer_print_direction, + start_near_location); added_something = true; } @@ -2893,15 +3192,16 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const std::optional near_start_location = std::optional(); constexpr double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; - gcode_layer.addLinesByOptimizer(support_lines, - gcode_layer.configs_storage.support_infill_config[combine_idx], - (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - near_start_location, - fan_speed, - alternate_layer_print_direction); + gcode_layer.addLinesByOptimizer( + support_lines, + gcode_layer.configs_storage.support_infill_config[combine_idx], + (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + near_start_location, + fan_speed, + alternate_layer_print_direction); added_something = true; } @@ -2914,9 +3214,28 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; constexpr coord_t simplify_curvature = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, simplify_curvature); + const ZSeamConfig z_seam_config( + EZSeamType::SHORTEST, + gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + simplify_curvature); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, infill_extruder.settings, extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, wall_toolpaths_here); + *this, + storage, + gcode_layer, + infill_extruder.settings, + extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + wall_toolpaths_here); added_something |= wall_orderer.addToLayer(); } } @@ -2981,29 +3300,30 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay infill_outline = wall.offset(-support_roof_line_width / 2); } - Infill roof_computation(pattern, - zig_zaggify_infill, - connect_polygons, - infill_outline, - gcode_layer.configs_storage.support_roof_config.getLineWidth(), - support_roof_line_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_outline, + gcode_layer.configs_storage.support_roof_config.getLineWidth(), + support_roof_line_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons roof_polygons; std::vector roof_paths; Polygons roof_lines; @@ -3031,10 +3351,28 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, roof_extruder.settings, roof_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, roof_extruder_nr, roof_extruder_nr, z_seam_config, roof_paths); + *this, + storage, + gcode_layer, + roof_extruder.settings, + roof_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + roof_extruder_nr, + roof_extruder_nr, + z_seam_config, + roof_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(roof_lines, gcode_layer.configs_storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer( + roof_lines, + gcode_layer.configs_storage.support_roof_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); return true; } @@ -3078,30 +3416,32 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L const coord_t max_resolution = bottom_extruder.settings.get("meshfix_maximum_resolution"); const coord_t max_deviation = bottom_extruder.settings.get("meshfix_maximum_deviation"); - const coord_t support_bottom_line_distance = bottom_extruder.settings.get("support_bottom_line_distance"); // note: no need to apply initial line width factor; support bottoms cannot exist on the first layer - Infill bottom_computation(pattern, - zig_zaggify_infill, - connect_polygons, - support_layer.support_bottom, - gcode_layer.configs_storage.support_bottom_config.getLineWidth(), - support_bottom_line_distance, - support_bottom_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + const coord_t support_bottom_line_distance = bottom_extruder.settings.get( + "support_bottom_line_distance"); // note: no need to apply initial line width factor; support bottoms cannot exist on the first layer + Infill bottom_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + support_layer.support_bottom, + gcode_layer.configs_storage.support_bottom_config.getLineWidth(), + support_bottom_line_distance, + support_bottom_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons bottom_polygons; std::vector bottom_paths; Polygons bottom_lines; @@ -3125,10 +3465,28 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, bottom_extruder.settings, bottom_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, bottom_extruder_nr, bottom_extruder_nr, z_seam_config, bottom_paths); + *this, + storage, + gcode_layer, + bottom_extruder.settings, + bottom_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + bottom_extruder_nr, + bottom_extruder_nr, + z_seam_config, + bottom_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(bottom_lines, gcode_layer.configs_storage.support_bottom_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer( + bottom_lines, + gcode_layer.configs_storage.support_bottom_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); return true; } @@ -3235,7 +3593,7 @@ void FffGcodeWriter::finalize() // set extrusion mode back to "normal" gcode.resetExtrusionMode(); - for (size_t e = 0; e < Application::getInstance().current_slice->scene.extruders.size(); e ++) + for (size_t e = 0; e < Application::getInstance().current_slice->scene.extruders.size(); e++) { gcode.writeTemperatureCommand(e, 0, false); } diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index e080f43989..46f8c97e44 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -1,16 +1,13 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include "InsetOrderOptimizer.h" + #include "ExtruderTrain.h" #include "FffGcodeWriter.h" -#include "InsetOrderOptimizer.h" #include "LayerPlan.h" #include "utils/views/convert.h" #include "utils/views/dfs.h" -#include - -#include -#include #include #include @@ -26,6 +23,10 @@ #include #include #include +#include + +#include +#include namespace rg = ranges; namespace rv = ranges::views; @@ -33,22 +34,23 @@ namespace rv = ranges::views; namespace cura { -InsetOrderOptimizer::InsetOrderOptimizer(const FffGcodeWriter& gcode_writer, - const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const Settings& settings, - const int extruder_nr, - const GCodePathConfig& inset_0_non_bridge_config, - const GCodePathConfig& inset_X_non_bridge_config, - const GCodePathConfig& inset_0_bridge_config, - const GCodePathConfig& inset_X_bridge_config, - const bool retract_before_outer_wall, - const coord_t wall_0_wipe_dist, - const coord_t wall_x_wipe_dist, - const size_t wall_0_extruder_nr, - const size_t wall_x_extruder_nr, - const ZSeamConfig& z_seam_config, - const std::vector& paths) +InsetOrderOptimizer::InsetOrderOptimizer( + const FffGcodeWriter& gcode_writer, + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const Settings& settings, + const int extruder_nr, + const GCodePathConfig& inset_0_non_bridge_config, + const GCodePathConfig& inset_X_non_bridge_config, + const GCodePathConfig& inset_0_bridge_config, + const GCodePathConfig& inset_X_bridge_config, + const bool retract_before_outer_wall, + const coord_t wall_0_wipe_dist, + const coord_t wall_x_wipe_dist, + const size_t wall_0_extruder_nr, + const size_t wall_x_extruder_nr, + const ZSeamConfig& z_seam_config, + const std::vector& paths) : gcode_writer(gcode_writer) , storage(storage) , gcode_layer(gcode_layer) @@ -94,7 +96,8 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool group_outer_walls = true; // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. - PathOrderOptimizer order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config, detect_loops, combing_boundary, reverse, order, group_outer_walls); + PathOrderOptimizer + order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config, detect_loops, combing_boundary, reverse, order, group_outer_walls); for (const auto& line : walls_to_be_added) { @@ -157,7 +160,7 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& { const auto poly = std::get<1>(locator); const auto line = std::get<0>(locator); - return LineLoc { + return LineLoc{ .line = line, .poly = poly, .area = line->is_closed ? poly.area() : 0.0, @@ -166,7 +169,13 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& | rg::to_vector; // Sort polygons by increasing area, we are building the graph from the leaves (smallest area) upwards. - rg::sort( locator_view, [](const auto& lhs, const auto& rhs) { return std::abs(lhs) < std::abs(rhs); }, &LineLoc::area); + rg::sort( + locator_view, + [](const auto& lhs, const auto& rhs) + { + return std::abs(lhs) < std::abs(rhs); + }, + &LineLoc::area); // Create a bi-direction directed acyclic graph (Tree). Where polygon B is a child of A if B is inside A. The root of the graph is // the polygon that contains all other polygons. The leaves are polygons that contain no polygons. @@ -209,20 +218,19 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& // - mark all reachable nodes with their depth from the root // - find hole roots, these are the innermost polygons enclosing a hole { - const std::function initialize_nodes = - [graph, root, &hole_roots, &min_node, &min_depth] - (const auto current_node, const auto depth) - { - min_node[current_node] = root; - min_depth[current_node] = depth; + const std::function initialize_nodes + = [graph, root, &hole_roots, &min_node, &min_depth](const auto current_node, const auto depth) + { + min_node[current_node] = root; + min_depth[current_node] = depth; - // find hole roots (defined by a positive area in clipper1), these are leaves of the tree structure - // as odd walls are also leaves we filter them out by adding a non-zero area check - if (current_node != root && graph.count(current_node) == 1 && current_node->line->is_closed && current_node->area > 0) - { - hole_roots.push_back(current_node); - } - }; + // find hole roots (defined by a positive area in clipper1), these are leaves of the tree structure + // as odd walls are also leaves we filter them out by adding a non-zero area check + if (current_node != root && graph.count(current_node) == 1 && current_node->line->is_closed && current_node->area > 0) + { + hole_roots.push_back(current_node); + } + }; actions::dfs_depth_state(root, graph, initialize_nodes); }; @@ -233,16 +241,14 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& { for (auto& hole_root : hole_roots) { - const std::function update_nodes = - [hole_root, &min_depth, &min_node] - (const auto& current_node, auto depth) + const std::function update_nodes = [hole_root, &min_depth, &min_node](const auto& current_node, auto depth) + { + if (depth < min_depth[current_node]) { - if (depth < min_depth[current_node]) - { - min_depth[current_node] = depth; - min_node[current_node] = hole_root; - } - }; + min_depth[current_node] = depth; + min_node[current_node] = hole_root; + } + }; actions::dfs_depth_state(hole_root, graph, update_nodes); } @@ -252,22 +258,21 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& // the depth is closest to root $r$ { const LineLoc* root_ = root; - const std::function set_order_constraints = - [&order, &min_node, &root_, graph, outer_to_inner] - (const auto& current_node, const auto& parent_node) + const std::function set_order_constraints + = [&order, &min_node, &root_, graph, outer_to_inner](const auto& current_node, const auto& parent_node) + { + if (min_node[current_node] == root_ && parent_node != nullptr) { - if (min_node[current_node] == root_ && parent_node != nullptr) - { - if (outer_to_inner) - { - order.insert(std::make_pair(parent_node->line, current_node->line)); - } - else - { - order.insert(std::make_pair(current_node->line, parent_node->line)); - } - } - }; + if (outer_to_inner) + { + order.insert(std::make_pair(parent_node->line, current_node->line)); + } + else + { + order.insert(std::make_pair(current_node->line, parent_node->line)); + } + } + }; actions::dfs_parent_state(root, graph, set_order_constraints); @@ -354,7 +359,7 @@ std::vector InsetOrderOptimizer::getWallsToBeAdded(const bool rev { if (paths.empty()) { - return { }; + return {}; } rg::any_view view; if (reverse) diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index 5c411a4f2d..9d2431bdbe 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -3,22 +3,24 @@ #include "SkeletalTrapezoidation.h" -#include -#include -#include -#include -#include - -#include -#include - #include "BoostInterface.hpp" #include "settings/types/Ratio.h" #include "utils/VoronoiUtils.h" #include "utils/linearAlg2D.h" #include "utils/macros.h" -#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 // A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance). +#include +#include + +#include +#include +#include +#include +#include + +#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX \ + 1000 // A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing + // performance). namespace cura { @@ -39,7 +41,15 @@ SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_ty } } -void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type& vd_edge, edge_t*& prev_edge, Point& start_source_point, Point& end_source_point, const std::vector& points, const std::vector& segments) +void SkeletalTrapezoidation::transferEdge( + Point from, + Point to, + vd_t::edge_type& vd_edge, + edge_t*& prev_edge, + Point& start_source_point, + Point& end_source_point, + const std::vector& points, + const std::vector& segments) { auto he_edge_it = vd_edge_to_he_edge.find(vd_edge.twin()); if (he_edge_it != vd_edge_to_he_edge.end()) @@ -107,7 +117,8 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type& { spdlog::warn("Previous edge doesn't go anywhere."); } - node_t* v0 = (prev_edge) ? prev_edge->to : &makeNode(*vd_edge.vertex0(), from); // TODO: investigate whether boost:voronoi can produce multiple verts and violates consistency + node_t* v0 + = (prev_edge) ? prev_edge->to : &makeNode(*vd_edge.vertex0(), from); // TODO: investigate whether boost:voronoi can produce multiple verts and violates consistency Point p0 = discretized.front(); for (size_t p1_idx = 1; p1_idx < discretized.size(); p1_idx++) { @@ -254,13 +265,14 @@ std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_ } -bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, - Point& start_source_point, - Point& end_source_point, - vd_t::edge_type*& starting_vd_edge, - vd_t::edge_type*& ending_vd_edge, - const std::vector& points, - const std::vector& segments) +bool SkeletalTrapezoidation::computePointCellRange( + vd_t::cell_type& cell, + Point& start_source_point, + Point& end_source_point, + vd_t::edge_type*& starting_vd_edge, + vd_t::edge_type*& ending_vd_edge, + const std::vector& points, + const std::vector& segments) { if (cell.incident_edge()->is_infinite()) { @@ -298,7 +310,9 @@ bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, } else { - assert((VoronoiUtils::p(vd_edge->vertex0()) == source_point || ! vd_edge->is_secondary()) && "point cells must end in the point! They cannot cross the point with an edge, because collinear edges are not allowed in the input."); + assert( + (VoronoiUtils::p(vd_edge->vertex0()) == source_point || ! vd_edge->is_secondary()) + && "point cells must end in the point! They cannot cross the point with an edge, because collinear edges are not allowed in the input."); } } while (vd_edge = vd_edge->next(), vd_edge != cell.incident_edge()); assert(starting_vd_edge && ending_vd_edge); @@ -306,13 +320,14 @@ bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, return true; } -void SkeletalTrapezoidation::computeSegmentCellRange(vd_t::cell_type& cell, - Point& start_source_point, - Point& end_source_point, - vd_t::edge_type*& starting_vd_edge, - vd_t::edge_type*& ending_vd_edge, - const std::vector& points, - const std::vector& segments) +void SkeletalTrapezoidation::computeSegmentCellRange( + vd_t::cell_type& cell, + Point& start_source_point, + Point& end_source_point, + vd_t::edge_type*& starting_vd_edge, + vd_t::edge_type*& ending_vd_edge, + const std::vector& points, + const std::vector& segments) { const Segment& source_segment = VoronoiUtils::getSourceSegment(cell, points, segments); Point from = source_segment.from(); @@ -357,15 +372,16 @@ void SkeletalTrapezoidation::computeSegmentCellRange(vd_t::cell_type& cell, end_source_point = source_segment.from(); } -SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, - const BeadingStrategy& beading_strategy, - AngleRadians transitioning_angle, - coord_t discretization_step_size, - coord_t transition_filter_dist, - coord_t allowed_filter_deviation, - coord_t beading_propagation_transition_dist, - int layer_idx, - SectionType section_type) +SkeletalTrapezoidation::SkeletalTrapezoidation( + const Polygons& polys, + const BeadingStrategy& beading_strategy, + AngleRadians transitioning_angle, + coord_t discretization_step_size, + coord_t transition_filter_dist, + coord_t allowed_filter_deviation, + coord_t beading_propagation_transition_dist, + int layer_idx, + SectionType section_type) : transitioning_angle(transitioning_angle) , discretization_step_size(discretization_step_size) , transition_filter_dist(transition_filter_dist) @@ -432,7 +448,15 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) // Copy start to end edge to graph edge_t* prev_edge = nullptr; - transferEdge(start_source_point, VoronoiUtils::p(starting_vonoroi_edge->vertex1()), *starting_vonoroi_edge, prev_edge, start_source_point, end_source_point, points, segments); + transferEdge( + start_source_point, + VoronoiUtils::p(starting_vonoroi_edge->vertex1()), + *starting_vonoroi_edge, + prev_edge, + start_source_point, + end_source_point, + points, + segments); node_t* starting_node = vd_node_to_he_node[starting_vonoroi_edge->vertex0()]; starting_node->data.distance_to_boundary = 0; @@ -516,44 +540,164 @@ void SkeletalTrapezoidation::generateToolpaths(std::vector& } updateBeadCount(); - scripta::log("st_graph_0", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_0", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); filterNoncentralRegions(); - scripta::log("st_graph_1", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_1", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); generateTransitioningRibs(); - scripta::log("st_graph_2", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_2", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); generateExtraRibs(); - scripta::log("st_graph_3", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_3", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); generateSegments(); - scripta::log("st_graph_4", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_4", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); } void SkeletalTrapezoidation::updateIsCentral() @@ -920,7 +1064,8 @@ void SkeletalTrapezoidation::filterTransitionMids() } } -std::list SkeletalTrapezoidation::dissolveNearbyTransitions(edge_t* edge_to_start, TransitionMiddle& origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up) +std::list + SkeletalTrapezoidation::dissolveNearbyTransitions(edge_t* edge_to_start, TransitionMiddle& origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up) { std::list to_be_dissolved; if (traveled_dist > max_dist) @@ -947,7 +1092,9 @@ std::list SkeletalTrapezoidation::diss const coord_t radius_here = edge->from->data.distance_to_boundary; const bool dissolve_result_is_odd = bool(origin_transition.lower_bead_count % 2) == going_up; const coord_t width_deviation = std::abs(origin_radius - radius_here) * 2; // times by two because the deviation happens at both sides of the significant edge - const coord_t line_width_deviation = dissolve_result_is_odd ? width_deviation : width_deviation / 2; // assume the deviation will be split over either 1 or 2 lines, i.e. assume wall_distribution_count = 1 + const coord_t line_width_deviation = dissolve_result_is_odd + ? width_deviation + : width_deviation / 2; // assume the deviation will be split over either 1 or 2 lines, i.e. assume wall_distribution_count = 1 if (line_width_deviation > allowed_filter_deviation) { should_dissolve = false; @@ -974,7 +1121,8 @@ std::list SkeletalTrapezoidation::diss } if (should_dissolve && ! seen_transition_on_this_edge) { - std::list to_be_dissolved_here = dissolveNearbyTransitions(edge, origin_transition, traveled_dist + ab_size, max_dist, going_up); + std::list to_be_dissolved_here + = dissolveNearbyTransitions(edge, origin_transition, traveled_dist + ab_size, max_dist, going_up); if (to_be_dissolved_here.empty()) { // The region is too long to be dissolved in this direction, so it cannot be dissolved in any direction. to_be_dissolved.clear(); @@ -1100,14 +1248,15 @@ void SkeletalTrapezoidation::generateTransitionEnds(edge_t& edge, coord_t mid_po } } -bool SkeletalTrapezoidation::generateTransitionEnd(edge_t& edge, - coord_t start_pos, - coord_t end_pos, - coord_t transition_half_length, - Ratio start_rest, - Ratio end_rest, - coord_t lower_bead_count, - ptr_vector_t>& edge_transition_ends) +bool SkeletalTrapezoidation::generateTransitionEnd( + edge_t& edge, + coord_t start_pos, + coord_t end_pos, + coord_t transition_half_length, + Ratio start_rest, + Ratio end_rest, + coord_t lower_bead_count, + ptr_vector_t>& edge_transition_ends) { Point a = edge.from->p; Point b = edge.to->p; @@ -1299,7 +1448,11 @@ void SkeletalTrapezoidation::applyTransitions(ptr_vector_tp - edge.from->p, discretization_step_size) || edge.from->data.distance_to_boundary >= edge.to->data.distance_to_boundary) + if (! edge.data.isCentral() || shorterThen(edge.to->p - edge.from->p, discretization_step_size) + || edge.from->data.distance_to_boundary >= edge.to->data.distance_to_boundary) { continue; } @@ -1433,34 +1587,35 @@ void SkeletalTrapezoidation::generateSegments() } } - std::sort(upward_quad_mids.begin(), - upward_quad_mids.end(), - [this](edge_t* a, edge_t* b) - { - if (a->to->data.distance_to_boundary == b->to->data.distance_to_boundary) - { // PathOrdering between two 'upward' edges of the same distance is important when one of the edges is flat and connected to the other - if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary && b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) - { - coord_t max = std::numeric_limits::max(); - coord_t a_dist_from_up = std::min(a->distToGoUp().value_or(max), a->twin->distToGoUp().value_or(max)) - vSize(a->to->p - a->from->p); - coord_t b_dist_from_up = std::min(b->distToGoUp().value_or(max), b->twin->distToGoUp().value_or(max)) - vSize(b->to->p - b->from->p); - return a_dist_from_up < b_dist_from_up; - } - else if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary) - { - return true; // Edge a might be 'above' edge b - } - else if (b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) - { - return false; // Edge b might be 'above' edge a - } - else - { - // PathOrdering is not important - } - } - return a->to->data.distance_to_boundary > b->to->data.distance_to_boundary; - }); + std::sort( + upward_quad_mids.begin(), + upward_quad_mids.end(), + [this](edge_t* a, edge_t* b) + { + if (a->to->data.distance_to_boundary == b->to->data.distance_to_boundary) + { // PathOrdering between two 'upward' edges of the same distance is important when one of the edges is flat and connected to the other + if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary && b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) + { + coord_t max = std::numeric_limits::max(); + coord_t a_dist_from_up = std::min(a->distToGoUp().value_or(max), a->twin->distToGoUp().value_or(max)) - vSize(a->to->p - a->from->p); + coord_t b_dist_from_up = std::min(b->distToGoUp().value_or(max), b->twin->distToGoUp().value_or(max)) - vSize(b->to->p - b->from->p); + return a_dist_from_up < b_dist_from_up; + } + else if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary) + { + return true; // Edge a might be 'above' edge b + } + else if (b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) + { + return false; // Edge b might be 'above' edge a + } + else + { + // PathOrdering is not important + } + } + return a->to->data.distance_to_boundary > b->to->data.distance_to_boundary; + }); ptr_vector_t node_beadings; { // Store beading @@ -1550,8 +1705,10 @@ void SkeletalTrapezoidation::propagateBeadingsUpward(std::vector& upwar { // Only propagate to places where there is place continue; } - assert((upward_edge->from->data.distance_to_boundary != upward_edge->to->data.distance_to_boundary || shorterThen(upward_edge->to->p - upward_edge->from->p, central_filter_dist)) - && "zero difference R edges should always be central"); + assert( + (upward_edge->from->data.distance_to_boundary != upward_edge->to->data.distance_to_boundary + || shorterThen(upward_edge->to->p - upward_edge->from->p, central_filter_dist)) + && "zero difference R edges should always be central"); coord_t length = vSize(upward_edge->to->p - upward_edge->from->p); BeadingPropagation upper_beading = lower_beading; upper_beading.dist_to_bottom_source += length; @@ -1570,7 +1727,8 @@ void SkeletalTrapezoidation::propagateBeadingsDownward(std::vector& upw if (! upward_quad_mid->data.isCentral()) { // for equidistant edge: propagate from known beading to node with unknown beading - if (upward_quad_mid->from->data.distance_to_boundary == upward_quad_mid->to->data.distance_to_boundary && upward_quad_mid->from->data.hasBeading() && ! upward_quad_mid->to->data.hasBeading()) + if (upward_quad_mid->from->data.distance_to_boundary == upward_quad_mid->to->data.distance_to_boundary && upward_quad_mid->from->data.hasBeading() + && ! upward_quad_mid->to->data.hasBeading()) { propagateBeadingsDownward(upward_quad_mid->twin, node_beadings); } @@ -1666,7 +1824,8 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin // f*(l-r) + r = s // f*(l-r) = s - r // f = (s-r) / (l-r) - float new_ratio = static_cast(switching_radius - right.toolpath_locations[next_inset_idx]) / static_cast(left.toolpath_locations[next_inset_idx] - right.toolpath_locations[next_inset_idx]); + float new_ratio = static_cast(switching_radius - right.toolpath_locations[next_inset_idx]) + / static_cast(left.toolpath_locations[next_inset_idx] - right.toolpath_locations[next_inset_idx]); new_ratio = std::min(1.0, new_ratio + 0.1); return interpolate(left, new_ratio, right); } @@ -1814,12 +1973,17 @@ std::shared_ptr SkeletalTrapezo { edge_t* edge_to; coord_t dist; - DistEdge(edge_t* edge_to, coord_t dist) : edge_to(edge_to), dist(dist) + DistEdge(edge_t* edge_to, coord_t dist) + : edge_to(edge_to) + , dist(dist) { } }; - auto compare = [](const DistEdge& l, const DistEdge& r) -> bool { return l.dist > r.dist; }; + auto compare = [](const DistEdge& l, const DistEdge& r) -> bool + { + return l.dist > r.dist; + }; std::priority_queue, decltype(compare)> further_edges(compare); bool first = true; for (edge_t* outgoing = node->incident_edge; outgoing && (first || outgoing != node->incident_edge); outgoing = outgoing->twin->next) @@ -1864,19 +2028,21 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c generated_toolpaths.resize(inset_idx + 1); } assert((generated_toolpaths[inset_idx].empty() || ! generated_toolpaths[inset_idx].back().junctions.empty()) && "empty extrusion lines should never have been generated"); - if (generated_toolpaths[inset_idx].empty() || generated_toolpaths[inset_idx].back().is_odd != is_odd || generated_toolpaths[inset_idx].back().junctions.back().perimeter_index != inset_idx // inset_idx should always be consistent + if (generated_toolpaths[inset_idx].empty() || generated_toolpaths[inset_idx].back().is_odd != is_odd + || generated_toolpaths[inset_idx].back().junctions.back().perimeter_index != inset_idx // inset_idx should always be consistent ) { 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 - && ! from_is_3way // force new path at 3way intersection + 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 && ! 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 - && ! to_is_3way // force new path at 3way intersection + 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 && ! to_is_3way // force new path at 3way intersection ) { if (! is_odd) @@ -1973,7 +2139,10 @@ void SkeletalTrapezoidation::connectJunctions(ptr_vector_t& edge_ assert(std::abs(int(from_junctions.size()) - int(to_junctions.size())) <= 1); // at transitions one end has more beads if (std::abs(int(from_junctions.size()) - int(to_junctions.size())) > 1) { - spdlog::warn("Can't create a transition when connecting two perimeters where the number of beads differs too much! {} vs. {}", from_junctions.size(), to_junctions.size()); + spdlog::warn( + "Can't create a transition when connecting two perimeters where the number of beads differs too much! {} vs. {}", + from_junctions.size(), + to_junctions.size()); } size_t segment_count = std::min(from_junctions.size(), to_junctions.size()); diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index a074dce00d..c3ebf1b2ed 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -3,28 +3,28 @@ #ifdef ARCUS -#include //The socket to communicate to. -#include //To sleep while waiting for the connection. -#include //To map settings to their extruder numbers for limit_to_extruder. - -#include -#include +#include "communication/ArcusCommunication.h" #include "Application.h" //To get and set the current slice command. #include "ExtruderTrain.h" #include "FffProcessor.h" //To start a slice. #include "PrintFeature.h" #include "Slice.h" //To process slices. -#include "communication/ArcusCommunication.h" #include "communication/ArcusCommunicationPrivate.h" //Our PIMPL. #include "communication/Listener.h" //To listen to the Arcus socket. #include "communication/SliceDataStruct.h" //To store sliced layer data. +#include "plugins/slots.h" #include "settings/types/LayerIndex.h" //To point to layers. #include "settings/types/Velocity.h" //To send to layer view how fast stuff is printing. #include "utils/channel.h" #include "utils/polygon.h" -#include "plugins/slots.h" +#include //The socket to communicate to. +#include +#include + +#include //To sleep while waiting for the connection. +#include //To map settings to their extruder numbers for limit_to_extruder. namespace cura { @@ -51,7 +51,8 @@ class ArcusCommunication::PathCompiler std::vector line_widths; //!< Line widths for the line segments stored, the size of this vector is N. std::vector line_thicknesses; //!< Line thicknesses for the line segments stored, the size of this vector is N. std::vector line_velocities; //!< Line feedrates for the line segments stored, the size of this vector is N. - std::vector points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is the dimensionality of the point. + std::vector points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is + //!< the dimensionality of the point. Point last_point; @@ -285,7 +286,9 @@ class ArcusCommunication::PathCompiler } }; -ArcusCommunication::ArcusCommunication() : private_data(new Private), path_compiler(new PathCompiler(*private_data)) +ArcusCommunication::ArcusCommunication() + : private_data(new Private) + , path_compiler(new PathCompiler(*private_data)) { } @@ -339,7 +342,7 @@ void ArcusCommunication::beginGCode() void ArcusCommunication::flushGCode() { - const std::string& message_str = slots::instance().invoke(private_data->gcode_output_stream.str()); + const std::string& message_str = slots::instance().invoke(private_data->gcode_output_stream.str()); if (message_str.size() == 0) { return; @@ -372,7 +375,7 @@ void ArcusCommunication::sendCurrentPosition(const Point& position) void ArcusCommunication::sendGCodePrefix(const std::string& prefix) const { std::shared_ptr message = std::make_shared(); - message->set_data(slots::instance().invoke(prefix)); + message->set_data(slots::instance().invoke(prefix)); private_data->socket->sendMessage(message); } @@ -426,7 +429,12 @@ void ArcusCommunication::sendOptimizedLayerData() data.slice_data.clear(); } -void ArcusCommunication::sendPolygon(const PrintFeatureType& type, const ConstPolygonRef& polygon, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) +void ArcusCommunication::sendPolygon( + const PrintFeatureType& type, + const ConstPolygonRef& polygon, + const coord_t& line_width, + const coord_t& line_thickness, + const Velocity& velocity) { path_compiler->sendPolygon(type, polygon, line_width, line_thickness, velocity); } diff --git a/src/slicer.cpp b/src/slicer.cpp index 582182da9c..3e2089ed6f 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -1,26 +1,26 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include // remove_if -#include -#include - -#include -#include +#include "slicer.h" #include "Application.h" #include "Slice.h" +#include "plugins/slots.h" #include "settings/AdaptiveLayerHeights.h" #include "settings/EnumSettings.h" #include "settings/types/LayerIndex.h" -#include "slicer.h" #include "utils/Simplify.h" #include "utils/SparsePointGridInclusive.h" #include "utils/ThreadPool.h" #include "utils/gettime.h" #include "utils/section_type.h" -#include "plugins/slots.h" +#include +#include + +#include // remove_if +#include +#include namespace cura { @@ -409,7 +409,8 @@ void SlicerLayer::joinPolylines(PolygonRef& polyline_0, PolygonRef& polyline_1, polyline_1.clear(); } -SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) : m_terminus_old_to_cur_map(end_idx) +SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) + : m_terminus_old_to_cur_map(end_idx) { // Initialize map to everything points to itself since nothing has moved yet. for (size_t idx = 0U; idx != end_idx; ++idx) @@ -419,7 +420,12 @@ SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) : m_terminus_cur_to_old_map = m_terminus_old_to_cur_map; } -void SlicerLayer::TerminusTrackingMap::updateMap(size_t num_terms, const Terminus* cur_terms, const Terminus* next_terms, size_t num_removed_terms, const Terminus* removed_cur_terms) +void SlicerLayer::TerminusTrackingMap::updateMap( + size_t num_terms, + const Terminus* cur_terms, + const Terminus* next_terms, + size_t num_removed_terms, + const Terminus* removed_cur_terms) { // save old locations std::vector old_terms(num_terms); @@ -738,10 +744,12 @@ void SlicerLayer::makePolygons(const Mesh* mesh) connectOpenPolylines(open_polylines); - // TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in the middle between the two open polygons. + // TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in + // the middle between the two open polygons. if (mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::NORMAL) - { // don't stitch when using (any) mesh surface mode, i.e. also don't stitch when using mixed mesh surface and closed polygons, because then polylines which are supposed to be open will be closed + { // don't stitch when using (any) mesh surface mode, i.e. also don't stitch when using mixed mesh surface and closed polygons, because then polylines which are supposed to be + // open will be closed stitch(open_polylines); } @@ -769,22 +777,39 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print. const coord_t snap_distance = std::max(mesh->settings.get("minimum_polygon_circumference"), static_cast(1)); - auto it = std::remove_if(polygons.begin(), polygons.end(), [snap_distance](PolygonRef poly) { return poly.shorterThan(snap_distance); }); + auto it = std::remove_if( + polygons.begin(), + polygons.end(), + [snap_distance](PolygonRef poly) + { + return poly.shorterThan(snap_distance); + }); polygons.erase(it, polygons.end()); // Finally optimize all the polygons. Every point removed saves time in the long run. -// polygons = Simplify(mesh->settings).polygon(polygons); - polygons = slots::instance().invoke(polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); + // polygons = Simplify(mesh->settings).polygon(polygons); + polygons = slots::instance().invoke( + polygons, + mesh->settings.get("meshfix_maximum_resolution"), + mesh->settings.get("meshfix_maximum_deviation"), + static_cast(mesh->settings.get("meshfix_maximum_extrusion_area_deviation"))); polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments // Clean up polylines for Surface Mode printing - it = std::remove_if(openPolylines.begin(), openPolylines.end(), [snap_distance](PolygonRef poly) { return poly.shorterThan(snap_distance); }); + it = std::remove_if( + openPolylines.begin(), + openPolylines.end(), + [snap_distance](PolygonRef poly) + { + return poly.shorterThan(snap_distance); + }); openPolylines.erase(it, openPolylines.end()); openPolylines.removeDegenerateVertsPolyline(); } -Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_count, bool use_variable_layer_heights, std::vector* adaptive_layers) : mesh(i_mesh) +Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_count, bool use_variable_layer_heights, std::vector* adaptive_layers) + : mesh(i_mesh) { const SlicingTolerance slicing_tolerance = mesh->settings.get("slicing_tolerance"); const coord_t initial_layer_thickness = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height_0"); @@ -809,137 +834,143 @@ Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_c void Slicer::buildSegments(const Mesh& mesh, const std::vector>& zbbox, const SlicingTolerance& slicing_tolerance, std::vector& layers) { - cura::parallel_for(layers, - [&](auto layer_it) - { - SlicerLayer& layer = *layer_it; - const int32_t& z = layer.z; - layer.segments.reserve(100); - - // loop over all mesh faces - for (unsigned int mesh_idx = 0; mesh_idx < mesh.faces.size(); mesh_idx++) - { - if ((z < zbbox[mesh_idx].first) || (z > zbbox[mesh_idx].second)) - { - continue; - } - - // get all vertices per face - const MeshFace& face = mesh.faces[mesh_idx]; - const MeshVertex& v0 = mesh.vertices[face.vertex_index[0]]; - const MeshVertex& v1 = mesh.vertices[face.vertex_index[1]]; - const MeshVertex& v2 = mesh.vertices[face.vertex_index[2]]; - - // get all vertices represented as 3D point - Point3 p0 = v0.p; - Point3 p1 = v1.p; - Point3 p2 = v2.p; - - // Compensate for points exactly on the slice-boundary, except for 'inclusive', which already handles this correctly. - if (slicing_tolerance != SlicingTolerance::INCLUSIVE) - { - p0.z += static_cast(p0.z == z) * -static_cast(p0.z < 1); - p1.z += static_cast(p1.z == z) * -static_cast(p1.z < 1); - p2.z += static_cast(p2.z == z) * -static_cast(p2.z < 1); - } - - SlicerSegment s; - s.endVertex = nullptr; - int end_edge_idx = -1; - - /* - Now see if the triangle intersects the layer, and if so, where. - - Edge cases are important here: - - If all three vertices of the triangle are exactly on the layer, - don't count the triangle at all, because if the model is - watertight, there will be adjacent triangles on all 3 sides that - are not flat on the layer. - - If two of the vertices are exactly on the layer, only count the - triangle if the last vertex is going up. We can't count both - upwards and downwards triangles here, because if the model is - manifold there will always be an adjacent triangle that is going - the other way and you'd get double edges. You would also get one - layer too many if the total model height is an exact multiple of - the layer thickness. Between going up and going down, we need to - choose the triangles going up, because otherwise the first layer - of where the model starts will be empty and the model will float - in mid-air. We'd much rather let the last layer be empty in that - case. - - If only one of the vertices is exactly on the layer, the - intersection between the triangle and the plane would be a point. - We can't print points and with a manifold model there would be - line segments adjacent to the point on both sides anyway, so we - need to discard this 0-length line segment then. - - Vertices in ccw order if look from outside. - */ - - if (p0.z < z && p1.z > z && p2.z > z) // 1_______2 - { // \ / - s = project2D(p0, p2, p1, z); //------------- z - end_edge_idx = 0; // \ / - } // 0 - - else if (p0.z > z && p1.z <= z && p2.z <= z) // 0 - { // / \ . - s = project2D(p0, p1, p2, z); //------------- z - end_edge_idx = 2; // / \ . - if (p2.z == z) // 1_______2 - { - s.endVertex = &v2; - } - } - - else if (p1.z < z && p0.z > z && p2.z > z) // 0_______2 - { // \ / - s = project2D(p1, p0, p2, z); //------------- z - end_edge_idx = 1; // \ / - } // 1 - - else if (p1.z > z && p0.z <= z && p2.z <= z) // 1 - { // / \ . - s = project2D(p1, p2, p0, z); //------------- z - end_edge_idx = 0; // / \ . - if (p0.z == z) // 0_______2 - { - s.endVertex = &v0; - } - } - - else if (p2.z < z && p1.z > z && p0.z > z) // 0_______1 - { // \ / - s = project2D(p2, p1, p0, z); //------------- z - end_edge_idx = 2; // \ / - } // 2 - - else if (p2.z > z && p1.z <= z && p0.z <= z) // 2 - { // / \ . - s = project2D(p2, p0, p1, z); //------------- z - end_edge_idx = 1; // / \ . - if (p1.z == z) // 0_______1 - { - s.endVertex = &v1; - } - } - else - { - // Not all cases create a segment, because a point of a face could create just a dot, and two touching faces - // on the slice would create two segments - continue; - } - - // store the segments per layer - layer.face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layer.segments.size())); - s.faceIndex = mesh_idx; - s.endOtherFaceIdx = face.connected_face_index[end_edge_idx]; - s.addedToPolygon = false; - layer.segments.push_back(s); - } - }); + cura::parallel_for( + layers, + [&](auto layer_it) + { + SlicerLayer& layer = *layer_it; + const int32_t& z = layer.z; + layer.segments.reserve(100); + + // loop over all mesh faces + for (unsigned int mesh_idx = 0; mesh_idx < mesh.faces.size(); mesh_idx++) + { + if ((z < zbbox[mesh_idx].first) || (z > zbbox[mesh_idx].second)) + { + continue; + } + + // get all vertices per face + const MeshFace& face = mesh.faces[mesh_idx]; + const MeshVertex& v0 = mesh.vertices[face.vertex_index[0]]; + const MeshVertex& v1 = mesh.vertices[face.vertex_index[1]]; + const MeshVertex& v2 = mesh.vertices[face.vertex_index[2]]; + + // get all vertices represented as 3D point + Point3 p0 = v0.p; + Point3 p1 = v1.p; + Point3 p2 = v2.p; + + // Compensate for points exactly on the slice-boundary, except for 'inclusive', which already handles this correctly. + if (slicing_tolerance != SlicingTolerance::INCLUSIVE) + { + p0.z += static_cast(p0.z == z) * -static_cast(p0.z < 1); + p1.z += static_cast(p1.z == z) * -static_cast(p1.z < 1); + p2.z += static_cast(p2.z == z) * -static_cast(p2.z < 1); + } + + SlicerSegment s; + s.endVertex = nullptr; + int end_edge_idx = -1; + + /* + Now see if the triangle intersects the layer, and if so, where. + + Edge cases are important here: + - If all three vertices of the triangle are exactly on the layer, + don't count the triangle at all, because if the model is + watertight, there will be adjacent triangles on all 3 sides that + are not flat on the layer. + - If two of the vertices are exactly on the layer, only count the + triangle if the last vertex is going up. We can't count both + upwards and downwards triangles here, because if the model is + manifold there will always be an adjacent triangle that is going + the other way and you'd get double edges. You would also get one + layer too many if the total model height is an exact multiple of + the layer thickness. Between going up and going down, we need to + choose the triangles going up, because otherwise the first layer + of where the model starts will be empty and the model will float + in mid-air. We'd much rather let the last layer be empty in that + case. + - If only one of the vertices is exactly on the layer, the + intersection between the triangle and the plane would be a point. + We can't print points and with a manifold model there would be + line segments adjacent to the point on both sides anyway, so we + need to discard this 0-length line segment then. + - Vertices in ccw order if look from outside. + */ + + if (p0.z < z && p1.z > z && p2.z > z) // 1_______2 + { // \ / + s = project2D(p0, p2, p1, z); //------------- z + end_edge_idx = 0; // \ / + } // 0 + + else if (p0.z > z && p1.z <= z && p2.z <= z) // 0 + { // / \ . + s = project2D(p0, p1, p2, z); //------------- z + end_edge_idx = 2; // / \ . + if (p2.z == z) // 1_______2 + { + s.endVertex = &v2; + } + } + + else if (p1.z < z && p0.z > z && p2.z > z) // 0_______2 + { // \ / + s = project2D(p1, p0, p2, z); //------------- z + end_edge_idx = 1; // \ / + } // 1 + + else if (p1.z > z && p0.z <= z && p2.z <= z) // 1 + { // / \ . + s = project2D(p1, p2, p0, z); //------------- z + end_edge_idx = 0; // / \ . + if (p0.z == z) // 0_______2 + { + s.endVertex = &v0; + } + } + + else if (p2.z < z && p1.z > z && p0.z > z) // 0_______1 + { // \ / + s = project2D(p2, p1, p0, z); //------------- z + end_edge_idx = 2; // \ / + } // 2 + + else if (p2.z > z && p1.z <= z && p0.z <= z) // 2 + { // / \ . + s = project2D(p2, p0, p1, z); //------------- z + end_edge_idx = 1; // / \ . + if (p1.z == z) // 0_______1 + { + s.endVertex = &v1; + } + } + else + { + // Not all cases create a segment, because a point of a face could create just a dot, and two touching faces + // on the slice would create two segments + continue; + } + + // store the segments per layer + layer.face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layer.segments.size())); + s.faceIndex = mesh_idx; + s.endOtherFaceIdx = face.connected_face_index[end_edge_idx]; + s.addedToPolygon = false; + layer.segments.push_back(s); + } + }); } -std::vector - Slicer::buildLayersWithHeight(size_t slice_layer_count, SlicingTolerance slicing_tolerance, coord_t initial_layer_thickness, coord_t thickness, bool use_variable_layer_heights, const std::vector* adaptive_layers) +std::vector Slicer::buildLayersWithHeight( + size_t slice_layer_count, + SlicingTolerance slicing_tolerance, + coord_t initial_layer_thickness, + coord_t thickness, + bool use_variable_layer_heights, + const std::vector* adaptive_layers) { std::vector layers_res; @@ -976,7 +1007,12 @@ std::vector void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::vector& layers) { - cura::parallel_for(layers, [&mesh](auto layer_it) { layer_it->makePolygons(&mesh); }); + cura::parallel_for( + layers, + [&mesh](auto layer_it) + { + layer_it->makePolygons(&mesh); + }); switch (slicing_tolerance) { @@ -1000,8 +1036,8 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v } size_t layer_apply_initial_xy_offset = 0; - if (layers.size() > 0 && layers[0].polygons.size() == 0 && ! mesh.settings.get("support_mesh") && ! mesh.settings.get("anti_overhang_mesh") && ! mesh.settings.get("cutting_mesh") - && ! mesh.settings.get("infill_mesh")) + if (layers.size() > 0 && layers[0].polygons.size() == 0 && ! mesh.settings.get("support_mesh") && ! mesh.settings.get("anti_overhang_mesh") + && ! mesh.settings.get("cutting_mesh") && ! mesh.settings.get("infill_mesh")) { layer_apply_initial_xy_offset = 1; } @@ -1014,8 +1050,7 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v const auto max_hole_area = std::numbers::pi / 4 * static_cast(hole_offset_max_diameter * hole_offset_max_diameter); - cura::parallel_for - ( + cura::parallel_for( 0, layers.size(), [&layers, layer_apply_initial_xy_offset, xy_offset, xy_offset_0, xy_offset_hole, hole_offset_max_diameter, max_hole_area](size_t layer_nr) @@ -1064,8 +1099,7 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v layers[layer_nr].polygons.add(outline.difference(holes.unionPolygons())); } } - } - ); + }); mesh.expandXY(xy_offset); } diff --git a/src/utils/channel.cpp b/src/utils/channel.cpp index cc04e25834..2dbe8fca23 100644 --- a/src/utils/channel.cpp +++ b/src/utils/channel.cpp @@ -3,41 +3,40 @@ #include "utils/channel.h" +#include "utils/resources/certificate.pem.h" + #include #include -#include "utils/resources/certificate.pem.h" - namespace cura::utils { - namespace details - { - inline constexpr static bool ALLOW_REMOTE_CHANNELS = ENABLE_REMOTE_PLUGINS; - } // namespace details +namespace details +{ +inline constexpr static bool ALLOW_REMOTE_CHANNELS = ENABLE_REMOTE_PLUGINS; +} // namespace details - std::shared_ptr createChannel(const ChannelSetupConfiguration& config) +std::shared_ptr createChannel(const ChannelSetupConfiguration& config) +{ + constexpr auto create_credentials = [](const ChannelSetupConfiguration& config) { - constexpr auto create_credentials = - [](const ChannelSetupConfiguration& config) - { - if (config.host == "localhost" || config.host == "127.0.0.1") - { - spdlog::info("Create local channel on port {}.", config.port); - return grpc::InsecureChannelCredentials(); - } - if (details::ALLOW_REMOTE_CHANNELS) - { - spdlog::info("Create local channel on port {}.", config.port); - auto creds_config = grpc::SslCredentialsOptions(); - creds_config.pem_root_certs = resources::certificate; - return grpc::SslCredentials(creds_config); - } - // Create empty credentials, so it'll make a dummy channel where all operations fail. - // This is consistent with creating a channel with the wrong credentials as it where. - spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", config.host, config.port); - return std::shared_ptr(); - }; - return grpc::CreateChannel(fmt::format("{}:{}", config.host, config.port), create_credentials(config)); - } + if (config.host == "localhost" || config.host == "127.0.0.1") + { + spdlog::info("Create local channel on port {}.", config.port); + return grpc::InsecureChannelCredentials(); + } + if (details::ALLOW_REMOTE_CHANNELS) + { + spdlog::info("Create local channel on port {}.", config.port); + auto creds_config = grpc::SslCredentialsOptions(); + creds_config.pem_root_certs = resources::certificate; + return grpc::SslCredentials(creds_config); + } + // Create empty credentials, so it'll make a dummy channel where all operations fail. + // This is consistent with creating a channel with the wrong credentials as it where. + spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", config.host, config.port); + return std::shared_ptr(); + }; + return grpc::CreateChannel(fmt::format("{}:{}", config.host, config.port), create_credentials(config)); +} } // namespace cura::utils From c9f5733eaa1f204a189cac492c13e6e9b5b648d4 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 20 Jul 2023 11:10:36 +0200 Subject: [PATCH 226/656] Don't pin release of clang-tidy-pr --- .github/workflows/lint-poster.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-poster.yml b/.github/workflows/lint-poster.yml index b20c161a87..87ef49271c 100644 --- a/.github/workflows/lint-poster.yml +++ b/.github/workflows/lint-poster.yml @@ -73,7 +73,7 @@ jobs: unzip linter-result.zip -d linter-result - name: Run clang-tidy-pr-comments action - uses: platisd/clang-tidy-pr-comments@bc0bb7da034a8317d54e7fe1e819159002f4cc40 + uses: platisd/clang-tidy-pr-comments with: github_token: ${{ secrets.GITHUB_TOKEN }} clang_tidy_fixes: linter-result/fixes.yml From 08cdbc04c8ab3346489a2b2f7ecffa08cfd49099 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 20 Jul 2023 12:10:36 +0200 Subject: [PATCH 227/656] Add missing include CURA-10475 --- include/plugins/broadcasts.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h index 256edc7d37..341405fb4c 100644 --- a/include/plugins/broadcasts.h +++ b/include/plugins/broadcasts.h @@ -8,6 +8,8 @@ #include "utils/types/char_range_literal.h" #include "utils/types/generic.h" +#include + namespace cura::plugins::details { From 30c92ecd977efdcb0bbbc345462448c8ccb6c770 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 20 Jul 2023 10:18:01 +0000 Subject: [PATCH 228/656] Applied clang-format. --- include/utils/polygon.h | 307 +++++++++++++++++++++++----------------- src/SkirtBrim.cpp | 106 +++++++------- 2 files changed, 228 insertions(+), 185 deletions(-) diff --git a/include/utils/polygon.h b/include/utils/polygon.h index 797a41e2a0..5d9c4c737c 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -4,33 +4,34 @@ #ifndef UTILS_POLYGON_H #define UTILS_POLYGON_H -#include +#include "../settings/types/Angle.h" //For angles between vertices. +#include "../settings/types/Ratio.h" +#include "IntPoint.h" -#include -#include #include -#include - -#include // std::reverse, fill_n array -#include +#include // std::reverse, fill_n array +#include #include // fabs +#include +#include #include // int64_t.min #include - -#include - -#include "../settings/types/Angle.h" //For angles between vertices. -#include "../settings/types/Ratio.h" -#include "IntPoint.h" +#include +#include +#include #define CHECK_POLY_ACCESS #ifdef CHECK_POLY_ACCESS #define POLY_ASSERT(e) assert(e) #else -#define POLY_ASSERT(e) do {} while(0) +#define POLY_ASSERT(e) \ + do \ + { \ + } while (0) #endif -namespace cura { +namespace cura +{ template bool shorterThan(const T& shape, const coord_t check_length) @@ -75,12 +76,15 @@ class ConstPolygonRef friend class Polygon; friend class PolygonRef; friend class ConstPolygonPointer; + protected: ClipperLib::Path* path; + public: ConstPolygonRef(const ClipperLib::Path& polygon) - : path(const_cast(&polygon)) - {} + : path(const_cast(&polygon)) + { + } ConstPolygonRef() = delete; // you cannot have a reference without an object! @@ -105,7 +109,7 @@ class ConstPolygonRef */ bool empty() const; - const Point& operator[] (unsigned int index) const + const Point& operator[](unsigned int index) const { POLY_ASSERT(index < size()); return (*path)[index]; @@ -201,7 +205,7 @@ class ConstPolygonRef Point min() const { Point ret = Point(POINT_MAX, POINT_MAX); - for(Point p : *path) + for (Point p : *path) { ret.X = std::min(ret.X, p.X); ret.Y = std::min(ret.Y, p.Y); @@ -212,7 +216,7 @@ class ConstPolygonRef Point max() const { Point ret = Point(POINT_MIN, POINT_MIN); - for(Point p : *path) + for (Point p : *path) { ret.X = std::max(ret.X, p.X); ret.Y = std::max(ret.Y, p.Y); @@ -228,8 +232,8 @@ class ConstPolygonRef Point centerOfMass() const { double x = 0, y = 0; - Point p0 = (*path)[path->size()-1]; - for(unsigned int n=0; nsize(); n++) + Point p0 = (*path)[path->size() - 1]; + for (unsigned int n = 0; n < path->size(); n++) { Point p1 = (*path)[n]; double second_factor = (p0.X * p1.Y) - (p1.X * p0.Y); @@ -251,7 +255,7 @@ class ConstPolygonRef { Point ret = p; float bestDist = FLT_MAX; - for(unsigned int n=0; nsize(); n++) + for (unsigned int n = 0; n < path->size(); n++) { float dist = vSize2f(p - (*path)[n]); if (dist < bestDist) @@ -371,7 +375,18 @@ class ConstPolygonRef * \param shortcut_length The desired length ofthe shortcutting line * \param cos_angle The cosine on the angle in L 012 */ - static void smooth_corner_simple(const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle); + static void smooth_corner_simple( + const Point p0, + const Point p1, + const Point p2, + const ListPolyIt p0_it, + const ListPolyIt p1_it, + const ListPolyIt p2_it, + const Point v10, + const Point v12, + const Point v02, + const int64_t shortcut_length, + float cos_angle); /*! * Smooth out a complex corner where the shortcut bypasses more than two line segments @@ -405,7 +420,15 @@ class ConstPolygonRef * \param[in,out] forward_is_too_far Whether trying another step forward is blocked by the shortcut length condition. Updated for the next iteration. * \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); + 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); }; @@ -416,14 +439,17 @@ class PolygonRef : public ConstPolygonRef friend class PolygonPointer; friend class Polygons; friend class PolygonsPart; + public: PolygonRef(ClipperLib::Path& polygon) - : ConstPolygonRef(polygon) - {} + : ConstPolygonRef(polygon) + { + } PolygonRef(const PolygonRef& other) - : ConstPolygonRef(*other.path) - {} + : ConstPolygonRef(*other.path) + { + } PolygonRef() = delete; // you cannot have a reference without an object! @@ -440,7 +466,7 @@ class PolygonRef : public ConstPolygonRef path->reserve(min_size); } - template + template ClipperLib::Path::iterator insert(ClipperLib::Path::const_iterator pos, iterator first, iterator last) { return path->insert(pos, first, last); @@ -449,7 +475,7 @@ class PolygonRef : public ConstPolygonRef PolygonRef& operator=(const ConstPolygonRef& other) = delete; // polygon assignment is expensive and probably not what you want when you use the assignment operator PolygonRef& operator=(ConstPolygonRef& other) = delete; // polygon assignment is expensive and probably not what you want when you use the assignment operator -// { path = other.path; return *this; } + // { path = other.path; return *this; } PolygonRef& operator=(PolygonRef&& other) { @@ -457,13 +483,13 @@ class PolygonRef : public ConstPolygonRef return *this; } - Point& operator[] (unsigned int index) + Point& operator[](unsigned int index) { POLY_ASSERT(index < size()); return (*path)[index]; } - const Point& operator[] (unsigned int index) const + const Point& operator[](unsigned int index) const { POLY_ASSERT(index < size()); return (*path)[index]; @@ -504,7 +530,7 @@ class PolygonRef : public ConstPolygonRef return *path; } - template + template void emplace_back(Args&&... args) { path->emplace_back(args...); @@ -565,7 +591,8 @@ 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 + * \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); @@ -573,10 +600,11 @@ class PolygonRef : public ConstPolygonRef * See simplify(.) */ void simplifyPolyline(const coord_t smallest_line_segment_squared = 100, const coord_t allowed_error_distance_squared = 25); + 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); @@ -598,16 +626,20 @@ class ConstPolygonPointer { protected: const ClipperLib::Path* path; + public: ConstPolygonPointer() - : path(nullptr) - {} + : path(nullptr) + { + } ConstPolygonPointer(const ConstPolygonRef* ref) - : path(ref->path) - {} + : path(ref->path) + { + } ConstPolygonPointer(const ConstPolygonRef& ref) - : path(ref.path) - {} + : path(ref.path) + { + } ConstPolygonRef operator*() const { @@ -635,15 +667,18 @@ class PolygonPointer : public ConstPolygonPointer { public: PolygonPointer() - : ConstPolygonPointer(nullptr) - {} + : ConstPolygonPointer(nullptr) + { + } PolygonPointer(PolygonRef* ref) - : ConstPolygonPointer(ref) - {} + : ConstPolygonPointer(ref) + { + } PolygonPointer(PolygonRef& ref) - : ConstPolygonPointer(ref) - {} + : ConstPolygonPointer(ref) + { + } PolygonRef operator*() { @@ -705,9 +740,10 @@ struct hash return std::hash()(&*ref); } }; -}//namespace std +} // namespace std -namespace cura { +namespace cura +{ class Polygon : public PolygonRef { @@ -715,25 +751,25 @@ class Polygon : public PolygonRef ClipperLib::Path poly; Polygon() - : PolygonRef(poly) + : PolygonRef(poly) { } Polygon(const ConstPolygonRef& other) - : PolygonRef(poly) - , poly(*other.path) + : PolygonRef(poly) + , poly(*other.path) { } Polygon(const Polygon& other) - : PolygonRef(poly) - , poly(*other.path) + : PolygonRef(poly) + , poly(*other.path) { } Polygon(Polygon&& moved) - : PolygonRef(poly) - , poly(std::move(moved.poly)) + : PolygonRef(poly) + , poly(std::move(moved.poly)) { } @@ -742,11 +778,11 @@ class Polygon : public PolygonRef } Polygon& operator=(const ConstPolygonRef& other) = delete; // copying a single polygon is generally not what you want -// { -// path = other.path; -// poly = *other.path; -// return *this; -// } + // { + // path = other.path; + // poly = *other.path; + // return *this; + // } Polygon& operator=(Polygon&& other) //!< move assignment { @@ -763,6 +799,7 @@ class Polygons friend class PolygonRef; friend class ConstPolygonRef; friend class PolygonUtils; + public: ClipperLib::Paths paths; @@ -785,12 +822,12 @@ class Polygons unsigned int pointCount() const; //!< Return the amount of points in all polygons - PolygonRef operator[] (unsigned int index) + PolygonRef operator[](unsigned int index) { POLY_ASSERT(index < size() && index <= static_cast(std::numeric_limits::max())); return paths[index]; } - ConstPolygonRef operator[] (unsigned int index) const + ConstPolygonRef operator[](unsigned int index) const { POLY_ASSERT(index < size() && index <= static_cast(std::numeric_limits::max())); return paths[index]; @@ -884,7 +921,7 @@ class Polygons */ void addLine(const Point from, const Point to) { - paths.emplace_back(ClipperLib::Path{from, to}); + paths.emplace_back(ClipperLib::Path{ from, to }); } void emplace_back(const Polygon& poly) @@ -901,7 +938,7 @@ class Polygons { paths.emplace_back(*poly.path); } - + template void emplace_back(Args... args) { @@ -930,11 +967,23 @@ class Polygons return ConstPolygonRef(paths.back()); } - Polygons() {} + Polygons() + { + } - Polygons(const Polygons& other) { paths = other.paths; } - Polygons(Polygons&& other) { paths = std::move(other.paths); } - Polygons& operator=(const Polygons& other) { paths = other.paths; return *this; } + Polygons(const Polygons& other) + { + paths = other.paths; + } + Polygons(Polygons&& other) + { + paths = std::move(other.paths); + } + Polygons& operator=(const Polygons& other) + { + paths = other.paths; + return *this; + } Polygons& operator=(Polygons&& other) { if (this != &other) @@ -990,9 +1039,9 @@ class Polygons /*! * Intersect polylines with this area Polygons object. - * + * * \note Due to a clipper bug with polylines with nearly collinear segments, the polylines are cut up into separate polylines, and restitched back together at the end. - * + * * \param polylines The (non-closed!) polylines to limit to the area of this Polygons object * \param restitch Whether to stitch the resulting segments into longer polylines, or leave every segment as a single segment * \param max_stitch_distance The maximum distance for two polylines to be stitched together with a segment @@ -1029,7 +1078,7 @@ class Polygons return ret; } - Polygons execute (ClipperLib::PolyFillType pft = ClipperLib::pftEvenOdd) const + Polygons execute(ClipperLib::PolyFillType pft = ClipperLib::pftEvenOdd) const { Polygons ret; ClipperLib::Clipper clipper(clipper_init); @@ -1047,15 +1096,15 @@ class Polygons ClipperLib::EndType end_type; if (inputPolyIsClosed) { - end_type = ClipperLib::etClosedLine; + end_type = ClipperLib::etClosedLine; } else if (joinType == ClipperLib::jtMiter) { - end_type = ClipperLib::etOpenSquare; + end_type = ClipperLib::etOpenSquare; } else { - end_type = ClipperLib::etOpenRound; + end_type = ClipperLib::etOpenRound; } ClipperLib::ClipperOffset clipper(miterLimit, 10.0); clipper.AddPaths(paths, joinType, end_type); @@ -1170,8 +1219,8 @@ class Polygons } } } -public: +public: void scale(const Ratio& ratio) { if (ratio == 1.) @@ -1228,16 +1277,16 @@ class Polygons /*! * Sort the polygons into bins where each bin has polygons which are contained within one of the polygons in the previous bin. - * + * * \warning When polygons are crossing each other the result is undefined. */ std::vector sortByNesting() const; /*! * Utility method for creating the tube (or 'donut') of a shape. - * \param inner_offset Offset relative to the original shape-outline towards the inside of the shape. Sort-of like a negative normal offset, except it's the offset part that's kept, not the shape. - * \param outer_offset Offset relative to the original shape-outline towards the outside of the shape. Comparable to normal offset. - * \return The resulting polygons. + * \param inner_offset Offset relative to the original shape-outline towards the inside of the shape. Sort-of like a negative normal offset, except it's the offset part that's + * kept, not the shape. \param outer_offset Offset relative to the original shape-outline towards the outside of the shape. Comparable to normal offset. \return The resulting + * polygons. */ Polygons tubeShape(const coord_t inner_offset, const coord_t outer_offset) const; @@ -1260,11 +1309,11 @@ class Polygons * \warning Note that this function reorders the polygons! */ PartsView splitIntoPartsView(bool unionAll = false); + private: void splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Polygons& reordered, ClipperLib::PolyNode* node) const; + public: - - /*! * Removes polygons with area smaller than \p min_area_size (note that min_area_size is in mm^2, not in micron^2). * Unless \p remove_holes is true, holes are not removed even if their area is below \p min_area_size. @@ -1333,46 +1382,46 @@ class Polygons ConstPolygonRef poly_keep = (*this)[poly_keep_idx]; bool should_be_removed = false; if (poly_keep.size() > 0) -// for (int hole_poly_idx = 0; hole_poly_idx < to_be_removed.size(); hole_poly_idx++) - for (ConstPolygonRef poly_rem : to_be_removed) - { -// PolygonRef poly_rem = to_be_removed[hole_poly_idx]; - if (poly_rem.size() != poly_keep.size() || poly_rem.size() == 0) continue; - - // find closest point, supposing this point aligns the two shapes in the best way - int closest_point_idx = 0; - int smallestDist2 = -1; - for (unsigned int point_rem_idx = 0; point_rem_idx < poly_rem.size(); point_rem_idx++) + // for (int hole_poly_idx = 0; hole_poly_idx < to_be_removed.size(); hole_poly_idx++) + for (ConstPolygonRef poly_rem : to_be_removed) { - int dist2 = vSize2(poly_rem[point_rem_idx] - poly_keep[0]); - if (dist2 < smallestDist2 || smallestDist2 < 0) + // PolygonRef poly_rem = to_be_removed[hole_poly_idx]; + if (poly_rem.size() != poly_keep.size() || poly_rem.size() == 0) + continue; + + // find closest point, supposing this point aligns the two shapes in the best way + int closest_point_idx = 0; + int smallestDist2 = -1; + for (unsigned int point_rem_idx = 0; point_rem_idx < poly_rem.size(); point_rem_idx++) { - smallestDist2 = dist2; - closest_point_idx = point_rem_idx; + int dist2 = vSize2(poly_rem[point_rem_idx] - poly_keep[0]); + if (dist2 < smallestDist2 || smallestDist2 < 0) + { + smallestDist2 = dist2; + closest_point_idx = point_rem_idx; + } } - } - bool poly_rem_is_poly_keep = true; - // compare the two polygons on all points - if (smallestDist2 > same_distance * same_distance) - continue; - for (unsigned int point_idx = 0; point_idx < poly_rem.size(); point_idx++) - { - int dist2 = vSize2(poly_rem[(closest_point_idx + point_idx) % poly_rem.size()] - poly_keep[point_idx]); - if (dist2 > same_distance * same_distance) + bool poly_rem_is_poly_keep = true; + // compare the two polygons on all points + if (smallestDist2 > same_distance * same_distance) + continue; + for (unsigned int point_idx = 0; point_idx < poly_rem.size(); point_idx++) { - poly_rem_is_poly_keep = false; + int dist2 = vSize2(poly_rem[(closest_point_idx + point_idx) % poly_rem.size()] - poly_keep[point_idx]); + if (dist2 > same_distance * same_distance) + { + poly_rem_is_poly_keep = false; + break; + } + } + if (poly_rem_is_poly_keep) + { + should_be_removed = true; break; } } - if (poly_rem_is_poly_keep) - { - should_be_removed = true; - break; - } - } - if (!should_be_removed) + if (! should_be_removed) result.add(poly_keep); - } return result; } @@ -1412,9 +1461,9 @@ class Polygons Point min() const { Point ret = Point(POINT_MAX, POINT_MAX); - for(const ClipperLib::Path& polygon : paths) + for (const ClipperLib::Path& polygon : paths) { - for(Point p : polygon) + for (Point p : polygon) { ret.X = std::min(ret.X, p.X); ret.Y = std::min(ret.Y, p.Y); @@ -1426,9 +1475,9 @@ class Polygons Point max() const { Point ret = Point(POINT_MIN, POINT_MIN); - for(const ClipperLib::Path& polygon : paths) + for (const ClipperLib::Path& polygon : paths) { - for(Point p : polygon) + for (Point p : polygon) { ret.X = std::max(ret.X, p.X); ret.Y = std::max(ret.Y, p.Y); @@ -1439,9 +1488,9 @@ class Polygons void applyMatrix(const PointMatrix& matrix) { - for(unsigned int i=0; i> which is similar to a vector of PolygonParts, except the base of the container is indices to polygons into the original Polygons, instead of the polygons themselves + * Extension of vector> which is similar to a vector of PolygonParts, except the base of the container is indices to polygons into the original Polygons, + * instead of the polygons themselves */ class PartsView : public std::vector> { public: Polygons& polygons; - PartsView(Polygons& polygons) : polygons(polygons) { } + PartsView(Polygons& polygons) + : polygons(polygons) + { + } /*! * Get the index of the PolygonsPart of which the polygon with index \p poly_idx is part. * @@ -1521,6 +1574,6 @@ class PartsView : public std::vector> PolygonsPart assemblePart(unsigned int part_idx) const; }; -}//namespace cura +} // namespace cura -#endif//UTILS_POLYGON_H +#endif // UTILS_POLYGON_H diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 5e1b31bee8..0aa1c2243b 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -1,31 +1,31 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include - #include "SkirtBrim.h" #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" +#include "settings/EnumSettings.h" #include "settings/types/Ratio.h" #include "sliceDataStorage.h" #include "support.h" -#include "utils/Simplify.h" //Simplifying the brim/skirt at every inset. -#include "settings/EnumSettings.h" #include "utils/PolylineStitcher.h" +#include "utils/Simplify.h" //Simplifying the brim/skirt at every inset. + +#include namespace cura { -SkirtBrim::SkirtBrim(SliceDataStorage& storage) : - storage(storage), - adhesion_type(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("adhesion_type")), - has_ooze_shield(storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0), - has_draft_shield(storage.draft_protection_shield.size() > 0), - extruders(Application::getInstance().current_slice->scene.extruders), - extruder_count(extruders.size()), - extruder_is_used(storage.getExtrudersUsed()) +SkirtBrim::SkirtBrim(SliceDataStorage& storage) + : storage(storage) + , adhesion_type(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("adhesion_type")) + , has_ooze_shield(storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0) + , has_draft_shield(storage.draft_protection_shield.size() > 0) + , extruders(Application::getInstance().current_slice->scene.extruders) + , extruder_count(extruders.size()) + , extruder_is_used(storage.getExtrudersUsed()) { first_used_extruder_nr = 0; for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) @@ -50,7 +50,7 @@ SkirtBrim::SkirtBrim(SliceDataStorage& storage) : gap.resize(extruder_count); for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { - if (!extruder_is_used[extruder_nr]) + if (! extruder_is_used[extruder_nr]) { continue; } @@ -86,7 +86,7 @@ std::vector SkirtBrim::generateBrimOffsetPlan(std::vector= 0 && extruder_nr != skirt_brim_extruder_nr) || starting_outlines[extruder_nr].empty()) + if (! extruder_is_used[extruder_nr] || (skirt_brim_extruder_nr >= 0 && extruder_nr != skirt_brim_extruder_nr) || starting_outlines[extruder_nr].empty()) { continue; // only include offsets for brim extruder } @@ -228,25 +228,21 @@ std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_bri } total_length[offset.extruder_nr] += added_length; - if - ( - offset.is_last && - total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet - total_length[offset.extruder_nr] > 0u // No lines got added; we have no extrusion lines to build on - ) + if (offset.is_last && total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] + && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet + total_length[offset.extruder_nr] > 0u // No lines got added; we have no extrusion lines to build on + ) { offset.is_last = false; constexpr bool is_last = true; - all_brim_offsets.emplace_back - ( - offset.inset_idx, - external_polys_only[offset.extruder_nr], - line_widths[offset.extruder_nr], - offset.total_offset + line_widths[offset.extruder_nr], - offset.inset_idx + 1, - offset.extruder_nr, - is_last - ); + all_brim_offsets.emplace_back( + offset.inset_idx, + external_polys_only[offset.extruder_nr], + line_widths[offset.extruder_nr], + offset.total_offset + line_widths[offset.extruder_nr], + offset.inset_idx + 1, + offset.extruder_nr, + is_last); std::sort(all_brim_offsets.begin() + offset_idx + 1, all_brim_offsets.end(), OffsetSorter); // reorder remaining offsets } } @@ -268,7 +264,7 @@ Polygons SkirtBrim::getInternalHoleExclusionArea(const Polygons& outline, const { Polygon hole_poly = part[hole_idx]; hole_poly.reverse(); - Polygons disallowed_region = hole_poly.offset(10u).difference(hole_poly.offset( - line_widths[extruder_nr] / 2 - hole_brim_distance)); + Polygons disallowed_region = hole_poly.offset(10u).difference(hole_poly.offset(-line_widths[extruder_nr] / 2 - hole_brim_distance)); ret = ret.unionPolygons(disallowed_region); } } @@ -316,12 +312,10 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, auto offset_dist = line_widths[offset.extruder_nr]; Polygons local_brim; - auto closed_polygons_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].closed_polygons - .offsetPolyLine(offset_dist, ClipperLib::jtRound, true); + auto closed_polygons_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].closed_polygons.offsetPolyLine(offset_dist, ClipperLib::jtRound, true); local_brim.add(closed_polygons_brim); - auto open_polylines_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].open_polylines - .offsetPolyLine(offset_dist, ClipperLib::jtRound); + auto open_polylines_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].open_polylines.offsetPolyLine(offset_dist, ClipperLib::jtRound); local_brim.add(open_polylines_brim); local_brim.unionPolygons(); @@ -341,7 +335,7 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, PolylineStitcher::stitch(brim_lines, result.open_polylines, result.closed_polygons, max_stitch_distance); // clean up too small lines - for (size_t line_idx = 0; line_idx < result.open_polylines.size(); ) + for (size_t line_idx = 0; line_idx < result.open_polylines.size();) { PolygonRef line = result.open_polylines[line_idx]; if (line.shorterThan(min_brim_line_length)) @@ -374,13 +368,14 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) Polygons first_layer_outline; Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; int reference_extruder_nr = skirt_brim_extruder_nr; - assert( ! (reference_extruder_nr == -1 && extruder_nr == -1) && "We should only request the outlines of all layers when the brim is being generated for only one material"); + assert(! (reference_extruder_nr == -1 && extruder_nr == -1) && "We should only request the outlines of all layers when the brim is being generated for only one material"); if (reference_extruder_nr == -1) { reference_extruder_nr = extruder_nr; } const int primary_line_count = line_count[reference_extruder_nr]; - const bool external_only = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. + const bool external_only + = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. const LayerIndex layer_nr = 0; if (adhesion_type == EPlatformAdhesion::SKIRT) { @@ -392,11 +387,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline = Polygons(); for (int i_layer = layer_nr; i_layer <= skirt_height; ++i_layer) { - first_layer_outline = - first_layer_outline.unionPolygons - ( - storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder_nr) - ); + first_layer_outline = first_layer_outline.unionPolygons(storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder_nr)); } if (skirt_around_prime_tower_brim) @@ -441,8 +432,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline = first_layer_outline.removeEmptyHoles(); } if (storage.support.generated && primary_line_count > 0 && ! storage.support.supportLayers.empty() - && (extruder_nr == -1 || extruder_nr == global_settings.get("support_infill_extruder_nr")) - ) + && (extruder_nr == -1 || extruder_nr == global_settings.get("support_infill_extruder_nr"))) { // remove model-brim from support SupportLayer& support_layer = storage.support.supportLayers[0]; const ExtruderTrain& support_infill_extruder = global_settings.get("support_infill_extruder_nr"); @@ -456,8 +446,9 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) // |+-+| |+--+| // +---+ +----+ const coord_t primary_extruder_skirt_brim_line_width = line_widths[reference_extruder_nr]; - Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), - ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides + Polygons model_brim_covered_area = first_layer_outline.offset( + primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), + ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides if (external_only) { // don't remove support within empty holes where no brim is generated. model_brim_covered_area.add(first_layer_empty_holes); @@ -475,12 +466,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline.add(support_layer.support_bottom); first_layer_outline.add(support_layer.support_roof); } - if - ( - storage.primeTower.enabled && - global_settings.get("prime_tower_brim_enable") && - (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr) - ) + if (storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable") && (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr)) { first_layer_outline.add(storage.primeTower.outer_poly); // don't remove parts of the prime tower, but make a brim for it } @@ -519,7 +505,8 @@ void SkirtBrim::generateShieldBrim(Polygons& brim_covered_area, std::vector expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ - const coord_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides + const coord_t primary_skirt_brim_width + = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides Polygons shield_brim; if (has_ooze_shield) @@ -528,7 +515,8 @@ void SkirtBrim::generateShieldBrim(Polygons& brim_covered_area, std::vector 0) { shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width); - storage.skirt_brim[extruder_nr].back().closed_polygons.add(shield_brim); // throw all polygons for the shileds onto one heap; because the brim lines are generated from both sides the order will not be important + storage.skirt_brim[extruder_nr].back().closed_polygons.add( + shield_brim); // throw all polygons for the shileds onto one heap; because the brim lines are generated from both sides the order will not be important } } @@ -594,7 +583,7 @@ void SkirtBrim::generateSecondarySkirtBrim(Polygons& covered_area, std::vector

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"); + 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"); size_t line_count = support_infill_extruder.settings.get("support_brim_line_count"); const coord_t minimal_length = support_infill_extruder.settings.get("skirt_brim_minimal_length"); if (! storage.support.generated || line_count <= 0 || storage.support.supportLayers.empty()) From 355bccbf46c7b3c4cb0aa629770b408ba6529fbd Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 20 Jul 2023 12:54:08 +0200 Subject: [PATCH 229/656] Add boost and (a)grpc includes to prio 2 CURA-10475 --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 4196ee4bd5..5d90caa166 100644 --- a/.clang-format +++ b/.clang-format @@ -59,7 +59,7 @@ ForEachMacros: IncludeBlocks: Regroup IncludeCategories: - Priority: 2 - Regex: ^<(scripta|spdlog|range|fmt|Arcus)/ + Regex: ^<(scripta|spdlog|range|fmt|Arcus|agrpc|grpc|boost)/ - Priority: 3 Regex: ^(<|"(gtest|gmock|isl|json)/) - Priority: 1 From b8a6e9b242dff100c9c109c10e99fcddffbd1b8d Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 20 Jul 2023 13:26:19 +0000 Subject: [PATCH 230/656] Applied clang-format. --- include/utils/actions/smooth.h | 34 ++-- src/SkirtBrim.cpp | 107 ++++++------ src/pathPlanning/Comb.cpp | 305 +++++++++++++++++++++------------ 3 files changed, 271 insertions(+), 175 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 25a7d712d3..21a0f44698 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -4,9 +4,10 @@ #ifndef UTILS_VIEWS_SMOOTH_H #define UTILS_VIEWS_SMOOTH_H -#include -#include -#include +#include "utils/types/arachne.h" +#include "utils/types/generic.h" +#include "utils/types/geometry.h" +#include "utils/types/get.h" #include #include @@ -18,10 +19,9 @@ #include #include -#include "utils/types/arachne.h" -#include "utils/types/generic.h" -#include "utils/types/geometry.h" -#include "utils/types/get.h" +#include +#include +#include namespace cura::actions { @@ -34,8 +34,9 @@ struct smooth_fn } template - requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> && (utils::point2d> || utils::junctions) - constexpr auto operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const + requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> &&( + utils::point2d> || utils::junctions)constexpr auto + operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const { const auto size = ranges::distance(rng) - 1; if (size < 4) @@ -124,15 +125,24 @@ struct smooth_fn } template - requires utils::point2d || utils::junction constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept + requires utils::point2d || utils::junction + constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept { return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); } template requires utils::point2d || utils::junction - constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, - const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept + constexpr auto isWithinAllowedDeviations( + Point* A, + Point* B, + Point* C, + Point* D, + const utils::floating_point auto fluid_angle, + const utils::integral auto max_resolution, + const utils::floating_point auto AB_magnitude, + const utils::floating_point auto BC_magnitude, + const utils::floating_point auto CD_magnitude) const noexcept { if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this { diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 607a3f9e27..4469a6a577 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -1,31 +1,31 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include - #include "SkirtBrim.h" #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" +#include "settings/EnumSettings.h" #include "settings/types/Ratio.h" #include "sliceDataStorage.h" #include "support.h" -#include "utils/Simplify.h" //Simplifying the brim/skirt at every inset. -#include "settings/EnumSettings.h" #include "utils/PolylineStitcher.h" +#include "utils/Simplify.h" //Simplifying the brim/skirt at every inset. + +#include namespace cura { -SkirtBrim::SkirtBrim(SliceDataStorage& storage) : - storage(storage), - adhesion_type(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("adhesion_type")), - has_ooze_shield(storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0), - has_draft_shield(storage.draft_protection_shield.size() > 0), - extruders(Application::getInstance().current_slice->scene.extruders), - extruder_count(extruders.size()), - extruder_is_used(storage.getExtrudersUsed()) +SkirtBrim::SkirtBrim(SliceDataStorage& storage) + : storage(storage) + , adhesion_type(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("adhesion_type")) + , has_ooze_shield(storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0) + , has_draft_shield(storage.draft_protection_shield.size() > 0) + , extruders(Application::getInstance().current_slice->scene.extruders) + , extruder_count(extruders.size()) + , extruder_is_used(storage.getExtrudersUsed()) { first_used_extruder_nr = 0; for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) @@ -42,7 +42,7 @@ SkirtBrim::SkirtBrim(SliceDataStorage& storage) : // NOTE: the line count will only be satisfied for the first extruder used. skirt_brim_extruder_nr = first_used_extruder_nr; } - + line_widths.resize(extruder_count); skirt_brim_minimal_length.resize(extruder_count); external_polys_only.resize(extruder_count); @@ -50,7 +50,7 @@ SkirtBrim::SkirtBrim(SliceDataStorage& storage) : gap.resize(extruder_count); for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { - if (!extruder_is_used[extruder_nr]) + if (! extruder_is_used[extruder_nr]) { continue; } @@ -86,7 +86,7 @@ std::vector SkirtBrim::generateBrimOffsetPlan(std::vector= 0 && extruder_nr != skirt_brim_extruder_nr) || starting_outlines[extruder_nr].empty()) + if (! extruder_is_used[extruder_nr] || (skirt_brim_extruder_nr >= 0 && extruder_nr != skirt_brim_extruder_nr) || starting_outlines[extruder_nr].empty()) { continue; // only include offsets for brim extruder } @@ -139,7 +139,7 @@ void SkirtBrim::generate() std::vector starting_outlines(extruder_count); std::vector all_brim_offsets = generateBrimOffsetPlan(starting_outlines); std::vector prime_brim_offsets_for_skirt = generatePrimeTowerBrimForSkirtAdhesionOffsetPlan(); - + constexpr LayerIndex layer_nr = 0; constexpr bool include_support = true; Polygons covered_area = storage.getLayerOutlines(layer_nr, include_support, /*include_prime_tower*/ true, /*external_polys_only*/ false); @@ -192,7 +192,7 @@ void SkirtBrim::generate() // Secondary brim of all other materials which don;t meet minimum length constriant yet generateSecondarySkirtBrim(covered_area, allowed_areas_per_extruder, total_length); - + // simplify paths to prevent buffer unnerruns in firmware const Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const coord_t maximum_resolution = global_settings.get("meshfix_maximum_resolution"); @@ -226,26 +226,22 @@ std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_bri break; } total_length[offset.extruder_nr] += added_length; - - if - ( - offset.is_last && - total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet + + if (offset.is_last && total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] + && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet total_length[offset.extruder_nr] > 0u // No lines got added; we have no extrusion lines to build on ) { offset.is_last = false; constexpr bool is_last = true; - all_brim_offsets.emplace_back - ( + all_brim_offsets.emplace_back( offset.inset_idx, external_polys_only[offset.extruder_nr], line_widths[offset.extruder_nr], offset.total_offset + line_widths[offset.extruder_nr], offset.inset_idx + 1, offset.extruder_nr, - is_last - ); + is_last); std::sort(all_brim_offsets.begin() + offset_idx + 1, all_brim_offsets.end(), OffsetSorter); // reorder remaining offsets } } @@ -267,7 +263,7 @@ Polygons SkirtBrim::getInternalHoleExclusionArea(const Polygons& outline, const { Polygon hole_poly = part[hole_idx]; hole_poly.reverse(); - Polygons disallowed_region = hole_poly.offset(10u).difference(hole_poly.offset( - line_widths[extruder_nr] / 2 - hole_brim_distance)); + Polygons disallowed_region = hole_poly.offset(10u).difference(hole_poly.offset(-line_widths[extruder_nr] / 2 - hole_brim_distance)); ret = ret.unionPolygons(disallowed_region); } } @@ -315,12 +311,10 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, auto offset_dist = line_widths[offset.extruder_nr]; Polygons local_brim; - auto closed_polygons_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].closed_polygons - .offset(offset_dist, ClipperLib::jtRound); + auto closed_polygons_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].closed_polygons.offset(offset_dist, ClipperLib::jtRound); local_brim.add(closed_polygons_brim); - auto open_polylines_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].open_polylines - .offsetPolyLine(offset_dist, ClipperLib::jtRound); + auto open_polylines_brim = storage.skirt_brim[offset.extruder_nr][reference_idx].open_polylines.offsetPolyLine(offset_dist, ClipperLib::jtRound); local_brim.add(open_polylines_brim); local_brim.unionPolygons(); @@ -338,9 +332,9 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, const coord_t max_stitch_distance = line_widths[offset.extruder_nr]; PolylineStitcher::stitch(brim_lines, result.open_polylines, result.closed_polygons, max_stitch_distance); - + // clean up too small lines - for (size_t line_idx = 0; line_idx < result.open_polylines.size(); ) + for (size_t line_idx = 0; line_idx < result.open_polylines.size();) { PolygonRef line = result.open_polylines[line_idx]; if (line.shorterThan(min_brim_line_length)) @@ -353,7 +347,7 @@ coord_t SkirtBrim::generateOffset(const Offset& offset, Polygons& covered_area, } } } - + { // update allowed_areas_per_extruder for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { @@ -373,13 +367,14 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) Polygons first_layer_outline; Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; int reference_extruder_nr = skirt_brim_extruder_nr; - assert( ! (reference_extruder_nr == -1 && extruder_nr == -1) && "We should only request the outlines of all layers when the brim is being generated for only one material"); + assert(! (reference_extruder_nr == -1 && extruder_nr == -1) && "We should only request the outlines of all layers when the brim is being generated for only one material"); if (reference_extruder_nr == -1) { reference_extruder_nr = extruder_nr; } const int primary_line_count = line_count[reference_extruder_nr]; - const bool external_only = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. + const bool external_only + = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. const LayerIndex layer_nr = 0; if (adhesion_type == EPlatformAdhesion::SKIRT) { @@ -402,11 +397,8 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) { for (const auto& extruder : Application::getInstance().current_slice->scene.extruders) { - first_layer_outline = - first_layer_outline.unionPolygons - ( - storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder.extruder_nr) - ); + first_layer_outline + = first_layer_outline.unionPolygons(storage.getLayerOutlines(i_layer, include_support, include_prime_tower, external_only, extruder.extruder_nr)); } } @@ -452,8 +444,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline = first_layer_outline.removeEmptyHoles(); } if (storage.support.generated && primary_line_count > 0 && ! storage.support.supportLayers.empty() - && (extruder_nr == -1 || extruder_nr == global_settings.get("support_infill_extruder_nr")) - ) + && (extruder_nr == -1 || extruder_nr == global_settings.get("support_infill_extruder_nr"))) { // remove model-brim from support SupportLayer& support_layer = storage.support.supportLayers[0]; const ExtruderTrain& support_infill_extruder = global_settings.get("support_infill_extruder_nr"); @@ -467,8 +458,9 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) // |+-+| |+--+| // +---+ +----+ const coord_t primary_extruder_skirt_brim_line_width = line_widths[reference_extruder_nr]; - Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), - ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides + Polygons model_brim_covered_area = first_layer_outline.offset( + primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), + ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides if (external_only) { // don't remove support within empty holes where no brim is generated. model_brim_covered_area.add(first_layer_empty_holes); @@ -486,12 +478,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline.add(support_layer.support_bottom); first_layer_outline.add(support_layer.support_roof); } - if - ( - storage.primeTower.enabled && - global_settings.get("prime_tower_brim_enable") && - (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr) - ) + if (storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable") && (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr)) { first_layer_outline.add(storage.primeTower.outer_poly); // don't remove parts of the prime tower, but make a brim for it } @@ -530,7 +517,8 @@ void SkirtBrim::generateShieldBrim(Polygons& brim_covered_area, std::vector expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ - const coord_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides + const coord_t primary_skirt_brim_width + = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides Polygons shield_brim; if (has_ooze_shield) @@ -539,7 +527,8 @@ void SkirtBrim::generateShieldBrim(Polygons& brim_covered_area, std::vector 0) { shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width); - storage.skirt_brim[extruder_nr].back().closed_polygons.add(shield_brim); // throw all polygons for the shileds onto one heap; because the brim lines are generated from both sides the order will not be important + storage.skirt_brim[extruder_nr].back().closed_polygons.add( + shield_brim); // throw all polygons for the shileds onto one heap; because the brim lines are generated from both sides the order will not be important } } @@ -600,19 +590,19 @@ void SkirtBrim::generateSecondarySkirtBrim(Polygons& covered_area, std::vector

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"); + 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"); size_t line_count = support_infill_extruder.settings.get("support_brim_line_count"); const coord_t minimal_length = support_infill_extruder.settings.get("skirt_brim_minimal_length"); if (! storage.support.generated || line_count <= 0 || storage.support.supportLayers.empty()) diff --git a/src/pathPlanning/Comb.cpp b/src/pathPlanning/Comb.cpp index bca5e87d20..eda5d5bc1d 100644 --- a/src/pathPlanning/Comb.cpp +++ b/src/pathPlanning/Comb.cpp @@ -1,30 +1,30 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "pathPlanning/Comb.h" -#include -#include // function -#include - -#include "pathPlanning/CombPaths.h" -#include "pathPlanning/LinePolygonsCrossings.h" #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" -#include "utils/linearAlg2D.h" -#include "utils/PolygonsPointIndex.h" +#include "pathPlanning/CombPaths.h" +#include "pathPlanning/LinePolygonsCrossings.h" #include "sliceDataStorage.h" +#include "utils/PolygonsPointIndex.h" #include "utils/SVG.h" +#include "utils/linearAlg2D.h" -namespace cura { +#include +#include // function +#include + +namespace cura +{ LocToLineGrid& Comb::getOutsideLocToLine(const ExtruderTrain& train) { if (outside_loc_to_line[train.extruder_nr] == nullptr) { - outside_loc_to_line[train.extruder_nr] = - PolygonUtils::createLocToLineGrid(getBoundaryOutside(train), offset_from_inside_to_outside * 3 / 2); + outside_loc_to_line[train.extruder_nr] = PolygonUtils::createLocToLineGrid(getBoundaryOutside(train), offset_from_inside_to_outside * 3 / 2); } return *outside_loc_to_line[train.extruder_nr]; } @@ -34,8 +34,7 @@ Polygons& Comb::getBoundaryOutside(const ExtruderTrain& train) if (boundary_outside[train.extruder_nr].empty()) { bool travel_avoid_supports = train.settings.get("travel_avoid_supports"); - boundary_outside[train.extruder_nr] = - storage.getLayerOutlines(layer_nr, travel_avoid_supports, travel_avoid_supports).offset(travel_avoid_distance); + boundary_outside[train.extruder_nr] = storage.getLayerOutlines(layer_nr, travel_avoid_supports, travel_avoid_supports).offset(travel_avoid_distance); } return boundary_outside[train.extruder_nr]; } @@ -45,8 +44,7 @@ Polygons& Comb::getModelBoundary(const ExtruderTrain& train) if (model_boundary[train.extruder_nr].empty()) { bool travel_avoid_supports = train.settings.get("travel_avoid_supports"); - model_boundary[train.extruder_nr] = - storage.getLayerOutlines(layer_nr, travel_avoid_supports, travel_avoid_supports); + model_boundary[train.extruder_nr] = storage.getLayerOutlines(layer_nr, travel_avoid_supports, travel_avoid_supports); } return boundary_outside[train.extruder_nr]; } @@ -55,32 +53,39 @@ LocToLineGrid& Comb::getModelBoundaryLocToLine(const ExtruderTrain& train) { if (model_boundary_loc_to_line[train.extruder_nr] == nullptr) { - model_boundary_loc_to_line[train.extruder_nr] = - PolygonUtils::createLocToLineGrid(getModelBoundary(train), offset_from_inside_to_outside * 3 / 2); + model_boundary_loc_to_line[train.extruder_nr] = PolygonUtils::createLocToLineGrid(getModelBoundary(train), offset_from_inside_to_outside * 3 / 2); } return *model_boundary_loc_to_line[train.extruder_nr]; } -Comb::Comb(const SliceDataStorage& storage, const LayerIndex layer_nr, const Polygons& comb_boundary_inside_minimum, const Polygons& comb_boundary_inside_optimal, coord_t comb_boundary_offset, coord_t travel_avoid_distance, coord_t move_inside_distance) -: storage(storage) -, layer_nr(layer_nr) -, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls -, max_moveInside_distance2(offset_from_outlines * offset_from_outlines) -, offset_from_inside_to_outside(offset_from_outlines + travel_avoid_distance) -, max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation -, boundary_inside_minimum( comb_boundary_inside_minimum ) // copy the boundary, because the partsView_inside will reorder the polygons -, boundary_inside_optimal( comb_boundary_inside_optimal ) // copy the boundary, because the partsView_inside will reorder the polygons -, partsView_inside_minimum( boundary_inside_minimum.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !! -, partsView_inside_optimal( boundary_inside_optimal.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !! -, inside_loc_to_line_minimum(PolygonUtils::createLocToLineGrid(boundary_inside_minimum, comb_boundary_offset)) -, inside_loc_to_line_optimal(PolygonUtils::createLocToLineGrid(boundary_inside_optimal, comb_boundary_offset)) -, move_inside_distance(move_inside_distance) -, travel_avoid_distance(travel_avoid_distance) +Comb::Comb( + const SliceDataStorage& storage, + const LayerIndex layer_nr, + const Polygons& comb_boundary_inside_minimum, + const Polygons& comb_boundary_inside_optimal, + coord_t comb_boundary_offset, + coord_t travel_avoid_distance, + coord_t move_inside_distance) + : storage(storage) + , layer_nr(layer_nr) + , offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls + , max_moveInside_distance2(offset_from_outlines * offset_from_outlines) + , offset_from_inside_to_outside(offset_from_outlines + travel_avoid_distance) + , max_crossing_dist2( + offset_from_inside_to_outside * offset_from_inside_to_outside + * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation + , boundary_inside_minimum(comb_boundary_inside_minimum) // copy the boundary, because the partsView_inside will reorder the polygons + , boundary_inside_optimal(comb_boundary_inside_optimal) // copy the boundary, because the partsView_inside will reorder the polygons + , partsView_inside_minimum(boundary_inside_minimum.splitIntoPartsView()) // WARNING !! changes the order of boundary_inside !! + , partsView_inside_optimal(boundary_inside_optimal.splitIntoPartsView()) // WARNING !! changes the order of boundary_inside !! + , inside_loc_to_line_minimum(PolygonUtils::createLocToLineGrid(boundary_inside_minimum, comb_boundary_offset)) + , inside_loc_to_line_optimal(PolygonUtils::createLocToLineGrid(boundary_inside_optimal, comb_boundary_offset)) + , move_inside_distance(move_inside_distance) + , travel_avoid_distance(travel_avoid_distance) { } -bool Comb::calc -( +bool Comb::calc( bool perform_z_hops, bool perform_z_hops_only_when_collides, const ExtruderTrain& train, // NOTE: USe for travel settings and 'extruder-nr' only, don't use for z-hop/retraction/wipe settings, as that should also be settable per mesh! @@ -90,15 +95,14 @@ bool Comb::calc bool _start_inside, bool _end_inside, coord_t max_comb_distance_ignored, - bool &unretract_before_last_travel_move -) + bool& unretract_before_last_travel_move) { - if(shorterThen(end_point - start_point, max_comb_distance_ignored)) + if (shorterThen(end_point - start_point, max_comb_distance_ignored)) { return true; } const Point travel_end_point_before_combing = end_point; - //Move start and end point inside the optimal comb boundary + // Move start and end point inside the optimal comb boundary unsigned int start_inside_poly = NO_INDEX; const bool start_inside = moveInside(boundary_inside_optimal, _start_inside, inside_loc_to_line_optimal.get(), start_point, start_inside_poly); @@ -107,8 +111,8 @@ bool Comb::calc unsigned int start_part_boundary_poly_idx = NO_INDEX; // Added initial value to stop MSVC throwing an exception in debug mode unsigned int end_part_boundary_poly_idx = NO_INDEX; - unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside_optimal.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx); - unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside_optimal.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx); + unsigned int start_part_idx = (start_inside_poly == NO_INDEX) ? NO_INDEX : partsView_inside_optimal.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx); + unsigned int end_part_idx = (end_inside_poly == NO_INDEX) ? NO_INDEX : partsView_inside_optimal.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx); const bool fail_on_unavoidable_obstacles = perform_z_hops && perform_z_hops_only_when_collides; @@ -117,7 +121,15 @@ bool Comb::calc { PolygonsPart part = partsView_inside_optimal.assemblePart(start_part_idx); comb_paths.emplace_back(); - const bool combing_succeeded = LinePolygonsCrossings::comb(part, *inside_loc_to_line_optimal, start_point, end_point, comb_paths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); + const bool combing_succeeded = LinePolygonsCrossings::comb( + part, + *inside_loc_to_line_optimal, + start_point, + end_point, + comb_paths.back(), + -offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + fail_on_unavoidable_obstacles); // If the endpoint of the travel path changes with combing, then it means that we are moving to an outer wall // and we should unretract before the last travel move when travelling to that outer wall unretract_before_last_travel_move = combing_succeeded && end_point != travel_end_point_before_combing; @@ -131,10 +143,11 @@ bool Comb::calc unsigned int end_inside_poly_min = NO_INDEX; const bool end_inside_min = moveInside(boundary_inside_minimum, _end_inside, inside_loc_to_line_minimum.get(), end_point, end_inside_poly_min); - unsigned int start_part_boundary_poly_idx_min { }; - unsigned int end_part_boundary_poly_idx_min { }; - unsigned int start_part_idx_min = (start_inside_poly_min == NO_INDEX)? NO_INDEX : partsView_inside_minimum.getPartContaining(start_inside_poly_min, &start_part_boundary_poly_idx_min); - unsigned int end_part_idx_min = (end_inside_poly_min == NO_INDEX)? NO_INDEX : partsView_inside_minimum.getPartContaining(end_inside_poly_min, &end_part_boundary_poly_idx_min); + unsigned int start_part_boundary_poly_idx_min{}; + unsigned int end_part_boundary_poly_idx_min{}; + unsigned int start_part_idx_min + = (start_inside_poly_min == NO_INDEX) ? NO_INDEX : partsView_inside_minimum.getPartContaining(start_inside_poly_min, &start_part_boundary_poly_idx_min); + unsigned int end_part_idx_min = (end_inside_poly_min == NO_INDEX) ? NO_INDEX : partsView_inside_minimum.getPartContaining(end_inside_poly_min, &end_part_boundary_poly_idx_min); CombPath result_path; bool comb_result; @@ -145,8 +158,16 @@ bool Comb::calc PolygonsPart part = partsView_inside_minimum.assemblePart(start_part_idx_min); comb_paths.emplace_back(); - comb_result = LinePolygonsCrossings::comb(part, *inside_loc_to_line_minimum, start_point, end_point, result_path, -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); - Comb::moveCombPathInside(boundary_inside_minimum, boundary_inside_optimal, result_path, comb_paths.back()); // add altered result_path to combPaths.back() + comb_result = LinePolygonsCrossings::comb( + part, + *inside_loc_to_line_minimum, + start_point, + end_point, + result_path, + -offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + fail_on_unavoidable_obstacles); + Comb::moveCombPathInside(boundary_inside_minimum, boundary_inside_optimal, result_path, comb_paths.back()); // add altered result_path to combPaths.back() // If the endpoint of the travel path changes with combing, then it means that we are moving to an outer wall // and we should unretract before the last travel move when travelling to that outer wall unretract_before_last_travel_move = comb_result && end_point != travel_end_point_before_combing; @@ -160,13 +181,13 @@ bool Comb::calc // when start_point is inside crossing_1_in is of interest // when it is in between inside and outside it is equal to crossing_1_mid - if (perform_z_hops && !perform_z_hops_only_when_collides) //Combing via outside makes combing fail. + if (perform_z_hops && ! perform_z_hops_only_when_collides) // Combing via outside makes combing fail. { return false; } - //Find the crossings using the minimum comb boundary, since it's guaranteed to be as close as we can get to the destination. - //Getting as close as possible prevents exiting the polygon in the wrong direction (e.g. into a hole instead of to the outside). + // Find the crossings using the minimum comb boundary, since it's guaranteed to be as close as we can get to the destination. + // Getting as close as possible prevents exiting the polygon in the wrong direction (e.g. into a hole instead of to the outside). Crossing start_crossing(start_point, start_inside_min, start_part_idx_min, start_part_boundary_poly_idx_min, boundary_inside_minimum, *inside_loc_to_line_minimum); Crossing end_crossing(end_point, end_inside_min, end_part_idx_min, end_part_boundary_poly_idx_min, boundary_inside_minimum, *inside_loc_to_line_minimum); @@ -183,17 +204,17 @@ bool Comb::calc const bool travel_avoid_other_parts = train.settings.get("travel_avoid_other_parts"); - if (travel_avoid_other_parts && !skip_avoid_other_parts_path) + if (travel_avoid_other_parts && ! skip_avoid_other_parts_path) { // compute the crossing points when moving through air // comb through all air, since generally the outside consists of a single part bool success = start_crossing.findOutside(train, getBoundaryOutside(train), end_crossing.in_or_mid, fail_on_unavoidable_obstacles, *this); - if (!success) + if (! success) { return false; } success = end_crossing.findOutside(train, getBoundaryOutside(train), start_crossing.out, fail_on_unavoidable_obstacles, *this); - if (!success) + if (! success) { return false; } @@ -205,25 +226,43 @@ bool Comb::calc // start to boundary assert(start_crossing.dest_part.size() > 0 && "The part we start inside when combing should have been computed already!"); comb_paths.emplace_back(); - //If we're inside the optimal bound, first try the optimal combing path. If it fails, use the minimum path instead. + // If we're inside the optimal bound, first try the optimal combing path. If it fails, use the minimum path instead. constexpr bool fail_for_optimum_bound = true; - bool combing_succeeded = start_inside && LinePolygonsCrossings::comb(boundary_inside_optimal, *inside_loc_to_line_optimal, start_point, start_crossing.in_or_mid, comb_paths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_for_optimum_bound); - if(!combing_succeeded) + bool combing_succeeded = start_inside + && LinePolygonsCrossings::comb( + boundary_inside_optimal, + *inside_loc_to_line_optimal, + start_point, + start_crossing.in_or_mid, + comb_paths.back(), + -offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + fail_for_optimum_bound); + if (! combing_succeeded) { - combing_succeeded = LinePolygonsCrossings::comb(start_crossing.dest_part, *inside_loc_to_line_minimum, start_point, start_crossing.in_or_mid, comb_paths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); + combing_succeeded = LinePolygonsCrossings::comb( + start_crossing.dest_part, + *inside_loc_to_line_minimum, + start_point, + start_crossing.in_or_mid, + comb_paths.back(), + -offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + fail_on_unavoidable_obstacles); } - if (!combing_succeeded) - { // Couldn't comb between start point and computed crossing from the start part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon + if (! combing_succeeded) + { // Couldn't comb between start point and computed crossing from the start part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside + // the polygon return false; } } // through air from boundary to boundary - if (travel_avoid_other_parts && !skip_avoid_other_parts_path) + if (travel_avoid_other_parts && ! skip_avoid_other_parts_path) { comb_paths.emplace_back(); comb_paths.throughAir = true; - if ( vSize(start_crossing.in_or_mid - end_crossing.in_or_mid) < vSize(start_crossing.in_or_mid - start_crossing.out) + vSize(end_crossing.in_or_mid - end_crossing.out) ) + if (vSize(start_crossing.in_or_mid - end_crossing.in_or_mid) < vSize(start_crossing.in_or_mid - start_crossing.out) + vSize(end_crossing.in_or_mid - end_crossing.out)) { // via outside is moving more over the in-between zone comb_paths.back().push_back(start_crossing.in_or_mid); comb_paths.back().push_back(end_crossing.in_or_mid); @@ -231,7 +270,15 @@ bool Comb::calc else { CombPath tmp_comb_path; - bool combing_succeeded = LinePolygonsCrossings::comb(getBoundaryOutside(train), getOutsideLocToLine(train), start_crossing.out, end_crossing.out, tmp_comb_path, offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, true); + bool combing_succeeded = LinePolygonsCrossings::comb( + getBoundaryOutside(train), + getOutsideLocToLine(train), + start_crossing.out, + end_crossing.out, + tmp_comb_path, + offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + true); if (combing_succeeded) { @@ -288,18 +335,36 @@ bool Comb::calc // boundary to end assert(end_crossing.dest_part.size() > 0 && "The part we end up inside when combing should have been computed already!"); comb_paths.emplace_back(); - //If we're inside the optimal bound, first try the optimal combing path. If it fails, use the minimum path instead. + // If we're inside the optimal bound, first try the optimal combing path. If it fails, use the minimum path instead. constexpr bool fail_for_optimum_bound = true; - bool combing_succeeded = end_inside && LinePolygonsCrossings::comb(boundary_inside_optimal, *inside_loc_to_line_optimal, end_crossing.in_or_mid, end_point, comb_paths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_for_optimum_bound); - if(!combing_succeeded) + bool combing_succeeded = end_inside + && LinePolygonsCrossings::comb( + boundary_inside_optimal, + *inside_loc_to_line_optimal, + end_crossing.in_or_mid, + end_point, + comb_paths.back(), + -offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + fail_for_optimum_bound); + if (! combing_succeeded) { - combing_succeeded = LinePolygonsCrossings::comb(end_crossing.dest_part, *inside_loc_to_line_minimum, end_crossing.in_or_mid, end_point, comb_paths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); + combing_succeeded = LinePolygonsCrossings::comb( + end_crossing.dest_part, + *inside_loc_to_line_minimum, + end_crossing.in_or_mid, + end_point, + comb_paths.back(), + -offset_dist_to_get_from_on_the_polygon_to_outside, + max_comb_distance_ignored, + fail_on_unavoidable_obstacles); } // If the endpoint of the travel path changes with combing, then it means that we are moving to an outer wall // and we should unretract before the last travel move when traveling to that outer wall unretract_before_last_travel_move = combing_succeeded && end_point != travel_end_point_before_combing; - if (!combing_succeeded) - { // Couldn't comb between end point and computed crossing to the end part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon + if (! combing_succeeded) + { // Couldn't comb between end point and computed crossing to the end part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the + // polygon return false; } } @@ -318,7 +383,7 @@ void Comb::moveCombPathInside(Polygons& boundary_inside, Polygons& boundary_insi return; } comb_path_output.push_back(comb_path_input[0]); - for(unsigned int point_idx = 1; point_idx close_towards_start_penalty_function([_dest_point](Point candidate){ return vSize2((candidate - _dest_point) / 10); }); + std::function close_towards_start_penalty_function( + [_dest_point](Point candidate) + { + return vSize2((candidate - _dest_point) / 10); + }); dest_part = partsView_inside.assemblePart(dest_part_idx); ClosestPolygonPoint boundary_crossing_point; @@ -387,32 +463,39 @@ void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, cons dest_part_poly_indices.emplace(poly_idx); } coord_t dist2_score = std::numeric_limits::max(); - std::function line_processor + std::function line_processor = [close_to, _dest_point, &boundary_crossing_point, &dist2_score, &dest_part_poly_indices](const PolygonsPointIndex& boundary_segment) + { + if (dest_part_poly_indices.find(boundary_segment.poly_idx) == dest_part_poly_indices.end()) + { // we're not looking at a polygon from the dest_part + return true; // a.k.a. continue; + } + Point closest_here = LinearAlg2D::getClosestOnLineSegment(close_to, boundary_segment.p(), boundary_segment.next().p()); + coord_t dist2_score_here = vSize2(close_to - closest_here) + vSize2(_dest_point - closest_here) / 10; + if (dist2_score_here < dist2_score) { - if (dest_part_poly_indices.find(boundary_segment.poly_idx) == dest_part_poly_indices.end()) - { // we're not looking at a polygon from the dest_part - return true; // a.k.a. continue; - } - Point closest_here = LinearAlg2D::getClosestOnLineSegment(close_to, boundary_segment.p(), boundary_segment.next().p()); - coord_t dist2_score_here = vSize2(close_to - closest_here) + vSize2(_dest_point - closest_here) / 10; - if (dist2_score_here < dist2_score) - { - dist2_score = dist2_score_here; - boundary_crossing_point = ClosestPolygonPoint(closest_here, boundary_segment.point_idx, boundary_segment.getPolygon(), boundary_segment.poly_idx); - } - return true; - }; + dist2_score = dist2_score_here; + boundary_crossing_point = ClosestPolygonPoint(closest_here, boundary_segment.point_idx, boundary_segment.getPolygon(), boundary_segment.poly_idx); + } + return true; + }; inside_loc_to_line.processLine(std::make_pair(dest_point, close_to), line_processor); } Point result(boundary_crossing_point.p()); // the inside point of the crossing - if (!boundary_crossing_point.isValid()) + if (! boundary_crossing_point.isValid()) { // no point has been found in the sparse grid result = dest_point; } - ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, boundary_crossing_point, offset_dist_to_get_from_on_the_polygon_to_outside, &boundary_inside, &inside_loc_to_line, close_towards_start_penalty_function); + ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside( + dest_part, + result, + boundary_crossing_point, + offset_dist_to_get_from_on_the_polygon_to_outside, + &boundary_inside, + &inside_loc_to_line, + close_towards_start_penalty_function); if (crossing_1_in_cp.isValid()) { dest_crossing_poly = crossing_1_in_cp.poly; @@ -435,7 +518,11 @@ bool Comb::Crossing::findOutside(const ExtruderTrain& train, const Polygons& out if (dest_is_inside || outside.inside(in_or_mid, true)) // start in_between { // move outside Point preferred_crossing_1_out = in_or_mid + normal(close_to - in_or_mid, comber.offset_from_inside_to_outside); - std::function close_to_penalty_function([preferred_crossing_1_out](Point candidate){ return vSize2((candidate - preferred_crossing_1_out) / 2); }); + std::function close_to_penalty_function( + [preferred_crossing_1_out](Point candidate) + { + return vSize2((candidate - preferred_crossing_1_out) / 2); + }); std::optional crossing_1_out_cpp = PolygonUtils::findClose(in_or_mid, outside, comber.getOutsideLocToLine(train), close_to_penalty_function); if (crossing_1_out_cpp) { @@ -466,7 +553,13 @@ bool Comb::Crossing::findOutside(const ExtruderTrain& train, const Polygons& out } -std::shared_ptr> Comb::Crossing::findBestCrossing(const ExtruderTrain& train, const Polygons& outside, ConstPolygonRef from, const Point estimated_start, const Point estimated_end, Comb& comber) +std::shared_ptr> Comb::Crossing::findBestCrossing( + const ExtruderTrain& train, + const Polygons& outside, + ConstPolygonRef from, + const Point estimated_start, + const Point estimated_end, + Comb& comber) { ClosestPolygonPoint* best_in = nullptr; ClosestPolygonPoint* best_out = nullptr; @@ -491,11 +584,13 @@ std::shared_ptr> Comb::Cross // the distance between an arbitrary point and the boundary may well be a couple of centimetres. // So the crossing_dist2 is about 1.000.000 while the detour_dist_2 is in the order of 400.000.000 // In the end we just want to choose between two points which have the _same_ crossing distance, modulo rounding error. - if ((!seen_close_enough_connection && detour_score < best_detour_score) // keep the best as long as we havent seen one close enough (so that we may walk along the polygon to find a closer connection from it in the code below) - || (!seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) // make the one which is close enough the best as soon as we see one close enough - || (seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2 && detour_score < best_detour_score)) // update to keep the best crossing which is close enough already + if ((! seen_close_enough_connection && detour_score < best_detour_score) // keep the best as long as we havent seen one close enough (so that we may walk along the polygon + // to find a closer connection from it in the code below) + || (! seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) // make the one which is close enough the best as soon as we see one close enough + || (seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2 + && detour_score < best_detour_score)) // update to keep the best crossing which is close enough already { - if (!seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) + if (! seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) { seen_close_enough_connection = true; } @@ -521,4 +616,4 @@ std::shared_ptr> Comb::Cross return std::make_shared>(*best_in, *best_out); } -}//namespace cura +} // namespace cura From a8cbef6730e961ac363d4215f56164cddbeae059 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 20 Jul 2023 15:56:30 +0200 Subject: [PATCH 231/656] Applied clang-tidy suggestions CURA-10758 --- src/SkirtBrim.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 0aa1c2243b..83d9522534 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -210,7 +210,7 @@ void SkirtBrim::generate() std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_brim_offsets, Polygons& covered_area, std::vector& allowed_areas_per_extruder) { - std::vector total_length(extruder_count, 0u); + std::vector total_length(extruder_count, 0U); for (size_t offset_idx = 0; offset_idx < all_brim_offsets.size(); offset_idx++) { @@ -220,9 +220,9 @@ std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_bri storage.skirt_brim[offset.extruder_nr].resize(offset.inset_idx + 1); } SkirtBrimLine& output_location = storage.skirt_brim[offset.extruder_nr][offset.inset_idx]; - coord_t added_length = generateOffset(offset, covered_area, allowed_areas_per_extruder, output_location); + const coord_t added_length = generateOffset(offset, covered_area, allowed_areas_per_extruder, output_location); - if (! added_length) + if (added_length == 0) { // no more place for more brim. Trying to satisfy minimum length constraint with generateSecondarySkirtBrim continue; } @@ -230,7 +230,7 @@ std::vector SkirtBrim::generatePrimaryBrim(std::vector& all_bri if (offset.is_last && total_length[offset.extruder_nr] < skirt_brim_minimal_length[offset.extruder_nr] && // This was the last offset of this extruder, but the brim lines don't meet minimal length yet - total_length[offset.extruder_nr] > 0u // No lines got added; we have no extrusion lines to build on + total_length[offset.extruder_nr] > 0U // No lines got added; we have no extrusion lines to build on ) { offset.is_last = false; From d3c032455c111499c2c140f00e6f1ccc4a5e35aa Mon Sep 17 00:00:00 2001 From: jellespijker Date: Fri, 21 Jul 2023 10:42:46 +0000 Subject: [PATCH 232/656] Applied clang-format. --- include/plugins/pluginproxy.h | 10 +++++----- include/plugins/slotproxy.h | 1 + src/Application.cpp | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index b70e5482a9..58fcb956ea 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -17,17 +17,17 @@ #include "utils/types/char_range_literal.h" #include "utils/types/generic.h" -#include -#include -#include -#include - #include #include #include #include #include #include +#include +#include +#include +#include + #include #include #include diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index c674ef48f6..2482e23dd9 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -11,6 +11,7 @@ #include "utils/types/char_range_literal.h" #include + #include #include #include diff --git a/src/Application.cpp b/src/Application.cpp index acd28f0007..0074940150 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -11,6 +11,8 @@ #include "utils/ThreadPool.h" #include "utils/string.h" //For stringcasecompare. +#include //For generating a UUID. +#include //For generating a UUID. #include #include #include @@ -20,8 +22,6 @@ #include #include -#include //For generating a UUID. -#include //For generating a UUID. #include #include #include From ea0cb69f6a3754f8a3b908ed4afef07266ee091c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 23 Jul 2023 11:06:44 +0200 Subject: [PATCH 233/656] Update gRPC definitions Conform agreed upon naming scheme CURA-10805 --- Cura.proto | 6 +++++- conanfile.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cura.proto b/Cura.proto index b97e7f0f24..4942f9c5ce 100644 --- a/Cura.proto +++ b/Cura.proto @@ -8,8 +8,12 @@ message ObjectList repeated Setting settings = 2; // meshgroup settings (for one-at-a-time printing) } +// Resevered IDs: +// 0 ... 99: Broadcasts +// 100 ... 199: Modify +// 200 ... 299: Generate enum SlotID { - BROADCAST_SETTINGS = 0; + SETTINGS_BROADCAST = 0; SIMPLIFY_MODIFY = 100; POSTPROCESS_MODIFY = 101; INFILL_MODIFY = 102; diff --git a/conanfile.py b/conanfile.py index db6341e9d0..950187552b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10805") # TODO: point to `testing` once the CURA-10805 is merged to CURA-10475 def generate(self): deps = CMakeDeps(self) From e058c19bb031bf6ab85bd3467790f2c6d14a601d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 23 Jul 2023 11:13:10 +0200 Subject: [PATCH 234/656] Use SlotID to determine broadcast channel Move type_trait is_broadcast_channel out of generic types to broadcast.h, since this is very plugin specific and otherwise the slot_id.pb.h will gets pulled in in a lot of irrelevant locations. CURA-10805 --- include/plugins/broadcasts.h | 22 ++++++++++++++++------ include/utils/types/generic.h | 20 -------------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h index 341405fb4c..dd0ab746f8 100644 --- a/include/plugins/broadcasts.h +++ b/include/plugins/broadcasts.h @@ -4,25 +4,35 @@ #ifndef PLUGINS_BROADCAST_H #define PLUGINS_BROADCAST_H +#include "cura/plugins/v0/slot_id.pb.h" #include "plugins/converters.h" -#include "utils/types/char_range_literal.h" -#include "utils/types/generic.h" #include +#include + namespace cura::plugins::details { -template -requires utils::is_broadcast_channel_v +template +struct is_broadcast_channel +{ + inline static constexpr bool value{ T1 == T2 }; +}; + +template +inline constexpr bool is_broadcast_channel_v = is_broadcast_channel::value; + +template +requires is_broadcast_channel_v constexpr auto broadcast_message_factory(auto&&... args) { return broadcast_settings_request{}(std::forward(args)...); }; -template -requires utils::is_broadcast_channel_v +template +requires is_broadcast_channel_v constexpr auto broadcast_factory() { return agrpc::RPC<&Stub::PrepareAsyncBroadcastSettings>{}; diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 73e176d996..22858a5fdb 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -4,13 +4,10 @@ #ifndef CURAENGINE_GENERIC_H #define CURAENGINE_GENERIC_H -#include "utils/types/char_range_literal.h" - #include #include #include -#include #include namespace cura::utils @@ -30,23 +27,6 @@ concept grpc_convertable = requires(T value) requires ranges::semiregular; }; -template -class is_broadcast_channel -{ - inline static constexpr bool value_() noexcept - { - constexpr std::string_view t1{ T1.value }; - constexpr std::string_view t2{ T2.value }; - return t1.compare(t2) == 0; - } - -public: - inline static constexpr bool value = value_(); -}; - -template -inline constexpr bool is_broadcast_channel_v = is_broadcast_channel::value; - #ifdef OLDER_APPLE_CLANG // std::integral and std::floating_point are not implemented in older Apple Clang versions < 13 From 36ad04336c65796ddf44e8ab388f377e5a9d9b00 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 23 Jul 2023 11:14:58 +0200 Subject: [PATCH 235/656] Store broadcast subscriptions as slotid See also: https://github.com/Ultimaker/CuraEngine_grpc_defintions/commit/443d8c325a7a90a817f25815a077f64e2591ae67 CURA-10805 --- include/plugins/converters.h | 2 +- include/plugins/metadata.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 5260d33731..f7fd3d050f 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -133,7 +133,7 @@ struct handshake_response .plugin_name = message.plugin_name(), .plugin_version = message.plugin_version(), .peer = std::string{ peer }, - .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; + .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; } }; diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index 7f8b11062a..fadc807d77 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -4,6 +4,7 @@ #ifndef CURAENGINE_INCLUDE_PLUGINS_METADATA_H #define CURAENGINE_INCLUDE_PLUGINS_METADATA_H +#include "cura/plugins/v0/slot_id.pb.h" #include "plugins/types.h" #include @@ -21,7 +22,7 @@ struct plugin_metadata std::string plugin_name; std::string plugin_version; std::string peer; - std::set broadcast_subscriptions; + std::set broadcast_subscriptions; }; struct slot_metadata From 26fb899cf00f39a700fcb2188ec326c08c498629 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 23 Jul 2023 11:54:37 +0200 Subject: [PATCH 236/656] Change plugin broadcast to use SlotID and add dedicated broadcast slot Broadcasting in plugins has been refactored to use `v0::SlotID` instead of `utils::CharRangeLiteral BroadcastChannel`. This provides better clarity and consistency across the system. A new dedicated broadcast slot, `slot_settings_broadcast`, has also been added for dedicated setting broadcasting purposes. This will allow for plugins with a pure virtual listing functionality. These broadcast slot(s) currently have a modify functionality which is never used. I will still need to change this later on, probably need to do that with some polymorphism adding functionality for Broadcast, Modify, Generate, for each slot. This is probably best done with a CRTP pattern Contributes to CURA-10805 --- include/plugins/pluginproxy.h | 12 ++++++------ include/plugins/slotproxy.h | 6 ++++-- include/plugins/slots.h | 15 +++++++++++---- src/communication/ArcusCommunication.cpp | 5 ++++- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 58fcb956ea..20c8e6711c 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -194,10 +194,10 @@ class PluginProxy return ret_value; } - template + template void broadcast(auto&&... args) { - if (! plugin_info_->broadcast_subscriptions.contains(BroadcastChannel.value)) + if (! plugin_info_->broadcast_subscriptions.contains(S)) { return; } @@ -208,7 +208,7 @@ class PluginProxy grpc_context, [this, &grpc_context, &status, &args...]() { - return this->broadcastCall(grpc_context, status, std::forward(args)...); + return this->broadcastCall(grpc_context, status, std::forward(args)...); }, boost::asio::detached); grpc_context.run(); @@ -263,14 +263,14 @@ class PluginProxy co_return; } - template + template boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) { grpc::ClientContext client_context{}; prep_client_context(client_context); - auto broadcaster{ details::broadcast_factory() }; - auto request = details::broadcast_message_factory(std::forward(args)...); + auto broadcaster{ details::broadcast_factory() }; + auto request = details::broadcast_message_factory(std::forward(args)...); auto response = google::protobuf::Empty{}; status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); co_return; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 2482e23dd9..bfeb65d3b3 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -44,6 +44,8 @@ class SlotProxy std::optional plugin_{ std::nullopt }; public: + static constexpr plugins::v0::SlotID slot_id{ SlotID }; + /** * @brief Default constructor. * @@ -81,12 +83,12 @@ class SlotProxy return std::invoke(default_process, std::forward(args)...); } - template + template void broadcast(auto&&... args) { if (plugin_.has_value()) { - plugin_.value().template broadcast(std::forward(args)...); + plugin_.value().template broadcast(std::forward(args)...); } } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 0117ccc7c0..7e7c707445 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -4,6 +4,7 @@ #ifndef PLUGINS_SLOTS_H #define PLUGINS_SLOTS_H +#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" @@ -62,6 +63,10 @@ template using slot_postprocess_ = SlotProxy; +template +using slot_settings_broadcast_ + = SlotProxy; + template struct Typelist { @@ -101,11 +106,11 @@ class Registry, Unit> : public Registry get_type().proxy = Tp{ std::forward(std::move(plugin)) }; } - template + template void broadcast(auto&&... args) { - value_.proxy.template broadcast(std::forward(args)...); - Base::value_.proxy.template broadcast(std::forward(args)...); + value_.proxy.template broadcast(std::forward(args)...); + // TODO: traverse the types and broadcast over each slot } protected: @@ -147,6 +152,7 @@ class SingletonRegistry template struct Holder { + using value_type = T; T proxy; }; @@ -154,8 +160,9 @@ struct Holder using slot_simplify = details::slot_simplify_; using slot_postprocess = details::slot_postprocess_<>; +using slot_settings_broadcast = details::slot_settings_broadcast_<>; -using SlotTypes = details::Typelist; +using SlotTypes = details::Typelist; } // namespace plugins using slots = plugins::details::SingletonRegistry; diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index c3ebf1b2ed..b912be239a 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -523,6 +523,9 @@ void ArcusCommunication::sliceNext() { switch (plugin.id()) { + case cura::proto::SlotID::SETTINGS_BROADCAST: + slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); + break; case cura::proto::SlotID::SIMPLIFY_MODIFY: slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); break; @@ -544,7 +547,7 @@ void ArcusCommunication::sliceNext() private_data->readExtruderSettingsMessage(slice_message->extruders()); // Broadcast the settings to the plugins - slots::instance().broadcast<"BroadcastSettings">(*slice_message); + slots::instance().broadcast(*slice_message); const size_t extruder_count = slice.scene.extruders.size(); // For each setting, register what extruder it should be obtained from (if this is limited to an extruder). From 2ef44a040dad2bb028ab93b9a832f4c62266ea59 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 23 Jul 2023 12:01:55 +0200 Subject: [PATCH 237/656] Renamed operator() method to modify in plugin proxies The operator() method was unclear in its intentions and use when applied to processing plugin proxies. It's been renamed to 'modify', making it clear that it's purpose is to alter or modify the input parameters passed to the plugin. This affects all references to these proxies within the code, improving overall readability. Contribute to CURA-10805 --- benchmark/simplify_benchmark.h | 4 ++-- include/plugins/pluginproxy.h | 2 +- include/plugins/slotproxy.h | 4 ++-- include/plugins/slots.h | 4 ++-- src/communication/ArcusCommunication.cpp | 4 ++-- src/slicer.cpp | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index b5e6d79b0c..5c91874a97 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -64,7 +64,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } @@ -89,7 +89,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = slots::instance().invoke(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 20c8e6711c..25ac9ac433 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -168,7 +168,7 @@ class PluginProxy * * @throws std::runtime_error if communication with the plugin fails. */ - value_type operator()(auto&&... args) + value_type modify(auto&&... args) { agrpc::GrpcContext grpc_context; value_type ret_value{}; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index bfeb65d3b3..8d6f818009 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -74,11 +74,11 @@ class SlotProxy * @param args The arguments for the plugin request. * @return The result of the plugin request or the default behavior. */ - auto operator()(auto&&... args) + constexpr auto modify(auto&&... args) { if (plugin_.has_value()) { - return std::invoke(plugin_.value(), std::forward(args)...); + return plugin_.value().modify(std::forward(args)...); } return std::invoke(default_process, std::forward(args)...); } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 7e7c707445..1d30e69172 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -95,9 +95,9 @@ class Registry, Unit> : public Registry } template - constexpr auto invoke(auto&&... args) + constexpr auto modify(auto&&... args) { - return std::invoke(get(), std::forward(args)...); + return get().modify(std::forward(args)...); } template diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index b912be239a..c1ffa1410c 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -342,7 +342,7 @@ void ArcusCommunication::beginGCode() void ArcusCommunication::flushGCode() { - const std::string& message_str = slots::instance().invoke(private_data->gcode_output_stream.str()); + const std::string& message_str = slots::instance().modify(private_data->gcode_output_stream.str()); if (message_str.size() == 0) { return; @@ -375,7 +375,7 @@ void ArcusCommunication::sendCurrentPosition(const Point& position) void ArcusCommunication::sendGCodePrefix(const std::string& prefix) const { std::shared_ptr message = std::make_shared(); - message->set_data(slots::instance().invoke(prefix)); + message->set_data(slots::instance().modify(prefix)); private_data->socket->sendMessage(message); } diff --git a/src/slicer.cpp b/src/slicer.cpp index 3e2089ed6f..f02003e925 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -788,7 +788,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. // polygons = Simplify(mesh->settings).polygon(polygons); - polygons = slots::instance().invoke( + polygons = slots::instance().modify( polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), From ddd70d0d22df09913c94c9557b9ba591ad9dac5f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 23 Jul 2023 13:50:17 +0200 Subject: [PATCH 238/656] Add termination condition for traversing broadcast calls Added the 'broadcast' method to the 'Registry' class to enable broadcasting over each slot, addressing a previous TODO comment. This change allows the slot-based functionality to traverse various types and perform actions accordingly. Please note that this hides a non-virtual function from struct 'Registry, Unit>' Contribute to CURA-10805 --- include/plugins/slots.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 1d30e69172..5284f12df4 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -78,6 +78,9 @@ class Registry; template class Unit> class Registry, Unit> { +public: + template + void broadcast(auto&&... args) {} // Base case, do nothing }; template class Unit> @@ -86,6 +89,7 @@ class Registry, Unit> : public Registry public: using ValueType = T; using Base = Registry, Unit>; + using Base::broadcast; friend Base; template @@ -110,7 +114,7 @@ class Registry, Unit> : public Registry void broadcast(auto&&... args) { value_.proxy.template broadcast(std::forward(args)...); - // TODO: traverse the types and broadcast over each slot + Base::template broadcast(std::forward(args)...); } protected: From 7f5a2f0997b0dbc8f45d9c4c02c2b809eb7ff7d3 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 23 Jul 2023 11:50:51 +0000 Subject: [PATCH 239/656] Applied clang-format. --- include/plugins/slots.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 5284f12df4..80613cad1b 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -80,7 +80,9 @@ class Registry, Unit> { public: template - void broadcast(auto&&... args) {} // Base case, do nothing + void broadcast(auto&&... args) + { + } // Base case, do nothing }; template class Unit> From 32e0946ead50e61a3a207bd190948046495ee58d Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Sun, 23 Jul 2023 21:28:40 +0200 Subject: [PATCH 240/656] Round some results to nearest int instead of truncating --- include/utils/IntPoint.h | 8 ++++---- tests/arcus/ArcusCommunicationPrivateTest.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/utils/IntPoint.h b/include/utils/IntPoint.h index 68aa4f84da..c4cc098ba9 100644 --- a/include/utils/IntPoint.h +++ b/include/utils/IntPoint.h @@ -50,7 +50,7 @@ INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1. INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.X, p0.Y-p1.Y); } INLINE Point operator*(const Point& p0, const coord_t i) { return Point(p0.X * i, p0.Y * i); } template::value, T>::type> //Use only for numeric types. -INLINE Point operator*(const Point& p0, const T i) { return Point(p0.X * i, p0.Y * i); } +INLINE Point operator*(const Point& p0, const T i) { return Point(std::llrint(p0.X * i), std::llrint(p0.Y * i)); } template::value, T>::type> //Use only for numeric types. INLINE Point operator*(const T i, const Point& p0) { return p0 * i; } INLINE Point operator/(const Point& p0, const coord_t i) { return Point(p0.X/i, p0.Y/i); } @@ -278,9 +278,9 @@ class Point3Matrix Point3 apply(const Point3 p) const { - return Point3(p.x * matrix[0] + p.y * matrix[1] + p.z * matrix[2] - , p.x * matrix[3] + p.y * matrix[4] + p.z * matrix[5] - , p.x * matrix[6] + p.y * matrix[7] + p.z * matrix[8]); + return Point3(std::llrint(p.x * matrix[0] + p.y * matrix[1] + p.z * matrix[2]) + , std::llrint(p.x * matrix[3] + p.y * matrix[4] + p.z * matrix[5]) + , std::llrint(p.x * matrix[6] + p.y * matrix[7] + p.z * matrix[8])); } /*! diff --git a/tests/arcus/ArcusCommunicationPrivateTest.cpp b/tests/arcus/ArcusCommunicationPrivateTest.cpp index 88a7b99b6d..b96dd840ff 100644 --- a/tests/arcus/ArcusCommunicationPrivateTest.cpp +++ b/tests/arcus/ArcusCommunicationPrivateTest.cpp @@ -225,7 +225,7 @@ TEST_F(ArcusCommunicationPrivateTest, ReadMeshGroupMessage) const size_t num_vertex = raw_vertices.size(); for (size_t i_coord = 0; i_coord < num_vertex; ++i_coord) { - auto micrometers = static_cast(raw_vertices[i_coord] * 1000.F); + auto micrometers = static_cast(std::llrintf(raw_vertices[i_coord] * 1000.F)); 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]); } From a2906ee0835fcc2ebf4b9b6f92e71201571c063d Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 25 Jul 2023 14:45:15 +0200 Subject: [PATCH 241/656] Add plugin value to infill pattern enum type CURA-10720 --- include/settings/EnumSettings.h | 1 + src/settings/Settings.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index 42818beb5b..bb289c4e8a 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -27,6 +27,7 @@ enum class EFillMethod CROSS_3D, GYROID, LIGHTNING, + PLUGIN, NONE // NOTE: Should remain last! (May be used in testing to enumarate the enum.) }; diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 56ddaa26ef..f2781cb5a3 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -443,6 +443,9 @@ EFillMethod Settings::get(const std::string& key) const { return EFillMethod::LIGHTNING; } + else if (value.rfind("PLUGIN", 0) == 0) { + return EFillMethod::PLUGIN; + } else // Default. { return EFillMethod::NONE; From 57ef7a951def046598df0fa26de123006db19844 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 25 Jul 2023 12:46:05 +0000 Subject: [PATCH 242/656] Applied clang-format. --- include/settings/EnumSettings.h | 126 ++++++++++++++++---------------- src/settings/Settings.cpp | 48 ++++++------ 2 files changed, 90 insertions(+), 84 deletions(-) diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index bb289c4e8a..b594854f2a 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -1,5 +1,5 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef ENUMSETTINGS_H #define ENUMSETTINGS_H @@ -28,7 +28,7 @@ enum class EFillMethod GYROID, LIGHTNING, PLUGIN, - NONE // NOTE: Should remain last! (May be used in testing to enumarate the enum.) + NONE // NOTE: Should remain last! (May be used in testing to enumarate the enum.) }; /*! @@ -116,8 +116,8 @@ enum class CombingMode */ enum class DraftShieldHeightLimitation { - FULL, //Draft shield takes full height of the print. - LIMITED //Draft shield is limited by draft_shield_height setting. + FULL, // Draft shield takes full height of the print. + LIMITED // Draft shield is limited by draft_shield_height setting. }; enum class SupportDistPriority @@ -138,74 +138,74 @@ enum class SlicingTolerance */ enum class EGCodeFlavor { -/** - * Marlin flavored GCode is Marlin/Sprinter based GCode. - * This is the most commonly used GCode set. - * G0 for moves, G1 for extrusion. - * E values give mm of filament extrusion. - * Retraction is done on E values with G1. Start/end code is added. - * M106 Sxxx and M107 are used to turn the fan on/off. - **/ + /** + * Marlin flavored GCode is Marlin/Sprinter based GCode. + * This is the most commonly used GCode set. + * G0 for moves, G1 for extrusion. + * E values give mm of filament extrusion. + * Retraction is done on E values with G1. Start/end code is added. + * M106 Sxxx and M107 are used to turn the fan on/off. + **/ MARLIN = 0, -/** - * UltiGCode flavored is Marlin based GCode. - * UltiGCode uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode. - * G0 for moves, G1 for extrusion. - * E values give mm^3 of filament extrusion. Ignores the filament diameter setting. - * Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction. - * Start/end code is not added. - * M106 Sxxx and M107 are used to turn the fan on/off. - **/ + /** + * UltiGCode flavored is Marlin based GCode. + * UltiGCode uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode. + * G0 for moves, G1 for extrusion. + * E values give mm^3 of filament extrusion. Ignores the filament diameter setting. + * Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction. + * Start/end code is not added. + * M106 Sxxx and M107 are used to turn the fan on/off. + **/ ULTIGCODE = 1, -/** - * Makerbot flavored GCode. - * Looks a lot like RepRap GCode with a few changes. Requires MakerWare to convert to X3G files. - * Heating needs to be done with M104 Sxxx T0 - * No G21 or G90 - * Fan ON is M126 T0 (No fan strength control?) - * Fan OFF is M127 T0 - * Homing is done with G162 X Y F2000 - **/ + /** + * Makerbot flavored GCode. + * Looks a lot like RepRap GCode with a few changes. Requires MakerWare to convert to X3G files. + * Heating needs to be done with M104 Sxxx T0 + * No G21 or G90 + * Fan ON is M126 T0 (No fan strength control?) + * Fan OFF is M127 T0 + * Homing is done with G162 X Y F2000 + **/ MAKERBOT = 2, -/** - * Bits From Bytes GCode. - * BFB machines use RPM instead of E. Which is coupled to the F instead of independed. (M108 S[deciRPM]) - * Need X,Y,Z,F on every line. - * Needs extruder ON/OFF (M101, M103), has auto-retrection (M227 S[2560*mm] P[2560*mm]) - **/ + /** + * Bits From Bytes GCode. + * BFB machines use RPM instead of E. Which is coupled to the F instead of independed. (M108 S[deciRPM]) + * Need X,Y,Z,F on every line. + * Needs extruder ON/OFF (M101, M103), has auto-retrection (M227 S[2560*mm] P[2560*mm]) + **/ BFB = 3, -/** - * MACH3 GCode - * MACH3 is CNC control software, which expects A/B/C/D for extruders, instead of E. - **/ + /** + * MACH3 GCode + * MACH3 is CNC control software, which expects A/B/C/D for extruders, instead of E. + **/ MACH3 = 4, -/** - * RepRap volumatric flavored GCode is Marlin based GCode. - * Volumatric uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode. - * G0 for moves, G1 for extrusion. - * E values give mm^3 of filament extrusion. Ignores the filament diameter setting. - * Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction. - * M106 Sxxx and M107 are used to turn the fan on/off. - **/ + /** + * RepRap volumatric flavored GCode is Marlin based GCode. + * Volumatric uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode. + * G0 for moves, G1 for extrusion. + * E values give mm^3 of filament extrusion. Ignores the filament diameter setting. + * Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction. + * M106 Sxxx and M107 are used to turn the fan on/off. + **/ MARLIN_VOLUMATRIC = 5, -/** - * Griffin flavored is Marlin based GCode. - * This is a type of RepRap used for machines with multiple extruder trains. - * G0 for moves, G1 for extrusion. - * E values give mm of filament extrusion. - * E values are stored separately per extruder train. - * Retraction is done on E values with G1. Start/end code is added. - * M227 is used to initialize a single extrusion train. - **/ + /** + * Griffin flavored is Marlin based GCode. + * This is a type of RepRap used for machines with multiple extruder trains. + * G0 for moves, G1 for extrusion. + * E values give mm of filament extrusion. + * E values are stored separately per extruder train. + * Retraction is done on E values with G1. Start/end code is added. + * M227 is used to initialize a single extrusion train. + **/ GRIFFIN = 6, REPETIER = 7, -/** - * Real RepRap GCode suitable for printers using RepRap firmware (e.g. Duet controllers) - **/ + /** + * Real RepRap GCode suitable for printers using RepRap firmware (e.g. Duet controllers) + **/ REPRAP = 8, }; @@ -231,6 +231,6 @@ enum class InsetDirection CENTER_LAST }; -} //Cura namespace. +} // namespace cura -#endif //ENUMSETTINGS_H \ No newline at end of file +#endif // ENUMSETTINGS_H \ No newline at end of file diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index f2781cb5a3..92e1e6afe9 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -1,14 +1,7 @@ // Copyright (c) 2022 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include -#include // regex parsing for temp flow graph -#include // ostringstream -#include -#include //Parsing strings (stod, stoul). - -#include +#include "settings/Settings.h" #include "Application.h" //To get the extruders. #include "BeadingStrategy/BeadingStrategyFactory.h" @@ -16,7 +9,6 @@ #include "Slice.h" #include "settings/EnumSettings.h" #include "settings/FlowTempGraph.h" -#include "settings/Settings.h" #include "settings/types/Angle.h" #include "settings/types/Duration.h" //For duration and time settings. #include "settings/types/LayerIndex.h" //For layer index settings. @@ -24,8 +16,17 @@ #include "settings/types/Temperature.h" //For temperature settings. #include "settings/types/Velocity.h" //For velocity settings. #include "utils/FMatrix4x3.h" -#include "utils/string.h" //For Escaped. #include "utils/polygon.h" +#include "utils/string.h" //For Escaped. + +#include + +#include +#include +#include // regex parsing for temp flow graph +#include // ostringstream +#include +#include //Parsing strings (stod, stoul). namespace cura { @@ -112,7 +113,8 @@ ExtruderTrain& Settings::get(const std::string& key) const return Application::getInstance().current_slice->scene.extruders[extruder_nr]; } -template<> std::vector Settings::get>(const std::string& key) const +template<> +std::vector Settings::get>(const std::string& key) const { int extruder_nr = std::atoi(get(key).c_str()); std::vector ret; @@ -133,7 +135,8 @@ template<> std::vector Settings::get template<> LayerIndex Settings::get(const std::string& key) const { - return std::atoi(get(key).c_str()) - 1; // For the user we display layer numbers starting from 1, but we start counting from 0. Still it may be negative for Raft layers. + return std::atoi(get(key).c_str()) + - 1; // For the user we display layer numbers starting from 1, but we start counting from 0. Still it may be negative for Raft layers. } template<> @@ -240,14 +243,15 @@ FlowTempGraph Settings::get(const std::string& key) const return result; } -template<> Polygons Settings::get(const std::string& key) const +template<> +Polygons Settings::get(const std::string& key) const { std::string value_string = get(key); Polygons result; if (value_string.empty()) { - return result; //Empty at this point. + return result; // Empty at this point. } /* We're looking to match one or more floating point values separated by * commas and surrounded by square brackets. Note that because the QML @@ -260,22 +264,22 @@ template<> Polygons Settings::get(const std::string& key) const if (std::regex_search(value_string, polygons_match, polygons_regex) && polygons_match.size() > 1) { std::string polygons_string = polygons_match.str(1); - + std::regex polygon_regex(R"(\[((\[[^\[\]]*\]\s*,?\s*)*)\]\s*,?)"); // matches with a list of lists (a list of 2D vertices) std::smatch polygon_match; - - std::regex_token_iterator rend; //Default constructor gets the end-of-sequence iterator. + + std::regex_token_iterator rend; // Default constructor gets the end-of-sequence iterator. std::regex_token_iterator polygon_match_iter(polygons_string.begin(), polygons_string.end(), polygon_regex, 0); while (polygon_match_iter != rend) { std::string polygon_str = *polygon_match_iter++; - + result.emplace_back(); PolygonRef poly = result.back(); std::regex point2D_regex(R"(\[([^,\[]*),([^,\]]*)\])"); // matches to a list of exactly two things - const int submatches[] = {1, 2}; // Match first number and second number of a pair. + const int submatches[] = { 1, 2 }; // Match first number and second number of a pair. std::regex_token_iterator match_iter(polygon_str.begin(), polygon_str.end(), point2D_regex, submatches); while (match_iter != rend) { @@ -443,7 +447,8 @@ EFillMethod Settings::get(const std::string& key) const { return EFillMethod::LIGHTNING; } - else if (value.rfind("PLUGIN", 0) == 0) { + else if (value.rfind("PLUGIN", 0) == 0) + { return EFillMethod::PLUGIN; } else // Default. @@ -519,7 +524,8 @@ EZSeamType Settings::get(const std::string& key) const { return EZSeamType::RANDOM; } - else if (value == "back") // It's called 'back' internally because originally this was intended to allow the user to put the seam in the back of the object where it's less visible. + else if (value == "back") // It's called 'back' internally because originally this was intended to allow the user to put the seam in the back of the object where it's less + // visible. { return EZSeamType::USER_SPECIFIED; } From 8d1d3115baf4d64260e8e7a0c081602764be6b0c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 25 Jul 2023 15:28:16 +0200 Subject: [PATCH 243/656] Fix unit tests CURA-10720 --- include/settings/EnumSettings.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index b594854f2a..0f0489a0d2 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -27,8 +27,8 @@ enum class EFillMethod CROSS_3D, GYROID, LIGHTNING, - PLUGIN, - NONE // NOTE: Should remain last! (May be used in testing to enumarate the enum.) + NONE, // NOTE: Should remain last! (May be used in testing to enumarate the enum.) + PLUGIN, // Place plugin after none to prevent it from being tested in the gtest suite. }; /*! From 3844e0c8569dd69f2cc82cde91a854a96c4208c9 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 27 Jul 2023 11:20:13 +0200 Subject: [PATCH 244/656] skirt_brim remains empty when generating the brimlines around support CURA-10502 --- src/SkirtBrim.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index dc0ecf83b1..6aed96d7fe 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -403,6 +403,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) } } + if (skirt_around_prime_tower_brim) { const int prime_tower_brim_extruder_nr = storage.primeTower.extruder_order[0]; @@ -626,6 +627,12 @@ void SkirtBrim::generateSupportBrim() const coord_t brim_width = brim_line_width * line_count; coord_t skirt_brim_length = 0; + + if (storage.skirt_brim[support_infill_extruder.extruder_nr].empty()) + { + storage.skirt_brim[support_infill_extruder.extruder_nr].emplace_back(); + } + for (const SkirtBrimLine& brim_line : storage.skirt_brim[support_infill_extruder.extruder_nr]) { skirt_brim_length += brim_line.closed_polygons.polygonLength(); From aaccc0ca662bfc1cab21fcd6d92edf8836804cec Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jul 2023 12:36:42 +0200 Subject: [PATCH 245/656] Access slot-register functions via SlotID instead of the full type. Users (of plugins) should only have to know about the SlotID enum. part of CURA-10805 --- include/plugins/slots.h | 39 ++++++++++++------------ src/communication/ArcusCommunication.cpp | 6 ++-- src/slicer.cpp | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 80613cad1b..fbc8e0b664 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -79,7 +79,7 @@ template class Unit> class Registry, Unit> { public: - template + template void broadcast(auto&&... args) { } // Base case, do nothing @@ -94,48 +94,49 @@ class Registry, Unit> : public Registry using Base::broadcast; friend Base; - template - constexpr Tp& get() + template + constexpr auto& get() { - return get_type().proxy; + return get_type().proxy; } - template + template constexpr auto modify(auto&&... args) { - return get().modify(std::forward(args)...); + return get().modify(std::forward(args)...); } - template + template void connect(auto&& plugin) { - get_type().proxy = Tp{ std::forward(std::move(plugin)) }; + using Tp = decltype(get_type().proxy); + get_type().proxy = Tp{ std::forward(std::move(plugin)) }; } - template + template void broadcast(auto&&... args) { - value_.proxy.template broadcast(std::forward(args)...); - Base::template broadcast(std::forward(args)...); + value_.proxy.template broadcast(std::forward(args)...); + Base::template broadcast(std::forward(args)...); } protected: - template - constexpr Unit& get_type() + template + constexpr auto& get_type() { - return get_helper(std::is_same{}); + return get_helper(std::bool_constant{}); } - template - constexpr Unit& get_helper(std::true_type) + template + constexpr auto& get_helper(std::true_type) { return value_; } - template - constexpr Unit& get_helper(std::false_type) + template + constexpr auto& get_helper(std::false_type) { - return Base::template get_type(); + return Base::template get_type(); } Unit value_; diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index c1ffa1410c..70df99a55f 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -342,7 +342,7 @@ void ArcusCommunication::beginGCode() void ArcusCommunication::flushGCode() { - const std::string& message_str = slots::instance().modify(private_data->gcode_output_stream.str()); + const std::string& message_str = slots::instance().modify(private_data->gcode_output_stream.str()); if (message_str.size() == 0) { return; @@ -375,7 +375,7 @@ void ArcusCommunication::sendCurrentPosition(const Point& position) void ArcusCommunication::sendGCodePrefix(const std::string& prefix) const { std::shared_ptr message = std::make_shared(); - message->set_data(slots::instance().modify(prefix)); + message->set_data(slots::instance().modify(prefix)); private_data->socket->sendMessage(message); } @@ -547,7 +547,7 @@ void ArcusCommunication::sliceNext() private_data->readExtruderSettingsMessage(slice_message->extruders()); // Broadcast the settings to the plugins - slots::instance().broadcast(*slice_message); + slots::instance().broadcast(*slice_message); const size_t extruder_count = slice.scene.extruders.size(); // For each setting, register what extruder it should be obtained from (if this is limited to an extruder). diff --git a/src/slicer.cpp b/src/slicer.cpp index f02003e925..16cf1c63f6 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -788,7 +788,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Finally optimize all the polygons. Every point removed saves time in the long run. // polygons = Simplify(mesh->settings).polygon(polygons); - polygons = slots::instance().modify( + polygons = slots::instance().modify( polygons, mesh->settings.get("meshfix_maximum_resolution"), mesh->settings.get("meshfix_maximum_deviation"), From b26cb029c82bfcfca27dc710983581a30380c76a Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jul 2023 13:45:30 +0200 Subject: [PATCH 246/656] Replace (slot connection) switch statement with factory. This reduces the responsibility of the general communication layer for the plugins by increasing encapsulation. Will also take care of adding new ones automagically. part of CURA-10805 --- include/plugins/slots.h | 37 ++++++++++++++++++++++++ src/communication/ArcusCommunication.cpp | 17 ++--------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index fbc8e0b664..3b4fab22cd 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -67,6 +67,8 @@ template using slot_settings_broadcast_ = SlotProxy; +using slot_to_connect_map_t = std::map)>>; + template struct Typelist { @@ -79,6 +81,10 @@ template class Unit> class Registry, Unit> { public: + void append_to_connect_map(std::map)>>& function_map) + { + } // Base case, do nothing. + template void broadcast(auto&&... args) { @@ -94,6 +100,11 @@ class Registry, Unit> : public Registry using Base::broadcast; friend Base; + void append_to_connect_map(slot_to_connect_map_t& function_map) + { + function_map.insert({ T::slot_id, [&](std::shared_ptr plugin) { this->connect(plugin); } }); + } + template constexpr auto& get() { @@ -170,8 +181,34 @@ using slot_postprocess = details::slot_postprocess_<>; using slot_settings_broadcast = details::slot_settings_broadcast_<>; using SlotTypes = details::Typelist; + +template +class SlotConnectionFactory_ +{ +public: + static SlotConnectionFactory_& instance() + { + static SlotConnectionFactory_ instance; + return instance; + } + + void connect(const plugins::v0::SlotID& slot_id, std::shared_ptr plugin) + { + slot_to_connect_map[slot_id](plugin); + } + +private: + SlotConnectionFactory_() + { + S::instance().append_to_connect_map(slot_to_connect_map); + } + + plugins::details::slot_to_connect_map_t slot_to_connect_map; +}; + } // namespace plugins using slots = plugins::details::SingletonRegistry; +using SlotConnectionFactory = plugins::SlotConnectionFactory_; } // namespace cura diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 70df99a55f..78377ed2f7 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -521,21 +521,8 @@ void ArcusCommunication::sliceNext() { if (plugin.has_address() && plugin.has_port()) { - switch (plugin.id()) - { - case cura::proto::SlotID::SETTINGS_BROADCAST: - slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); - break; - case cura::proto::SlotID::SIMPLIFY_MODIFY: - slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); - break; - case cura::proto::SlotID::POSTPROCESS_MODIFY: - slots::instance().connect(utils::createChannel({ plugin.address(), plugin.port() })); - break; - default: - spdlog::error("Not yet implemented: {}", plugin.id()); - break; - } + const auto slot_id = static_cast(plugin.id()); + SlotConnectionFactory::instance().connect(slot_id, utils::createChannel({ plugin.address(), plugin.port() })); } } #endif // ENABLE_PLUGINS From 7e860330525c091b74dc0a106ecaaac5db7fe0a9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jul 2023 18:02:58 +0200 Subject: [PATCH 247/656] Fix slots used in benchmark (SlotId instead of definition). done as part of CURA-10805 --- benchmark/simplify_benchmark.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 5c91874a97..1dfe5d3716 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -64,7 +64,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } @@ -78,7 +78,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St try { - slots::instance().connect(utils::createChannel({host, port})); + slots::instance().connect(utils::createChannel({host, port})); } catch (std::runtime_error e) { @@ -89,7 +89,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St Polygons simplified; for (const auto& polys : shapes) { - benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); } } } From e32cb04c87e065c013d7d1717376f73be9c8d89c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jul 2023 18:05:35 +0200 Subject: [PATCH 248/656] Should be possible now to make a slot without modify. If NoStub is used instead of the modify one (for that slot, which doesn't exist in the use case here discussed), it should now use the default implementation of the slot instead. part of CURA-10805 --- include/plugins/slotproxy.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 8d6f818009..6b45563ee6 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -21,6 +21,11 @@ namespace cura::plugins { +// (Will be) Used to make/identify slots with no modify (or generate) methods. +class NoStub +{ +}; + /** * @brief A class template representing a proxy for a plugin slot. * @@ -76,7 +81,7 @@ class SlotProxy */ constexpr auto modify(auto&&... args) { - if (plugin_.has_value()) + if (plugin_.has_value() && ! std::is_same{}) { return plugin_.value().modify(std::forward(args)...); } From bfae0ea221aba1f521ebf65e905d17e883b71533 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 27 Jul 2023 18:08:26 +0200 Subject: [PATCH 249/656] Add string-switch type and integrate into settings retrieval This commit introduces a new type 'string-switch' to handle string comparisons in a switch-case format. This is achieved by employing a DJB2a-based hash function to convert each string to a unique, corresponding enum value. The primary motivation behind this was to refactor the existing settings retrieval process, which was heavily reliant on long if-else constructs. By replacing these with more manageable switch statements, the code readability and maintainability is significantly improved. This change also facilitates the addition of new settings by simply extending existing enums and the related switch statements. Contributes to CURA-10702 --- include/settings/EnumSettings.h | 46 ++-- include/utils/types/string_switch.h | 41 ++++ src/settings/Settings.cpp | 328 ++++++++++++++-------------- 3 files changed, 230 insertions(+), 185 deletions(-) create mode 100644 include/utils/types/string_switch.h diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index 0f0489a0d2..1d82feb83c 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -1,5 +1,5 @@ -// Copyright (c) 2021 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef ENUMSETTINGS_H #define ENUMSETTINGS_H @@ -27,7 +27,7 @@ enum class EFillMethod CROSS_3D, GYROID, LIGHTNING, - NONE, // NOTE: Should remain last! (May be used in testing to enumarate the enum.) + NONE, // NOTE: Should remain second last! Before PLUGIN (Might be used in testing to enumerate the enum.) PLUGIN, // Place plugin after none to prevent it from being tested in the gtest suite. }; @@ -39,7 +39,8 @@ enum class EPlatformAdhesion SKIRT, BRIM, RAFT, - NONE + NONE, + PLUGIN, }; /*! @@ -49,7 +50,8 @@ enum class ESupportType { NONE, PLATFORM_ONLY, - EVERYWHERE + EVERYWHERE, + PLUGIN, }; /*! @@ -58,7 +60,8 @@ enum class ESupportType enum class ESupportStructure { NORMAL, - TREE + TREE, + PLUGIN, }; enum class EZSeamType @@ -71,7 +74,8 @@ enum class EZSeamType /* The 'Skirt/brim' type behaves like shortest, except it doesn't try to do tie-breaking for similar locations to * the last attempt, as that gives a different result when the seams are next to each other instead of on top. */ - SKIRT_BRIM + SKIRT_BRIM, + PLUGIN, }; enum class EZSeamCornerPrefType @@ -80,26 +84,30 @@ enum class EZSeamCornerPrefType Z_SEAM_CORNER_PREF_INNER, Z_SEAM_CORNER_PREF_OUTER, Z_SEAM_CORNER_PREF_ANY, - Z_SEAM_CORNER_PREF_WEIGHTED + Z_SEAM_CORNER_PREF_WEIGHTED, + PLUGIN, }; enum class ESurfaceMode { NORMAL, SURFACE, - BOTH + BOTH, + PLUGIN, }; enum class FillPerimeterGapMode { NOWHERE, - EVERYWHERE + EVERYWHERE, + PLUGIN, }; enum class BuildPlateShape { RECTANGULAR, - ELLIPTIC + ELLIPTIC, + PLUGIN, }; enum class CombingMode @@ -108,7 +116,8 @@ enum class CombingMode ALL, NO_SKIN, NO_OUTER_SURFACES, - INFILL + INFILL, + PLUGIN, }; /*! @@ -117,20 +126,23 @@ enum class CombingMode enum class DraftShieldHeightLimitation { FULL, // Draft shield takes full height of the print. - LIMITED // Draft shield is limited by draft_shield_height setting. + LIMITED, // Draft shield is limited by draft_shield_height setting. + PLUGIN, }; enum class SupportDistPriority { XY_OVERRIDES_Z, - Z_OVERRIDES_XY + Z_OVERRIDES_XY, + PLUGIN, }; enum class SlicingTolerance { MIDDLE, INCLUSIVE, - EXCLUSIVE + EXCLUSIVE, + PLUGIN, }; /*! * Different flavors of GCode. Some machines require different types of GCode. @@ -207,6 +219,7 @@ enum class EGCodeFlavor * Real RepRap GCode suitable for printers using RepRap firmware (e.g. Duet controllers) **/ REPRAP = 8, + PLUGIN = 9, }; /*! @@ -228,7 +241,8 @@ enum class InsetDirection * If the innermost wall is a central wall, it is printed last. Otherwise * prints the same as inside out. */ - CENTER_LAST + CENTER_LAST, + PLUGIN, }; } // namespace cura diff --git a/include/utils/types/string_switch.h b/include/utils/types/string_switch.h new file mode 100644 index 0000000000..e7d9689aac --- /dev/null +++ b/include/utils/types/string_switch.h @@ -0,0 +1,41 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_TYPES_STRING_SWITCH_H +#define UTILS_TYPES_STRING_SWITCH_H + +#include + +namespace cura::utils +{ + +// Source: https://learnmoderncpp.com/2020/06/01/strings-as-switch-case-labels/ +inline constexpr uint64_t hash_djb2a(const std::string_view value) +{ + uint64_t hash{ 5381 }; + for (unsigned char c : value) + { + hash = ((hash << 5) + hash) ^ c; + } + return hash; +} + +inline constexpr uint64_t hash_enum(const std::string_view value) +{ + constexpr uint64_t plugin_namespace_sep_location{ 6 }; + if (value.size() > plugin_namespace_sep_location && value.at(plugin_namespace_sep_location) == ':') + { + return hash_djb2a("plugin"); + } + return hash_djb2a(value); +} + +constexpr inline auto operator""_sw(const char* str, size_t len) +{ + return hash_enum(std::string_view{ str, len }); +} + + +} // namespace cura::utils + +#endif // UTILS_TYPES_STRING_SWITCH_H diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 92e1e6afe9..594aa2fccb 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include "settings/Settings.h" @@ -18,6 +18,7 @@ #include "utils/FMatrix4x3.h" #include "utils/polygon.h" #include "utils/string.h" //For Escaped. +#include "utils/types/string_switch.h" //For string switch. #include @@ -135,8 +136,8 @@ std::vector Settings::get>(const std template<> LayerIndex Settings::get(const std::string& key) const { - return std::atoi(get(key).c_str()) - - 1; // For the user we display layer numbers starting from 1, but we start counting from 0. Still it may be negative for Raft layers. + // For the user we display layer numbers starting from 1, but we start counting from 0. Still it may be negative for Raft layers. + return std::atoi(get(key).c_str()) - 1; } template<> @@ -185,12 +186,16 @@ template<> DraftShieldHeightLimitation Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "limited") + using namespace cura::utils; + switch (hash_enum(value)) { + case "full"_sw: + return DraftShieldHeightLimitation::FULL; + case "limited"_sw: return DraftShieldHeightLimitation::LIMITED; - } - else // if (value == "full") or default. - { + case "plugin"_sw: + return DraftShieldHeightLimitation::PLUGIN; + default: return DraftShieldHeightLimitation::FULL; } } @@ -350,109 +355,74 @@ template<> EGCodeFlavor Settings::get(const std::string& key) const { const std::string& value = get(key); - // I wish that switch statements worked for std::string... - if (value == "Griffin") + using namespace cura::utils; + switch (hash_enum(value)) { + case "Marlin"_sw: + return EGCodeFlavor::MARLIN; + case "Griffin"_sw: return EGCodeFlavor::GRIFFIN; - } - else if (value == "UltiGCode") - { + case "UltiGCode"_sw: return EGCodeFlavor::ULTIGCODE; - } - else if (value == "Makerbot") - { + case "Makerbot"_sw: return EGCodeFlavor::MAKERBOT; - } - else if (value == "BFB") - { + case "BFB"_sw: return EGCodeFlavor::BFB; - } - else if (value == "MACH3") - { + case "MACH3"_sw: return EGCodeFlavor::MACH3; - } - else if (value == "RepRap (Volumetric)") - { + case "RepRap (Volumetric)"_sw: return EGCodeFlavor::MARLIN_VOLUMATRIC; - } - else if (value == "Repetier") - { + case "Repetier"_sw: return EGCodeFlavor::REPETIER; - } - else if (value == "RepRap (RepRap)") - { + case "RepRap (RepRap)"_sw: return EGCodeFlavor::REPRAP; + case "plugin"_sw: + return EGCodeFlavor::PLUGIN; + default: + return EGCodeFlavor::MARLIN; } - // Default: - return EGCodeFlavor::MARLIN; } template<> EFillMethod Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "lines") + using namespace cura::utils; + switch (hash_enum(value)) { + case "none"_sw: + return EFillMethod::NONE; + case "lines"_sw: return EFillMethod::LINES; - } - else if (value == "grid") - { + case "grid"_sw: return EFillMethod::GRID; - } - else if (value == "cubic") - { + case "cubic"_sw: return EFillMethod::CUBIC; - } - else if (value == "cubicsubdiv") - { + case "cubicsubdiv"_sw: return EFillMethod::CUBICSUBDIV; - } - else if (value == "tetrahedral") - { + case "tetrahedral"_sw: return EFillMethod::TETRAHEDRAL; - } - else if (value == "quarter_cubic") - { + case "quarter_cubic"_sw: return EFillMethod::QUARTER_CUBIC; - } - else if (value == "triangles") - { + case "triangles"_sw: return EFillMethod::TRIANGLES; - } - else if (value == "trihexagon") - { + case "trihexagon"_sw: return EFillMethod::TRIHEXAGON; - } - else if (value == "concentric") - { + case "concentric"_sw: return EFillMethod::CONCENTRIC; - } - else if (value == "zigzag") - { + case "zigzag"_sw: return EFillMethod::ZIG_ZAG; - } - else if (value == "cross") - { + case "cross"_sw: return EFillMethod::CROSS; - } - else if (value == "cross_3d") - { + case "cross_3d"_sw: return EFillMethod::CROSS_3D; - } - else if (value == "gyroid") - { + case "gyroid"_sw: return EFillMethod::GYROID; - } - else if (value == "lightning") - { + case "lightning"_sw: return EFillMethod::LIGHTNING; - } - else if (value.rfind("PLUGIN", 0) == 0) - { + case "plugin"_sw: return EFillMethod::PLUGIN; - } - else // Default. - { + default: return EFillMethod::NONE; } } @@ -461,20 +431,20 @@ template<> EPlatformAdhesion Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "brim") + using namespace cura::utils; + switch (hash_enum(value)) { + case "skirt"_sw: + return EPlatformAdhesion::SKIRT; + case "brim"_sw: return EPlatformAdhesion::BRIM; - } - else if (value == "raft") - { + case "raft"_sw: return EPlatformAdhesion::RAFT; - } - else if (value == "none") - { + case "none"_sw: return EPlatformAdhesion::NONE; - } - else // Default. - { + case "plugin"_sw: + return EPlatformAdhesion::PLUGIN; + default: return EPlatformAdhesion::SKIRT; } } @@ -483,16 +453,18 @@ template<> ESupportType Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "everywhere") + using namespace cura::utils; + switch (hash_enum(value)) { + case "none"_sw: + return ESupportType::NONE; + case "everywhere"_sw: return ESupportType::EVERYWHERE; - } - else if (value == "buildplate") - { + case "buildplate"_sw: return ESupportType::PLATFORM_ONLY; - } - else // Default. - { + case "plugin"_sw: + return ESupportType::PLUGIN; + default: return ESupportType::NONE; } } @@ -501,16 +473,16 @@ template<> ESupportStructure Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "normal") + using namespace cura::utils; + switch (hash_enum(value)) { + case "normal"_sw: return ESupportStructure::NORMAL; - } - else if (value == "tree") - { + case "tree"_sw: return ESupportStructure::TREE; - } - else // Default. - { + case "plugin"_sw: + return ESupportStructure::PLUGIN; + default: return ESupportStructure::NORMAL; } } @@ -520,21 +492,20 @@ template<> EZSeamType Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "random") + using namespace cura::utils; + switch (hash_enum(value)) { + case "shortest"_sw: + return EZSeamType::SHORTEST; + case "random"_sw: return EZSeamType::RANDOM; - } - else if (value == "back") // It's called 'back' internally because originally this was intended to allow the user to put the seam in the back of the object where it's less - // visible. - { + case "back"_sw: return EZSeamType::USER_SPECIFIED; - } - else if (value == "sharpest_corner") - { + case "sharpest_corner"_sw: return EZSeamType::SHARPEST_CORNER; - } - else // Default. - { + case "plugin"_sw: + return EZSeamType::PLUGIN; + default: return EZSeamType::SHORTEST; } } @@ -543,24 +514,22 @@ template<> EZSeamCornerPrefType Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "z_seam_corner_inner") + using namespace cura::utils; + switch (hash_enum(value)) { + case "z_seam_corner_none"_sw: + return EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE; + case "z_seam_corner_inner"_sw: return EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER; - } - else if (value == "z_seam_corner_outer") - { + case "z_seam_corner_outer"_sw: return EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER; - } - else if (value == "z_seam_corner_any") - { + case "z_seam_corner_any"_sw: return EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY; - } - else if (value == "z_seam_corner_weighted") - { + case "z_seam_corner_weighted"_sw: return EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED; - } - else // Default. - { + case "plugin"_sw: + return EZSeamCornerPrefType::PLUGIN; + default: return EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE; } } @@ -569,16 +538,18 @@ template<> ESurfaceMode Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "surface") + using namespace cura::utils; + switch (hash_enum(value)) { + case "normal"_sw: + return ESurfaceMode::NORMAL; + case "surface"_sw: return ESurfaceMode::SURFACE; - } - else if (value == "both") - { + case "both"_sw: return ESurfaceMode::BOTH; - } - else // Default. - { + case "plugin"_sw: + return ESurfaceMode::PLUGIN; + default: return ESurfaceMode::NORMAL; } } @@ -586,12 +557,17 @@ ESurfaceMode Settings::get(const std::string& key) const template<> FillPerimeterGapMode Settings::get(const std::string& key) const { - if (get(key) == "everywhere") + const std::string& value = get(key); + using namespace cura::utils; + switch (hash_enum(value)) { + case "nowhere"_sw: + return FillPerimeterGapMode::NOWHERE; + case "everywhere"_sw: return FillPerimeterGapMode::EVERYWHERE; - } - else // Default. - { + case "plugin"_sw: + return FillPerimeterGapMode::PLUGIN; + default: return FillPerimeterGapMode::NOWHERE; } } @@ -599,12 +575,17 @@ FillPerimeterGapMode Settings::get(const std::string& key) template<> BuildPlateShape Settings::get(const std::string& key) const { - if (get(key) == "elliptic") + const std::string& value = get(key); + using namespace cura::utils; + switch (hash_enum(value)) { + case "rectangular"_sw: + return BuildPlateShape::RECTANGULAR; + case "elliptic"_sw: return BuildPlateShape::ELLIPTIC; - } - else // Default. - { + case "plugin"_sw: + return BuildPlateShape::PLUGIN; + default: return BuildPlateShape::RECTANGULAR; } } @@ -613,24 +594,22 @@ template<> CombingMode Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "off") + using namespace cura::utils; + switch (hash_enum(value)) { + case "all"_sw: + return CombingMode::ALL; + case "off"_sw: return CombingMode::OFF; - } - else if (value == "noskin") - { + case "noskin"_sw: return CombingMode::NO_SKIN; - } - else if (value == "no_outer_surfaces") - { + case "no_outer_surfaces"_sw: return CombingMode::NO_OUTER_SURFACES; - } - else if (value == "infill") - { + case "infill"_sw: return CombingMode::INFILL; - } - else // Default. - { + case "plugin"_sw: + return CombingMode::PLUGIN; + default: return CombingMode::ALL; } } @@ -638,12 +617,17 @@ CombingMode Settings::get(const std::string& key) const template<> SupportDistPriority Settings::get(const std::string& key) const { - if (get(key) == "z_overrides_xy") + const std::string& value = get(key); + using namespace cura::utils; + switch (hash_enum(value)) { + case "xy_overrides_z"_sw: + return SupportDistPriority::XY_OVERRIDES_Z; + case "z_overrides_xy"_sw: return SupportDistPriority::Z_OVERRIDES_XY; - } - else // Default. - { + case "plugin"_sw: + return SupportDistPriority::PLUGIN; + default: return SupportDistPriority::XY_OVERRIDES_Z; } } @@ -652,16 +636,18 @@ template<> SlicingTolerance Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "inclusive") + using namespace cura::utils; + switch (hash_enum(value)) { + case "middle"_sw: + return SlicingTolerance::MIDDLE; + case "inclusive"_sw: return SlicingTolerance::INCLUSIVE; - } - else if (value == "exclusive") - { + case "exclusive"_sw: return SlicingTolerance::EXCLUSIVE; - } - else // Default. - { + case "plugin"_sw: + return SlicingTolerance::PLUGIN; + default: return SlicingTolerance::MIDDLE; } } @@ -670,12 +656,16 @@ template<> InsetDirection Settings::get(const std::string& key) const { const std::string& value = get(key); - if (value == "outside_in") + using namespace cura::utils; + switch (hash_enum(value)) { + case "inside_out"_sw: + return InsetDirection::INSIDE_OUT; + case "outside_in"_sw: return InsetDirection::OUTSIDE_IN; - } - else // Default. - { + case "plugin"_sw: + return InsetDirection::PLUGIN; + default: return InsetDirection::INSIDE_OUT; } } From 79b30cbae5fa46d41a44da096fa67b7e4824d0f1 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jul 2023 18:10:22 +0200 Subject: [PATCH 250/656] Start of component based system for building slots/plugins. The end-goal of this is to ensure each plugin can have any combination of Generate/Modify/Broadcast. For now, just (partially) carve out the Modifier functionality out of the PluginProxy. part of CURA-10851 --- include/plugins/pluginproxy.h | 187 ++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 63 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 25ac9ac433..d090fa4e52 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -35,6 +35,125 @@ namespace cura::plugins { +template +concept plugin_modifier_v = requires(T value) +{ + requires std::is_member_function_pointer_v; +}; + +template +concept default_modifier_v = requires(T value) +{ + requires ! plugin_modifier_v; + requires std::is_member_function_pointer_v; +}; + +template +class PluginProxyModifyComponent +{ + using value_type = typename ResponseTp::native_value_type; + +public: + PluginProxyModifyComponent(Parent& parent); + value_type modify(auto&&... args); +}; + +template +class PluginProxyModifyComponent +{ + using value_type = typename ResponseTp::native_value_type; +public: + PluginProxyModifyComponent(Parent& parent) + { + } + + value_type modify(auto&&... args) + { + return Default(std::forward(args)...); + } +}; + +template +class PluginProxyModifyComponent +{ + using value_type = typename ResponseTp::native_value_type; + using rsp_msg_type = typename ResponseTp::value_type; + using modify_stub_t = Stub; + +public: + PluginProxyModifyComponent(Parent& parent) : parent_(parent) + { + } + + /** + * @brief Executes to plugin Modify operation. + * + * As part of this operation, a request is sent to the plugin + * and the returned response is processed. + * + * @tparam Args - argument types for the plugin request + * @param args - arguments for the plugin request + * @return The converted response value from plugin. + * + * @throws std::runtime_error if communication with the plugin fails. + */ + value_type modify(auto&&... args) + { + agrpc::GrpcContext grpc_context; + value_type ret_value{}; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &ret_value, &args...]() + { + return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (parent_.plugin_info_.has_value()) + { + throw exceptions::RemoteException(parent_.slot_info_, parent_.plugin_info_.value(), status.error_message()); + } + throw exceptions::RemoteException(parent_.slot_info_, status.error_message()); + } + return ret_value; + } + +private: + /** + * @brief Executes the modifyCall operation with the plugin. + * + * Sends a request to the plugin and saves the response. + * + * @param grpc_context - The gRPC context to use for the call + * @param status - Status of the gRPC call which gets updated in this method + * @param ret_value - Reference to the value in which response to be stored + * @param args - Request arguments + * @return A boost::asio::awaitable indicating completion of the operation + */ + boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) + { + using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + parent_.prep_client_context(client_context); + + // Construct request + auto request{ parent_.req_(std::forward(args)...) }; + + // Make unary request + rsp_msg_type response; + status = co_await RPC::request(grpc_context, parent_.modify_stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = parent_.rsp_(response); + co_return; + } + + Parent& parent_; +}; + /** * @brief A plugin proxy class template. * @@ -51,6 +170,7 @@ namespace cura::plugins template class PluginProxy { + friend PluginProxyModifyComponent; public: // type aliases for easy use using value_type = typename ResponseTp::native_value_type; @@ -109,7 +229,7 @@ class PluginProxy if (valid_) { spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); - if (! plugin_info.broadcast_subscriptions.empty()) + if (!plugin_info.broadcast_subscriptions.empty()) { spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); } @@ -122,7 +242,7 @@ class PluginProxy { throw exceptions::RemoteException(slot_info_, status.error_message()); } - if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) + if (! plugin_info.plugin_name.empty() && !plugin_info.slot_version.empty()) { plugin_info_ = plugin_info; } @@ -156,42 +276,10 @@ class PluginProxy } ~PluginProxy() = default; - /** - * @brief Executes to plugin Modify operation. - * - * As part of this operation, a request is sent to the plugin - * and the returned response is processed. - * - * @tparam Args - argument types for the plugin request - * @param args - arguments for the plugin request - * @return The converted response value from plugin. - * - * @throws std::runtime_error if communication with the plugin fails. - */ value_type modify(auto&&... args) { - agrpc::GrpcContext grpc_context; - value_type ret_value{}; - grpc::Status status; - - boost::asio::co_spawn( - grpc_context, - [this, &grpc_context, &status, &ret_value, &args...]() - { - return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); - }, - boost::asio::detached); - grpc_context.run(); - - if (! status.ok()) // TODO: handle different kind of status codes - { - if (plugin_info_.has_value()) - { - throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); - } - throw exceptions::RemoteException(slot_info_, status.error_message()); - } - return ret_value; + static auto modify_component = PluginProxyModifyComponent(*this); + return modify_component.modify(std::forward(args)...); } template @@ -236,33 +324,6 @@ class PluginProxy .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake - /** - * @brief Executes the modifyCall operation with the plugin. - * - * Sends a request to the plugin and saves the response. - * - * @param grpc_context - The gRPC context to use for the call - * @param status - Status of the gRPC call which gets updated in this method - * @param ret_value - Reference to the value in which response to be stored - * @param args - Request arguments - * @return A boost::asio::awaitable indicating completion of the operation - */ - boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) - { - using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context); - - // Construct request - auto request{ req_(std::forward(args)...) }; - - // Make unary request - rsp_msg_type response; - status = co_await RPC::request(grpc_context, modify_stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = rsp_(response); - co_return; - } - template boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) { From 991662c684596c3bcddb81dafd4d11c66bc745ea Mon Sep 17 00:00:00 2001 From: rburema Date: Thu, 27 Jul 2023 16:11:33 +0000 Subject: [PATCH 251/656] Applied clang-format. --- include/plugins/slots.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 3b4fab22cd..72cbdb6898 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -102,7 +102,11 @@ class Registry, Unit> : public Registry void append_to_connect_map(slot_to_connect_map_t& function_map) { - function_map.insert({ T::slot_id, [&](std::shared_ptr plugin) { this->connect(plugin); } }); + function_map.insert({ T::slot_id, + [&](std::shared_ptr plugin) + { + this->connect(plugin); + } }); } template From c40dd58cfab4e777f99b064241208ed550bbd8ff Mon Sep 17 00:00:00 2001 From: rburema Date: Thu, 27 Jul 2023 16:11:56 +0000 Subject: [PATCH 252/656] Applied clang-format. --- include/plugins/pluginproxy.h | 9 ++++++--- include/plugins/slots.h | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index d090fa4e52..de3c9d8af6 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -62,6 +62,7 @@ template { using value_type = typename ResponseTp::native_value_type; + public: PluginProxyModifyComponent(Parent& parent) { @@ -81,7 +82,8 @@ class PluginProxyModifyComponent using modify_stub_t = Stub; public: - PluginProxyModifyComponent(Parent& parent) : parent_(parent) + PluginProxyModifyComponent(Parent& parent) + : parent_(parent) { } @@ -171,6 +173,7 @@ template, Unit> : public Registry void append_to_connect_map(slot_to_connect_map_t& function_map) { - function_map.insert({ T::slot_id, [&](std::shared_ptr plugin) { this->connect(plugin); } }); + function_map.insert({ T::slot_id, + [&](std::shared_ptr plugin) + { + this->connect(plugin); + } }); } template From 6ece234ff37378283a8e33d2c292955af403f0c1 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jul 2023 18:47:46 +0200 Subject: [PATCH 253/656] Small fixes. - The statement with is_same would not compile on linux, try this way. - The (slot) map wasn't filled, since no recursion happened in the recursive tempated (Registry) type(s). part of CURA-10805 --- include/plugins/slotproxy.h | 2 +- include/plugins/slots.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 6b45563ee6..015d2b061e 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -81,7 +81,7 @@ class SlotProxy */ constexpr auto modify(auto&&... args) { - if (plugin_.has_value() && ! std::is_same{}) + if (plugin_.has_value() && ! std::is_same_v) { return plugin_.value().modify(std::forward(args)...); } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 72cbdb6898..700b7c3989 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -81,7 +81,7 @@ template class Unit> class Registry, Unit> { public: - void append_to_connect_map(std::map)>>& function_map) + constexpr void append_to_connect_map(slot_to_connect_map_t& function_map) { } // Base case, do nothing. @@ -100,13 +100,14 @@ class Registry, Unit> : public Registry using Base::broadcast; friend Base; - void append_to_connect_map(slot_to_connect_map_t& function_map) + constexpr void append_to_connect_map(slot_to_connect_map_t& function_map) { function_map.insert({ T::slot_id, [&](std::shared_ptr plugin) { - this->connect(plugin); + connect(plugin); } }); + Base::append_to_connect_map(function_map); } template From 34d64ec746781bc04e3b209ea36ea19fcaccbe4b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 28 Jul 2023 08:00:17 +0200 Subject: [PATCH 254/656] Add tests for the new string_view switch setting retrieval CURA-10720 --- tests/settings/SettingsTest.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/settings/SettingsTest.cpp b/tests/settings/SettingsTest.cpp index a59acbda1b..f0c727b246 100644 --- a/tests/settings/SettingsTest.cpp +++ b/tests/settings/SettingsTest.cpp @@ -1,10 +1,12 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "settings/Settings.h" //The class under test. + #include "Application.h" //To test extruder train settings. #include "ExtruderTrain.h" #include "Slice.h" +#include "settings/EnumSettings.h" #include "settings/FlowTempGraph.h" #include "settings/types/Angle.h" #include "settings/types/Duration.h" @@ -14,6 +16,7 @@ #include "settings/types/Velocity.h" #include "utils/Coord_t.h" #include "utils/FMatrix4x3.h" //Testing matrix transformation settings. + #include //For M_PI. #include #include //For shared_ptr. @@ -250,5 +253,17 @@ TEST_F(SettingsTest, LimitToExtruder) EXPECT_EQ(limit_extruder_value, settings.get("test_setting")); } +TEST_F(SettingsTest, PluginExtendedEnum) +{ + settings.add("infill_type", "PLUGIN::plugin_1::MOZAIC"); + EXPECT_EQ(settings.get("infill_type"), EFillMethod::PLUGIN); +} + +TEST_F(SettingsTest, EnumStringSwitch) +{ + settings.add("infill_type", "lightning"); + EXPECT_EQ(settings.get("infill_type"), EFillMethod::LIGHTNING); +} + } // namespace cura // NOLINTEND(*-magic-numbers) From a0c33afaef92f61b372e6b870ab4f6f05da28141 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 28 Jul 2023 09:11:36 +0200 Subject: [PATCH 255/656] Should fix it for non-MSVC compilers. done as part of CURA-10805 --- include/plugins/slotproxy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 015d2b061e..d1a7c51907 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -81,7 +81,7 @@ class SlotProxy */ constexpr auto modify(auto&&... args) { - if (plugin_.has_value() && ! std::is_same_v) + if (plugin_.has_value() && ! std::is_same_v) { return plugin_.value().modify(std::forward(args)...); } From af234bd9b0b2a07e3b5e67db4939e43aaf53d4be Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 28 Jul 2023 10:54:17 +0200 Subject: [PATCH 256/656] uniform keyword ordering CURA-10720 --- include/utils/types/string_switch.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/types/string_switch.h b/include/utils/types/string_switch.h index e7d9689aac..7b520858ae 100644 --- a/include/utils/types/string_switch.h +++ b/include/utils/types/string_switch.h @@ -10,7 +10,7 @@ namespace cura::utils { // Source: https://learnmoderncpp.com/2020/06/01/strings-as-switch-case-labels/ -inline constexpr uint64_t hash_djb2a(const std::string_view value) +constexpr inline uint64_t hash_djb2a(const std::string_view value) { uint64_t hash{ 5381 }; for (unsigned char c : value) @@ -20,7 +20,7 @@ inline constexpr uint64_t hash_djb2a(const std::string_view value) return hash; } -inline constexpr uint64_t hash_enum(const std::string_view value) +constexpr inline uint64_t hash_enum(const std::string_view value) { constexpr uint64_t plugin_namespace_sep_location{ 6 }; if (value.size() > plugin_namespace_sep_location && value.at(plugin_namespace_sep_location) == ':') From ec5e17f58d88b2bc3c2ebf9ee25c6049e162d91d Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 28 Jul 2023 11:51:25 +0200 Subject: [PATCH 257/656] Remove the previous way of handling slots without modify. One one hand, this was arguably a hack, since it sort-of depended on the compiler throwing away the call to (plugin-proxy) modify, which, if not done, would eventually have lead to a compiler-error (in the situation before the plugin-proxy had a separated-out modify component at least), since NoStub doesn't have the requisite types associated with it to go through with the entire modify call-stack. On the other, the whole modify-component (in the PluginProxy file) despite not being a hack, has its own problems at the moment (overreliance on 'parent' class breaking modularization) and while perhaps more 'correct' does feel like a more cumbersome solution in way. On the up side, this does give a bit more flexibility for coming up with different 'defaults' under different conditions. For when the behaviour should be slightly different when there is a plugin that doesn't implement one of the methods that is called versus no plugin being subscribed to that slot. part of CURA-10851 --- include/plugins/slotproxy.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 6b45563ee6..8d6f818009 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -21,11 +21,6 @@ namespace cura::plugins { -// (Will be) Used to make/identify slots with no modify (or generate) methods. -class NoStub -{ -}; - /** * @brief A class template representing a proxy for a plugin slot. * @@ -81,7 +76,7 @@ class SlotProxy */ constexpr auto modify(auto&&... args) { - if (plugin_.has_value() && ! std::is_same{}) + if (plugin_.has_value()) { return plugin_.value().modify(std::forward(args)...); } From 4b88ec475dca17d2eb3b206bdee97b0258482603 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 31 Jul 2023 11:39:58 +0200 Subject: [PATCH 258/656] Moved SlotConnectionFact Refactored the "SlotConnectionFactory_" class in the slots.h file to utilize "std::move" in order to improve performance by minimizing unnecessary copies. Also rearranged the order of code for better clarity and maintenance. This will make understanding and working with the code easier for future developers. --- include/plugins/slots.h | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 700b7c3989..e682f55e78 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -18,6 +18,7 @@ #include #include +#include namespace cura { @@ -179,31 +180,23 @@ struct Holder T proxy; }; -} // namespace details - -using slot_simplify = details::slot_simplify_; -using slot_postprocess = details::slot_postprocess_<>; -using slot_settings_broadcast = details::slot_settings_broadcast_<>; - -using SlotTypes = details::Typelist; - template -class SlotConnectionFactory_ +class SlotConnectionFactory { public: - static SlotConnectionFactory_& instance() + static SlotConnectionFactory& instance() { - static SlotConnectionFactory_ instance; + static SlotConnectionFactory instance; return instance; } void connect(const plugins::v0::SlotID& slot_id, std::shared_ptr plugin) { - slot_to_connect_map[slot_id](plugin); + slot_to_connect_map[slot_id](std::move(plugin)); } private: - SlotConnectionFactory_() + SlotConnectionFactory() { S::instance().append_to_connect_map(slot_to_connect_map); } @@ -211,9 +204,17 @@ class SlotConnectionFactory_ plugins::details::slot_to_connect_map_t slot_to_connect_map; }; +} // namespace details + +using slot_simplify = details::slot_simplify_; +using slot_postprocess = details::slot_postprocess_<>; +using slot_settings_broadcast = details::slot_settings_broadcast_<>; + +using SlotTypes = details::Typelist; + } // namespace plugins using slots = plugins::details::SingletonRegistry; -using SlotConnectionFactory = plugins::SlotConnectionFactory_; +using SlotConnectionFactory = plugins::details::SlotConnectionFactory; } // namespace cura From 233abae7888116663e425cfd6879c6ce47d9715d Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 31 Jul 2023 14:02:58 +0200 Subject: [PATCH 259/656] Update grpc definitions possible broken commit CURA-10619 --- conanfile.py | 2 +- include/plugins/converters.h | 22 ++++++++++++++-------- include/plugins/slots.h | 9 +++++---- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/conanfile.py b/conanfile.py index db6341e9d0..22e5dbf6b7 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10619") def generate(self): deps = CMakeDeps(self) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 5260d33731..7b6ec71e42 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -9,10 +9,10 @@ #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.pb.h" -#include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" -#include "cura/plugins/slots/postprocess/v0/postprocess.pb.h" -#include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" -#include "cura/plugins/slots/simplify/v0/simplify.pb.h" +#include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" +#include "cura/plugins/slots/postprocess/v0/modify.pb.h" +#include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" +#include "cura/plugins/slots/simplify/v0/modify.pb.h" #include "plugins/metadata.h" #include "plugins/types.h" @@ -140,7 +140,7 @@ struct handshake_response struct simplify_request { - using value_type = slots::simplify::v0::CallRequest; ///< The protobuf message type. + using value_type = slots::simplify::v0::modify::CallRequest; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -198,7 +198,7 @@ struct simplify_request */ struct simplify_response { - using value_type = slots::simplify::v0::CallResponse; ///< The protobuf message type. + using value_type = slots::simplify::v0::modify::CallResponse; ///< The protobuf message type. using native_value_type = Polygons; ///< The native value type. /** @@ -236,7 +236,7 @@ struct simplify_response struct postprocess_request { - using value_type = slots::postprocess::v0::CallRequest; ///< The protobuf message type. + using value_type = slots::postprocess::v0::modify::CallRequest; ///< The protobuf message type. using native_value_type = std::string; ///< The native value type. /** @@ -255,7 +255,7 @@ struct postprocess_request struct postprocess_response { - using value_type = slots::postprocess::v0::CallResponse; + using value_type = slots::postprocess::v0::modify::CallResponse; using native_value_type = std::string; native_value_type operator()(const value_type& message) const @@ -264,6 +264,12 @@ struct postprocess_response } }; +//struct infill_generate_request +//{ +// using value_type = slots::infill::v0::generate::CallRequest +// using native_value_type = Polygons +//} + } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 0117ccc7c0..38fa880b9d 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -4,9 +4,10 @@ #ifndef PLUGINS_SLOTS_H #define PLUGINS_SLOTS_H -#include "cura/plugins/slots/postprocess/v0/postprocess.grpc.pb.h" -#include "cura/plugins/slots/simplify/v0/simplify.grpc.pb.h" +#include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" +#include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" +#include "infill.h" #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" @@ -49,7 +50,7 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy; +using slot_simplify_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -60,7 +61,7 @@ using slot_simplify_ = SlotProxy using slot_postprocess_ - = SlotProxy; + = SlotProxy; template struct Typelist From 965030b1a8751686d0c5dbc739319c52619137c0 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Mon, 31 Jul 2023 12:03:46 +0000 Subject: [PATCH 260/656] Applied clang-format. --- include/plugins/converters.h | 8 ++++---- include/plugins/slots.h | 13 ++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 7b6ec71e42..05945ab49d 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -264,11 +264,11 @@ struct postprocess_response } }; -//struct infill_generate_request +// struct infill_generate_request //{ -// using value_type = slots::infill::v0::generate::CallRequest -// using native_value_type = Polygons -//} +// using value_type = slots::infill::v0::generate::CallRequest +// using native_value_type = Polygons +// } } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 38fa880b9d..935f8fff48 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -50,7 +50,8 @@ struct simplify_default * @tparam Default The default behavior when no plugin is registered. */ template -using slot_simplify_ = SlotProxy; +using slot_simplify_ + = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -60,8 +61,14 @@ using slot_simplify_ = SlotProxy -using slot_postprocess_ - = SlotProxy; +using slot_postprocess_ = SlotProxy< + v0::SlotID::POSTPROCESS_MODIFY, + "<=1.0.0", + slots::postprocess::v0::modify::PostprocessModifyService::Stub, + Validator, + postprocess_request, + postprocess_response, + Default>; template struct Typelist From 9a65b9778ebecb22b7211160426311fa3ea80fb8 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 31 Jul 2023 14:16:53 +0200 Subject: [PATCH 261/656] Add generate method In this change, all instances of the "modify" method in slots.h, pluginproxy.h, and slotproxy.h have been renamed to "invoke". The aim of this refactoring is to improve the method semantics to more accurately describe the implementation, which is to invoke plugin directives rather than modify them. An additional "generate" method has also been created in slots.h for additional functionality. Contributes to CURA-10619 --- include/plugins/pluginproxy.h | 2 +- include/plugins/slotproxy.h | 4 ++-- include/plugins/slots.h | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 25ac9ac433..4fb0865ab1 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -168,7 +168,7 @@ class PluginProxy * * @throws std::runtime_error if communication with the plugin fails. */ - value_type modify(auto&&... args) + value_type invoke(auto&&... args) { agrpc::GrpcContext grpc_context; value_type ret_value{}; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index d1a7c51907..f53aab16a9 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -79,11 +79,11 @@ class SlotProxy * @param args The arguments for the plugin request. * @return The result of the plugin request or the default behavior. */ - constexpr auto modify(auto&&... args) + constexpr auto invoke(auto&&... args) { if (plugin_.has_value() && ! std::is_same_v) { - return plugin_.value().modify(std::forward(args)...); + return plugin_.value().invoke(std::forward(args)...); } return std::invoke(default_process, std::forward(args)...); } diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 6e12cd664a..36ee93e6f8 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -128,7 +128,13 @@ class Registry, Unit> : public Registry template constexpr auto modify(auto&&... args) { - return get().modify(std::forward(args)...); + return get().invoke(std::forward(args)...); + } + + template + constexpr auto generate(auto&&... args) + { + return get().invoke(std::forward(args)...); } template From 70da939883c756ae6fa4dadd928a87e5c34c9cb9 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 31 Jul 2023 12:17:37 +0000 Subject: [PATCH 262/656] Applied clang-format. --- include/plugins/slots.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 36ee93e6f8..d73140026e 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -4,9 +4,9 @@ #ifndef PLUGINS_SLOTS_H #define PLUGINS_SLOTS_H +#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" -#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" #include "infill.h" #include "plugins/converters.h" From 4da9cd1943e1a793b2d45d3d644d358213023a04 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 31 Jul 2023 15:20:46 +0200 Subject: [PATCH 263/656] Merge branch 'CURA-10619-infill-generate-slot' of /Users/c.lamboo/CuraEngineII with conflicts. --- include/TreeSupportUtils.h | 6 ++-- include/infill.h | 34 +++++++++++++++----- include/plugins/slots.h | 12 +++++++ src/FffGcodeWriter.cpp | 65 ++++++++++++++++++++++---------------- src/TopSurface.cpp | 4 +-- src/infill.cpp | 29 ++++++++--------- src/utils/polygonUtils.cpp | 4 +-- 7 files changed, 97 insertions(+), 57 deletions(-) diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index 2f6378dc20..e6b2d9577f 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -139,6 +139,7 @@ class TreeSupportUtils support_shift, config.maximum_resolution, config.maximum_deviation, + config.settings, wall_line_count, narrow_area_width, infill_origin, @@ -148,12 +149,13 @@ class TreeSupportUtils use_endpieces, skip_some_zags, zag_skip_count, - pocket_size + pocket_size, + cross_fill_provider ); Polygons areas; Polygons lines; - roof_computation.generate(toolpaths, areas, lines, config.settings, layer_idx, SectionType::SUPPORT, cross_fill_provider); + roof_computation.generate(toolpaths, areas, lines, layer_idx, SectionType::SUPPORT); lines.add(toPolylines(areas)); lines.add(toPolylines(toolpaths)); return lines; diff --git a/include/infill.h b/include/infill.h index c6ce9ce4cd..d3e823cb8e 100644 --- a/include/infill.h +++ b/include/infill.h @@ -7,8 +7,8 @@ #include "infill/LightningGenerator.h" #include "infill/ZigzagConnectorProcessor.h" #include "settings/EnumSettings.h" //For infill types. -#include "settings/types/Angle.h" #include "settings/Settings.h" +#include "settings/types/Angle.h" #include "utils/ExtrusionLine.h" #include "utils/IntPoint.h" #include "utils/section_type.h" @@ -20,9 +20,15 @@ class AABB; class SierpinskiFillProvider; class SliceMeshStorage; +//namespace plugins::details +//{ +// struct infill_default; +//} + class Infill { friend class InfillTest; +// friend class plugins::details::infill_default; EFillMethod pattern; //!< the space filling pattern of the infill to generate bool zig_zaggify; //!< Whether to connect the end pieces of the support lines via the wall @@ -51,6 +57,11 @@ class Infill coord_t pocket_size; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern bool mirror_offset; //!< Indication in which offset direction the extra infill lines are made + const Settings& settings; + const SierpinskiFillProvider* cross_fill_provider = nullptr; + const LightningLayer * lightning_layer = nullptr; + const SliceMeshStorage* mesh = nullptr; + static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0) public: Infill(EFillMethod pattern @@ -66,6 +77,7 @@ class Infill , coord_t shift , coord_t max_resolution , coord_t max_deviation + , const Settings& settings , size_t wall_line_count = 0 , coord_t small_area_width = 0 , const Point& infill_origin = Point() @@ -76,6 +88,9 @@ class Infill , bool skip_some_zags = false , size_t zag_skip_count = 0 , coord_t pocket_size = 0 + , const SierpinskiFillProvider* cross_fill_provider = nullptr + , const LightningLayer* lightning_layer = nullptr + , const SliceMeshStorage* mesh = nullptr ) : pattern(pattern) , zig_zaggify(zig_zaggify) @@ -101,6 +116,10 @@ class Infill , zag_skip_count(zag_skip_count) , pocket_size(pocket_size) , mirror_offset(zig_zaggify) + , settings(settings) + , cross_fill_provider(cross_fill_provider) + , lightning_layer(lightning_layer) + , mesh(mesh) { //TODO: The connected lines algorithm is only available for linear-based infill, for now. //We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. @@ -119,7 +138,7 @@ class Infill * \param mesh A mesh for which to generate infill (should only be used for non-helper-mesh objects). * \param[in] cross_fill_provider The cross fractal subdivision decision functor */ - void generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, int layer_idx, SectionType section_type, const SierpinskiFillProvider* cross_fill_provider = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); + void generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, int layer_idx, SectionType section_type); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -138,16 +157,15 @@ class Infill /*! * Generate the infill pattern without the infill_multiplier functionality */ - void _generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, const SierpinskiFillProvider* cross_fill_pattern = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); - + void _generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines); /*! * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. - * + * * This is done in a way such that there is not overlap between the lines * except the middle original one if the multiplier is odd. - * + * * This introduces a lot of line segments. - * + * * \param[in,out] result_polygons The polygons to be multiplied (input and output) * \param[in,out] result_lines The lines to be multiplied (input and output) */ @@ -266,7 +284,7 @@ class Infill * \param toolpaths (output) The resulting toolpaths. Binned by inset_idx. * \param inset_value The offset between each consecutive two polygons */ - void generateConcentricInfill(std::vector& toolpaths, const Settings& settings); + void generateConcentricInfill(std::vector& toolpaths); /*! * Generate a rectangular grid of infill lines diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 36ee93e6f8..9157d728b4 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -44,6 +44,14 @@ struct simplify_default } }; +//struct infill_generate_default +//{ +// auto operator()(Infill& infill, auto&&... args) +// { +// return infill._generate(std::forward(args)...); +// } +//}; + /** * @brief Alias for the Simplify slot. * @@ -55,6 +63,9 @@ template using slot_simplify_ = SlotProxy; +//template +//using slot_infill_generate_ = SlotProxy; + /** * @brief Alias for the Postprocess slot. * @@ -223,6 +234,7 @@ class SlotConnectionFactory using slot_simplify = details::slot_simplify_; using slot_postprocess = details::slot_postprocess_<>; using slot_settings_broadcast = details::slot_settings_broadcast_<>; +//using slot_infill_generate = details::slot_infill_generate_; using SlotTypes = details::Typelist; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9586059a53..64fdc416b6 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -639,6 +639,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) extra_infill_shift, max_resolution, max_deviation, + base_settings, wall_line_count, small_area_width, infill_origin, @@ -650,7 +651,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) zag_skip_count, pocket_size); std::vector raft_paths; - infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); + infill_comp.generate(raft_paths, raft_polygons, raftLines, layer_nr, SectionType::ADHESION); if (! raft_paths.empty()) { const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; @@ -765,6 +766,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) extra_infill_shift, interface_max_resolution, interface_max_deviation, + interface_settings, wall_line_count, small_area_width, infill_origin, @@ -776,7 +778,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) zag_skip_count, pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. - infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); + infill_comp.generate(raft_paths, raft_polygons, raft_lines, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); raft_polygons.clear(); @@ -870,6 +872,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) extra_infill_shift, surface_max_resolution, surface_max_deviation, + surface_settings, wall_line_count, small_area_width, infill_origin, @@ -881,7 +884,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) zag_skip_count, pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. - infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); + infill_comp.generate(raft_paths, raft_polygons, raft_lines, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); raft_polygons.clear(); @@ -1629,6 +1632,7 @@ bool FffGcodeWriter::processMultiLayerInfill( infill_shift, max_resolution, max_deviation, + mesh.settings, wall_line_count, small_area_width, infill_origin, @@ -1638,17 +1642,17 @@ bool FffGcodeWriter::processMultiLayerInfill( use_endpieces, skip_some_zags, zag_skip_count, - mesh.settings.get("cross_infill_pocket_size")); + mesh.settings.get("cross_infill_pocket_size"), + mesh.cross_fill_provider, + lightning_layer, + &mesh + ); infill_comp.generate( infill_paths, infill_polygons, infill_lines, - mesh.settings, gcode_layer.getLayerNr(), - SectionType::INFILL, - mesh.cross_fill_provider, - lightning_layer, - &mesh); + SectionType::INFILL); } if (! infill_lines.empty() || ! infill_polygons.empty()) { @@ -1835,6 +1839,7 @@ bool FffGcodeWriter::processSingleLayerInfill( infill_shift, max_resolution, max_deviation, + mesh.settings, skin_below_wall_count, small_area_width, infill_origin, @@ -1844,17 +1849,16 @@ bool FffGcodeWriter::processSingleLayerInfill( use_endpieces, skip_some_zags, zag_skip_count, - pocket_size); + pocket_size, + mesh.cross_fill_provider, + lightning_layer, + &mesh); infill_comp.generate( wall_tool_paths.back(), infill_polygons, infill_lines, - mesh.settings, gcode_layer.getLayerNr(), - SectionType::INFILL, - mesh.cross_fill_provider, - lightning_layer, - &mesh); + SectionType::INFILL); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, min_skin_below_wall_count); @@ -1900,6 +1904,7 @@ bool FffGcodeWriter::processSingleLayerInfill( infill_shift, max_resolution, max_deviation, + mesh.settings, wall_line_count_here, small_area_width, infill_origin, @@ -1909,17 +1914,18 @@ bool FffGcodeWriter::processSingleLayerInfill( use_endpieces, skip_some_zags, zag_skip_count, - pocket_size); + pocket_size, + mesh.cross_fill_provider, + lightning_layer, + &mesh + ); infill_comp.generate( wall_tool_paths.back(), infill_polygons, infill_lines, - mesh.settings, gcode_layer.getLayerNr(), - SectionType::INFILL, - mesh.cross_fill_provider, - lightning_layer, - &mesh); + SectionType::INFILL + ); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, wall_line_count); @@ -2761,6 +2767,7 @@ void FffGcodeWriter::processSkinPrintFeature( extra_infill_shift, max_resolution, max_deviation, + mesh.settings, wall_line_count, small_area_width, infill_origin, @@ -2771,7 +2778,7 @@ void FffGcodeWriter::processSkinPrintFeature( skip_some_zags, zag_skip_count, pocket_size); - infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN); + infill_comp.generate(skin_paths, skin_polygons, skin_lines, gcode_layer.getLayerNr(), SectionType::SKIN); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) @@ -3113,6 +3120,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer support_shift, max_resolution, max_deviation, + infill_extruder.settings, wall_count, small_area_width, infill_origin, @@ -3122,15 +3130,14 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer use_endpieces, skip_some_zags, zag_skip_count, - pocket_size); + pocket_size, + storage.support.cross_fill_provider); infill_comp.generate( wall_toolpaths_here, support_polygons, support_lines, - infill_extruder.settings, gcode_layer.getLayerNr(), - SectionType::SUPPORT, - storage.support.cross_fill_provider); + SectionType::SUPPORT); } if (need_travel_to_end_of_last_spiral && infill_extruder.settings.get("magic_spiralize")) @@ -3314,6 +3321,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay extra_infill_shift, max_resolution, max_deviation, + roof_extruder.settings, wall_line_count, small_area_width, infill_origin, @@ -3327,7 +3335,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay Polygons roof_polygons; std::vector roof_paths; Polygons roof_lines; - roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); + roof_computation.generate(roof_paths, roof_polygons, roof_lines, gcode_layer.getLayerNr(), SectionType::SUPPORT); if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) { return false; // We didn't create any support roof. @@ -3432,6 +3440,7 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L extra_infill_shift, max_resolution, max_deviation, + bottom_extruder.settings, wall_line_count, small_area_width, infill_origin, @@ -3445,7 +3454,7 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L Polygons bottom_polygons; std::vector bottom_paths; Polygons bottom_lines; - bottom_computation.generate(bottom_paths, bottom_polygons, bottom_lines, bottom_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); + bottom_computation.generate(bottom_paths, bottom_polygons, bottom_lines, gcode_layer.getLayerNr(), SectionType::SUPPORT); if (bottom_paths.empty() && bottom_polygons.empty() && bottom_lines.empty()) { return false; diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index 86d546e4ad..7c3553ef61 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -86,11 +86,11 @@ 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, small_area_width, 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 - 10, shift, max_resolution, max_deviation, mesh.settings, wall_line_count, small_area_width, infill_origin, skip_line_stitching); std::vector ironing_paths; Polygons ironing_polygons; Polygons ironing_lines; - infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, mesh.settings, layer.getLayerNr(), SectionType::IRONING); + infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, layer.getLayerNr(), SectionType::IRONING); if(ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty()) { diff --git a/src/infill.cpp b/src/infill.cpp index 26911124dc..662af51889 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -72,12 +72,8 @@ Polygons Infill::generateWallToolPaths(std::vector& toolpath void Infill::generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, - const Settings& settings, int layer_idx, - SectionType section_type, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) + SectionType section_type) { if (outer_contour.empty()) { @@ -159,7 +155,9 @@ void Infill::generate(std::vector& toolpaths, Polygons generated_result_polygons; Polygons generated_result_lines; - _generate(toolpaths, generated_result_polygons, generated_result_lines, settings, cross_fill_provider, lightning_trees, mesh); + _generate(toolpaths, generated_result_polygons, generated_result_lines); + + zig_zaggify = zig_zaggify_real; multiplyInfill(generated_result_polygons, generated_result_lines); result_polygons.add(generated_result_polygons); @@ -171,7 +169,12 @@ void Infill::generate(std::vector& toolpaths, // So make sure we provide it with a Polygons that is safe to clear and only add stuff to result_lines. Polygons generated_result_polygons; Polygons generated_result_lines; - _generate(toolpaths, generated_result_polygons, generated_result_lines, settings, cross_fill_provider, lightning_trees, mesh); + + + + _generate(toolpaths, generated_result_polygons, generated_result_lines); + + result_polygons.add(generated_result_polygons); result_lines.add(generated_result_lines); } @@ -211,11 +214,7 @@ void Infill::generate(std::vector& toolpaths, void Infill::_generate(std::vector& toolpaths, Polygons& result_polygons, - Polygons& result_lines, - const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) + Polygons& result_lines) { if (inner_contour.empty()) return; @@ -246,7 +245,7 @@ void Infill::_generate(std::vector& toolpaths, generateTrihexagonInfill(result_lines); break; case EFillMethod::CONCENTRIC: - generateConcentricInfill(toolpaths, settings); + generateConcentricInfill(toolpaths); break; case EFillMethod::ZIG_ZAG: generateZigZagInfill(result_lines, line_distance, fill_angle); @@ -273,7 +272,7 @@ void Infill::_generate(std::vector& toolpaths, break; case EFillMethod::LIGHTNING: assert(lightning_trees); // "Cannot generate Lightning infill without a generator!\n" - generateLightningInfill(lightning_trees, result_lines); + generateLightningInfill(lightning_layer, result_lines); break; default: spdlog::error("Fill pattern has unknown value.\n"); @@ -386,7 +385,7 @@ void Infill::generateLightningInfill(const LightningLayer* trees, Polygons& resu result_lines.add(trees->convertToLines(inner_contour, infill_line_width)); } -void Infill::generateConcentricInfill(std::vector& toolpaths, const Settings& settings) +void Infill::generateConcentricInfill(std::vector& toolpaths) { const coord_t min_area = infill_line_width * infill_line_width; diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 8135507834..2c02820f76 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -95,10 +95,10 @@ std::vector PolygonUtils::spreadDotsArea(const Polygons& polygons, Point { std::vector dummy_toolpaths; Settings dummy_settings; - Infill infill_gen(EFillMethod::LINES, false, false, polygons, 0, grid_size.X, 0, 1, 0, 0, 0, 0, 0); + Infill infill_gen(EFillMethod::LINES, false, false, polygons, 0, grid_size.X, 0, 1, 0, 0, 0, 0, 0, dummy_settings); Polygons result_polygons; Polygons result_lines; - infill_gen.generate(dummy_toolpaths, result_polygons, result_lines, dummy_settings, 0, SectionType::DOTS); // FIXME: @jellespijker make sure the propper layer nr is used + infill_gen.generate(dummy_toolpaths, result_polygons, result_lines, 0, SectionType::DOTS); // FIXME: @jellespijker make sure the propper layer nr is used std::vector result; for (PolygonRef line : result_lines) { From 30e27b1ca6e5cb79f94d67fdb0704d8d4acc1ee4 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 31 Jul 2023 16:59:59 +0200 Subject: [PATCH 264/656] Add infill slots to generate CURA-10619 --- include/TreeSupportUtils.h | 6 +- include/infill.h | 44 ++++++------- include/plugins/converters.h | 121 +++++++++++++++++++++++++++++++++-- include/plugins/slots.h | 23 +++---- src/FffGcodeWriter.cpp | 65 ++++++++----------- src/TopSurface.cpp | 22 ++++++- src/infill.cpp | 59 ++++++++++------- src/utils/polygonUtils.cpp | 4 +- 8 files changed, 235 insertions(+), 109 deletions(-) diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index e6b2d9577f..2f6378dc20 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -139,7 +139,6 @@ class TreeSupportUtils support_shift, config.maximum_resolution, config.maximum_deviation, - config.settings, wall_line_count, narrow_area_width, infill_origin, @@ -149,13 +148,12 @@ class TreeSupportUtils use_endpieces, skip_some_zags, zag_skip_count, - pocket_size, - cross_fill_provider + pocket_size ); Polygons areas; Polygons lines; - roof_computation.generate(toolpaths, areas, lines, layer_idx, SectionType::SUPPORT); + roof_computation.generate(toolpaths, areas, lines, config.settings, layer_idx, SectionType::SUPPORT, cross_fill_provider); lines.add(toPolylines(areas)); lines.add(toPolylines(toolpaths)); return lines; diff --git a/include/infill.h b/include/infill.h index d3e823cb8e..43d7ddf27c 100644 --- a/include/infill.h +++ b/include/infill.h @@ -7,8 +7,8 @@ #include "infill/LightningGenerator.h" #include "infill/ZigzagConnectorProcessor.h" #include "settings/EnumSettings.h" //For infill types. -#include "settings/Settings.h" #include "settings/types/Angle.h" +#include "settings/Settings.h" #include "utils/ExtrusionLine.h" #include "utils/IntPoint.h" #include "utils/section_type.h" @@ -20,15 +20,18 @@ class AABB; class SierpinskiFillProvider; class SliceMeshStorage; -//namespace plugins::details -//{ -// struct infill_default; -//} +namespace plugins { +namespace details { +struct infill_generate_default; +} +struct infill_generate_request; +} -class Infill +class Infill { friend class InfillTest; -// friend class plugins::details::infill_default; + friend class plugins::details::infill_generate_default; + friend class plugins::infill_generate_request; EFillMethod pattern; //!< the space filling pattern of the infill to generate bool zig_zaggify; //!< Whether to connect the end pieces of the support lines via the wall @@ -47,7 +50,7 @@ class Infill coord_t max_deviation; //!< Max deviation fro the original poly when enforcing max_resolution size_t wall_line_count; //!< Number of walls to generate at the boundary of the infill region, spaced \ref infill_line_width apart coord_t small_area_width; //!< Maximum width of a small infill region to be filled with walls - const Point infill_origin; //!< origin of the infill pattern + Point infill_origin; //!< origin of the infill pattern bool skip_line_stitching; //!< Whether to bypass the line stitching normally performed for polyline type infills bool fill_gaps; //!< Whether to fill gaps in strips of infill that would be too thin to fit the infill lines. If disabled, those areas are left empty. bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector @@ -57,17 +60,14 @@ class Infill coord_t pocket_size; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern bool mirror_offset; //!< Indication in which offset direction the extra infill lines are made - const Settings& settings; - const SierpinskiFillProvider* cross_fill_provider = nullptr; - const LightningLayer * lightning_layer = nullptr; - const SliceMeshStorage* mesh = nullptr; - static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0) public: + Infill() = default; + Infill(EFillMethod pattern , bool zig_zaggify , bool connect_polygons - , const Polygons& in_outline + , const Polygons in_outline , coord_t infill_line_width , coord_t line_distance , coord_t infill_overlap @@ -77,7 +77,6 @@ class Infill , coord_t shift , coord_t max_resolution , coord_t max_deviation - , const Settings& settings , size_t wall_line_count = 0 , coord_t small_area_width = 0 , const Point& infill_origin = Point() @@ -88,10 +87,7 @@ class Infill , bool skip_some_zags = false , size_t zag_skip_count = 0 , coord_t pocket_size = 0 - , const SierpinskiFillProvider* cross_fill_provider = nullptr - , const LightningLayer* lightning_layer = nullptr - , const SliceMeshStorage* mesh = nullptr - ) + ) : pattern(pattern) , zig_zaggify(zig_zaggify) , connect_polygons(connect_polygons) @@ -116,10 +112,6 @@ class Infill , zag_skip_count(zag_skip_count) , pocket_size(pocket_size) , mirror_offset(zig_zaggify) - , settings(settings) - , cross_fill_provider(cross_fill_provider) - , lightning_layer(lightning_layer) - , mesh(mesh) { //TODO: The connected lines algorithm is only available for linear-based infill, for now. //We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. @@ -138,7 +130,7 @@ class Infill * \param mesh A mesh for which to generate infill (should only be used for non-helper-mesh objects). * \param[in] cross_fill_provider The cross fractal subdivision decision functor */ - void generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, int layer_idx, SectionType section_type); + void generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, int layer_idx, SectionType section_type, const SierpinskiFillProvider* cross_fill_provider = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -157,7 +149,7 @@ class Infill /*! * Generate the infill pattern without the infill_multiplier functionality */ - void _generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines); + std::tuple, Polygons, Polygons> _generate(const Settings& settings, const SierpinskiFillProvider* cross_fill_pattern = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. * @@ -284,7 +276,7 @@ class Infill * \param toolpaths (output) The resulting toolpaths. Binned by inset_idx. * \param inset_value The offset between each consecutive two polygons */ - void generateConcentricInfill(std::vector& toolpaths); + void generateConcentricInfill(std::vector& toolpaths, const Settings& settings); /*! * Generate a rectangular grid of infill lines diff --git a/include/plugins/converters.h b/include/plugins/converters.h index ca36ddb99c..3706bd3962 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -9,6 +9,7 @@ #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.pb.h" +#include "cura/plugins/slots/infill/v0/generate.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" @@ -264,11 +265,121 @@ struct postprocess_response } }; -// struct infill_generate_request -//{ -// using value_type = slots::infill::v0::generate::CallRequest -// using native_value_type = Polygons -// } +struct infill_generate_request +{ + using value_type = slots::infill::v0::generate::CallRequest; + using native_value_type = Infill; + + value_type operator()(native_value_type infill, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh + ) const + { + value_type message{}; + + if (infill.inner_contour.empty()) + { + return message; + } + + auto* msg_infill_areas = message.mutable_infill_areas(); + auto* msg_polygons = msg_infill_areas->mutable_polygons(); + for (auto& polygon: infill.inner_contour.splitIntoParts()) + { + auto* msg_polygon = msg_polygons->Add(); + auto* msg_outline = msg_polygon->mutable_outline(); + + auto* msg_outline_path = msg_outline->add_path(); + for (const auto& point : ranges::front(polygon)) + { + msg_outline_path->set_x(point.X); + msg_outline_path->set_y(point.Y); + } + + auto* msg_holes = msg_polygon->mutable_holes(); + for (const auto& polygon : polygon.paths | ranges::views::drop(1)) + { + auto* msg_hole = msg_holes->Add(); + for (const auto& point : polygon) + { + auto* msg_path = msg_hole->add_path(); + msg_path->set_x(point.X); + msg_path->set_y(point.Y); + } + } + } + + return message; + } +}; + +struct infill_generate_response +{ + using value_type = slots::infill::v0::generate::CallResponse; + using native_value_type = std::tuple, Polygons, Polygons>; + + native_value_type operator()(const value_type& message) const + { + VariableWidthLines toolpaths; + Polygons result_polygons; + Polygons result_lines; + + for (auto& tool_path: message.tool_paths().tool_paths()) + { + ExtrusionLine lines; + for (auto& msg_junction: tool_path.junctions()) + { + auto& p = msg_junction.point(); + auto junction = ExtrusionJunction { p.x(), p.y(), msg_junction.width() }; + lines.emplace_back(junction); + } + + toolpaths.push_back(lines); + } + + std::vector toolpaths_; + toolpaths_.push_back(toolpaths); + + for (auto& polygon_msg: message.polygons().polygons()) + { + Polygons polygon {}; + + Polygon outline {}; + for (auto& path_msg: polygon_msg.outline().path()) + { + outline.add(Point{ path_msg.x(), path_msg.y() }); + } + polygon.add(outline); + + + for (auto& hole_msg: polygon_msg.holes()) + { + Polygon hole {}; + for (auto& path_msg: hole_msg.path()) + { + hole.add(Point{ path_msg.x(), path_msg.y() }); + } + polygon.add(hole); + } + + result_polygons.add(polygon); + } + + for (auto& polygon: message.poly_lines().paths()) + { + Polygon poly_line; + for (auto& p: polygon.path()) + { + poly_line.emplace_back(Point{ p.x(), p.y() }); + } + result_lines.emplace_back(poly_line); + } + + return std::make_tuple(toolpaths_, result_polygons, result_lines); + } +}; } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index bc4a5d35f0..de1a77cf0c 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -7,6 +7,7 @@ #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" +#include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" #include "infill.h" #include "plugins/converters.h" @@ -44,13 +45,13 @@ struct simplify_default } }; -//struct infill_generate_default -//{ -// auto operator()(Infill& infill, auto&&... args) -// { -// return infill._generate(std::forward(args)...); -// } -//}; +struct infill_generate_default +{ + auto operator()(Infill infill, auto&&... args) + { + return infill._generate(std::forward(args)...); + } +}; /** * @brief Alias for the Simplify slot. @@ -63,8 +64,8 @@ template using slot_simplify_ = SlotProxy; -//template -//using slot_infill_generate_ = SlotProxy; +template +using slot_infill_generate_ = SlotProxy; /** * @brief Alias for the Postprocess slot. @@ -234,9 +235,9 @@ class SlotConnectionFactory using slot_simplify = details::slot_simplify_; using slot_postprocess = details::slot_postprocess_<>; using slot_settings_broadcast = details::slot_settings_broadcast_<>; -//using slot_infill_generate = details::slot_infill_generate_; +using slot_infill_generate = details::slot_infill_generate_; -using SlotTypes = details::Typelist; +using SlotTypes = details::Typelist; } // namespace plugins using slots = plugins::details::SingletonRegistry; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 64fdc416b6..9586059a53 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -639,7 +639,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) extra_infill_shift, max_resolution, max_deviation, - base_settings, wall_line_count, small_area_width, infill_origin, @@ -651,7 +650,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) zag_skip_count, pocket_size); std::vector raft_paths; - infill_comp.generate(raft_paths, raft_polygons, raftLines, layer_nr, SectionType::ADHESION); + infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); if (! raft_paths.empty()) { const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; @@ -766,7 +765,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) extra_infill_shift, interface_max_resolution, interface_max_deviation, - interface_settings, wall_line_count, small_area_width, infill_origin, @@ -778,7 +776,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) zag_skip_count, pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. - infill_comp.generate(raft_paths, raft_polygons, raft_lines, layer_nr, SectionType::ADHESION); + infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); raft_polygons.clear(); @@ -872,7 +870,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) extra_infill_shift, surface_max_resolution, surface_max_deviation, - surface_settings, wall_line_count, small_area_width, infill_origin, @@ -884,7 +881,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) zag_skip_count, pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. - infill_comp.generate(raft_paths, raft_polygons, raft_lines, layer_nr, SectionType::ADHESION); + infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); raft_polygons.clear(); @@ -1632,7 +1629,6 @@ bool FffGcodeWriter::processMultiLayerInfill( infill_shift, max_resolution, max_deviation, - mesh.settings, wall_line_count, small_area_width, infill_origin, @@ -1642,17 +1638,17 @@ bool FffGcodeWriter::processMultiLayerInfill( use_endpieces, skip_some_zags, zag_skip_count, - mesh.settings.get("cross_infill_pocket_size"), - mesh.cross_fill_provider, - lightning_layer, - &mesh - ); + mesh.settings.get("cross_infill_pocket_size")); infill_comp.generate( infill_paths, infill_polygons, infill_lines, + mesh.settings, gcode_layer.getLayerNr(), - SectionType::INFILL); + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); } if (! infill_lines.empty() || ! infill_polygons.empty()) { @@ -1839,7 +1835,6 @@ bool FffGcodeWriter::processSingleLayerInfill( infill_shift, max_resolution, max_deviation, - mesh.settings, skin_below_wall_count, small_area_width, infill_origin, @@ -1849,16 +1844,17 @@ bool FffGcodeWriter::processSingleLayerInfill( use_endpieces, skip_some_zags, zag_skip_count, - pocket_size, - mesh.cross_fill_provider, - lightning_layer, - &mesh); + pocket_size); infill_comp.generate( wall_tool_paths.back(), infill_polygons, infill_lines, + mesh.settings, gcode_layer.getLayerNr(), - SectionType::INFILL); + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, min_skin_below_wall_count); @@ -1904,7 +1900,6 @@ bool FffGcodeWriter::processSingleLayerInfill( infill_shift, max_resolution, max_deviation, - mesh.settings, wall_line_count_here, small_area_width, infill_origin, @@ -1914,18 +1909,17 @@ bool FffGcodeWriter::processSingleLayerInfill( use_endpieces, skip_some_zags, zag_skip_count, - pocket_size, - mesh.cross_fill_provider, - lightning_layer, - &mesh - ); + pocket_size); infill_comp.generate( wall_tool_paths.back(), infill_polygons, infill_lines, + mesh.settings, gcode_layer.getLayerNr(), - SectionType::INFILL - ); + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, wall_line_count); @@ -2767,7 +2761,6 @@ void FffGcodeWriter::processSkinPrintFeature( extra_infill_shift, max_resolution, max_deviation, - mesh.settings, wall_line_count, small_area_width, infill_origin, @@ -2778,7 +2771,7 @@ void FffGcodeWriter::processSkinPrintFeature( skip_some_zags, zag_skip_count, pocket_size); - infill_comp.generate(skin_paths, skin_polygons, skin_lines, gcode_layer.getLayerNr(), SectionType::SKIN); + infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) @@ -3120,7 +3113,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer support_shift, max_resolution, max_deviation, - infill_extruder.settings, wall_count, small_area_width, infill_origin, @@ -3130,14 +3122,15 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer use_endpieces, skip_some_zags, zag_skip_count, - pocket_size, - storage.support.cross_fill_provider); + pocket_size); infill_comp.generate( wall_toolpaths_here, support_polygons, support_lines, + infill_extruder.settings, gcode_layer.getLayerNr(), - SectionType::SUPPORT); + SectionType::SUPPORT, + storage.support.cross_fill_provider); } if (need_travel_to_end_of_last_spiral && infill_extruder.settings.get("magic_spiralize")) @@ -3321,7 +3314,6 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay extra_infill_shift, max_resolution, max_deviation, - roof_extruder.settings, wall_line_count, small_area_width, infill_origin, @@ -3335,7 +3327,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay Polygons roof_polygons; std::vector roof_paths; Polygons roof_lines; - roof_computation.generate(roof_paths, roof_polygons, roof_lines, gcode_layer.getLayerNr(), SectionType::SUPPORT); + roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) { return false; // We didn't create any support roof. @@ -3440,7 +3432,6 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L extra_infill_shift, max_resolution, max_deviation, - bottom_extruder.settings, wall_line_count, small_area_width, infill_origin, @@ -3454,7 +3445,7 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L Polygons bottom_polygons; std::vector bottom_paths; Polygons bottom_lines; - bottom_computation.generate(bottom_paths, bottom_polygons, bottom_lines, gcode_layer.getLayerNr(), SectionType::SUPPORT); + bottom_computation.generate(bottom_paths, bottom_polygons, bottom_lines, bottom_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); if (bottom_paths.empty() && bottom_polygons.empty() && bottom_lines.empty()) { return false; diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index 7c3553ef61..ffa95e8f21 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -86,11 +86,29 @@ 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, mesh.settings, wall_line_count, small_area_width, 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 - 10, + shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_line_stitching + ); std::vector ironing_paths; Polygons ironing_polygons; Polygons ironing_lines; - infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, layer.getLayerNr(), SectionType::IRONING); + infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, mesh.settings, layer.getLayerNr(), SectionType::IRONING); if(ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty()) { diff --git a/src/infill.cpp b/src/infill.cpp index 662af51889..7cc5546958 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -1,15 +1,9 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include //For std::sort. -#include -#include - -#include -#include +#include "infill.h" #include "WallToolPaths.h" -#include "infill.h" #include "infill/GyroidInfill.h" #include "infill/ImageBasedDensityProvider.h" #include "infill/LightningGenerator.h" @@ -18,6 +12,7 @@ #include "infill/SierpinskiFillProvider.h" #include "infill/SubDivCube.h" #include "infill/UniformDensityProvider.h" +#include "plugins/slots.h" #include "sliceDataStorage.h" #include "utils/PolygonConnector.h" #include "utils/PolylineStitcher.h" @@ -25,6 +20,13 @@ #include "utils/UnionFind.h" #include "utils/polygonUtils.h" +#include +#include + +#include //For std::sort. +#include +#include + /*! * Function which returns the scanline_idx for a given x coordinate * @@ -72,8 +74,12 @@ Polygons Infill::generateWallToolPaths(std::vector& toolpath void Infill::generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, + const Settings& settings, int layer_idx, - SectionType section_type) + SectionType section_type, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh) { if (outer_contour.empty()) { @@ -155,8 +161,10 @@ void Infill::generate(std::vector& toolpaths, Polygons generated_result_polygons; Polygons generated_result_lines; - _generate(toolpaths, generated_result_polygons, generated_result_lines); - + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); + toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); + generated_result_polygons.add(generated_result_polygons_); + generated_result_lines.add(generated_result_lines_); zig_zaggify = zig_zaggify_real; multiplyInfill(generated_result_polygons, generated_result_lines); @@ -170,10 +178,10 @@ void Infill::generate(std::vector& toolpaths, Polygons generated_result_polygons; Polygons generated_result_lines; - - - _generate(toolpaths, generated_result_polygons, generated_result_lines); - + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); + toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); + generated_result_polygons.add(generated_result_polygons_); + generated_result_lines.add(generated_result_lines_); result_polygons.add(generated_result_polygons); result_lines.add(generated_result_lines); @@ -212,14 +220,21 @@ void Infill::generate(std::vector& toolpaths, } } -void Infill::_generate(std::vector& toolpaths, - Polygons& result_polygons, - Polygons& result_lines) +std::tuple, Polygons, Polygons> Infill::_generate( + const Settings& settings, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh + ) { if (inner_contour.empty()) - return; + return {}; if (line_distance == 0) - return; + return {}; + + std::vector toolpaths; + Polygons result_polygons; + Polygons result_lines; switch (pattern) { @@ -245,7 +260,7 @@ void Infill::_generate(std::vector& toolpaths, generateTrihexagonInfill(result_lines); break; case EFillMethod::CONCENTRIC: - generateConcentricInfill(toolpaths); + generateConcentricInfill(toolpaths, settings); break; case EFillMethod::ZIG_ZAG: generateZigZagInfill(result_lines, line_distance, fill_angle); @@ -272,7 +287,7 @@ void Infill::_generate(std::vector& toolpaths, break; case EFillMethod::LIGHTNING: assert(lightning_trees); // "Cannot generate Lightning infill without a generator!\n" - generateLightningInfill(lightning_layer, result_lines); + generateLightningInfill(lightning_trees, result_lines); break; default: spdlog::error("Fill pattern has unknown value.\n"); @@ -385,7 +400,7 @@ void Infill::generateLightningInfill(const LightningLayer* trees, Polygons& resu result_lines.add(trees->convertToLines(inner_contour, infill_line_width)); } -void Infill::generateConcentricInfill(std::vector& toolpaths) +void Infill::generateConcentricInfill(std::vector& toolpaths, const Settings& settings) { const coord_t min_area = infill_line_width * infill_line_width; diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 2c02820f76..8135507834 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -95,10 +95,10 @@ std::vector PolygonUtils::spreadDotsArea(const Polygons& polygons, Point { std::vector dummy_toolpaths; Settings dummy_settings; - Infill infill_gen(EFillMethod::LINES, false, false, polygons, 0, grid_size.X, 0, 1, 0, 0, 0, 0, 0, dummy_settings); + Infill infill_gen(EFillMethod::LINES, false, false, polygons, 0, grid_size.X, 0, 1, 0, 0, 0, 0, 0); Polygons result_polygons; Polygons result_lines; - infill_gen.generate(dummy_toolpaths, result_polygons, result_lines, 0, SectionType::DOTS); // FIXME: @jellespijker make sure the propper layer nr is used + infill_gen.generate(dummy_toolpaths, result_polygons, result_lines, dummy_settings, 0, SectionType::DOTS); // FIXME: @jellespijker make sure the propper layer nr is used std::vector result; for (PolygonRef line : result_lines) { From f5a96cc992aab20053d9189aaa382f36aeec41eb Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Mon, 31 Jul 2023 15:00:45 +0000 Subject: [PATCH 265/656] Applied clang-format. --- include/infill.h | 226 ++++++++++++++++++++--------------- include/plugins/converters.h | 58 ++++----- include/plugins/slots.h | 11 +- src/TopSurface.cpp | 77 +++++++----- src/infill.cpp | 200 ++++++++++++++++++++----------- 5 files changed, 347 insertions(+), 225 deletions(-) diff --git a/include/infill.h b/include/infill.h index 43d7ddf27c..906f069421 100644 --- a/include/infill.h +++ b/include/infill.h @@ -7,8 +7,8 @@ #include "infill/LightningGenerator.h" #include "infill/ZigzagConnectorProcessor.h" #include "settings/EnumSettings.h" //For infill types. -#include "settings/types/Angle.h" #include "settings/Settings.h" +#include "settings/types/Angle.h" #include "utils/ExtrusionLine.h" #include "utils/IntPoint.h" #include "utils/section_type.h" @@ -20,12 +20,14 @@ class AABB; class SierpinskiFillProvider; class SliceMeshStorage; -namespace plugins { -namespace details { +namespace plugins +{ +namespace details +{ struct infill_generate_default; } struct infill_generate_request; -} +} // namespace plugins class Infill { @@ -55,8 +57,8 @@ class Infill bool fill_gaps; //!< Whether to fill gaps in strips of infill that would be too thin to fit the infill lines. If disabled, those areas are left empty. bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself - bool skip_some_zags; //!< (ZigZag) Whether to skip some zags - size_t zag_skip_count; //!< (ZigZag) To skip one zag in every N if skip some zags is enabled + bool skip_some_zags; //!< (ZigZag) Whether to skip some zags + size_t zag_skip_count; //!< (ZigZag) To skip one zag in every N if skip some zags is enabled coord_t pocket_size; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern bool mirror_offset; //!< Indication in which offset direction the extra infill lines are made @@ -64,64 +66,67 @@ class Infill public: Infill() = default; - Infill(EFillMethod pattern - , bool zig_zaggify - , bool connect_polygons - , const Polygons in_outline - , coord_t infill_line_width - , coord_t line_distance - , coord_t infill_overlap - , size_t infill_multiplier - , AngleDegrees fill_angle - , coord_t z - , coord_t shift - , coord_t max_resolution - , coord_t max_deviation - , size_t wall_line_count = 0 - , coord_t small_area_width = 0 - , const Point& infill_origin = Point() - , bool skip_line_stitching = false - , bool fill_gaps = true - , bool connected_zigzags = false - , bool use_endpieces = false - , bool skip_some_zags = false - , size_t zag_skip_count = 0 - , coord_t pocket_size = 0 - ) - : pattern(pattern) - , zig_zaggify(zig_zaggify) - , connect_polygons(connect_polygons) - , outer_contour(in_outline) - , infill_line_width(infill_line_width) - , line_distance(line_distance) - , infill_overlap(infill_overlap) - , infill_multiplier(infill_multiplier) - , fill_angle(fill_angle) - , z(z) - , shift(shift) - , max_resolution(max_resolution) - , max_deviation(max_deviation) - , wall_line_count(wall_line_count) - , small_area_width(0) // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.x - , infill_origin(infill_origin) - , skip_line_stitching(skip_line_stitching) - , fill_gaps(fill_gaps) - , connected_zigzags(connected_zigzags) - , use_endpieces(use_endpieces) - , skip_some_zags(skip_some_zags) - , zag_skip_count(zag_skip_count) - , pocket_size(pocket_size) - , mirror_offset(zig_zaggify) + Infill( + EFillMethod pattern, + bool zig_zaggify, + bool connect_polygons, + const Polygons in_outline, + coord_t infill_line_width, + coord_t line_distance, + coord_t infill_overlap, + size_t infill_multiplier, + AngleDegrees fill_angle, + coord_t z, + coord_t shift, + coord_t max_resolution, + coord_t max_deviation, + size_t wall_line_count = 0, + coord_t small_area_width = 0, + const Point& infill_origin = Point(), + bool skip_line_stitching = false, + bool fill_gaps = true, + bool connected_zigzags = false, + bool use_endpieces = false, + bool skip_some_zags = false, + size_t zag_skip_count = 0, + coord_t pocket_size = 0) + : pattern(pattern) + , zig_zaggify(zig_zaggify) + , connect_polygons(connect_polygons) + , outer_contour(in_outline) + , infill_line_width(infill_line_width) + , line_distance(line_distance) + , infill_overlap(infill_overlap) + , infill_multiplier(infill_multiplier) + , fill_angle(fill_angle) + , z(z) + , shift(shift) + , max_resolution(max_resolution) + , max_deviation(max_deviation) + , wall_line_count(wall_line_count) + , small_area_width( + 0) // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.x + , infill_origin(infill_origin) + , skip_line_stitching(skip_line_stitching) + , fill_gaps(fill_gaps) + , connected_zigzags(connected_zigzags) + , use_endpieces(use_endpieces) + , skip_some_zags(skip_some_zags) + , zag_skip_count(zag_skip_count) + , pocket_size(pocket_size) + , mirror_offset(zig_zaggify) { - //TODO: The connected lines algorithm is only available for linear-based infill, for now. - //We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. - //Cubic Subdivision ends lines in the center of the infill so it won't be effective. - connect_lines = zig_zaggify && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); + // TODO: The connected lines algorithm is only available for linear-based infill, for now. + // We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. + // Cubic Subdivision ends lines in the center of the infill so it won't be effective. + connect_lines = zig_zaggify + && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); } /*! * Generate the infill. - * + * * \param toolpaths (output) The resulting variable-width paths (from the extra walls around the pattern). Binned by inset_idx. * \param result_polygons (output) The resulting polygons (from concentric infill) * \param result_lines (output) The resulting line segments (from linear infill types) @@ -130,7 +135,16 @@ class Infill * \param mesh A mesh for which to generate infill (should only be used for non-helper-mesh objects). * \param[in] cross_fill_provider The cross fractal subdivision decision functor */ - void generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, int layer_idx, SectionType section_type, const SierpinskiFillProvider* cross_fill_provider = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); + void generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + int layer_idx, + SectionType section_type, + const SierpinskiFillProvider* cross_fill_provider = nullptr, + const LightningLayer* lightning_layer = nullptr, + const SliceMeshStorage* mesh = nullptr); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -144,12 +158,25 @@ class Infill * \param settings [in] A settings storage to use for generating variable-width walls. * \return The inner contour of the wall toolpaths */ - static Polygons generateWallToolPaths(std::vector& toolpaths, Polygons& outer_contour, const size_t wall_line_count, const coord_t line_width, const coord_t infill_overlap, const Settings& settings, int layer_idx, SectionType section_type); + static Polygons generateWallToolPaths( + std::vector& toolpaths, + Polygons& outer_contour, + const size_t wall_line_count, + const coord_t line_width, + const coord_t infill_overlap, + const Settings& settings, + int layer_idx, + SectionType section_type); + private: /*! * Generate the infill pattern without the infill_multiplier functionality */ - std::tuple, Polygons, Polygons> _generate(const Settings& settings, const SierpinskiFillProvider* cross_fill_pattern = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); + std::tuple, Polygons, Polygons> _generate( + const Settings& settings, + const SierpinskiFillProvider* cross_fill_pattern = nullptr, + const LightningLayer* lightning_layer = nullptr, + const SliceMeshStorage* mesh = nullptr); /*! * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. * @@ -181,9 +208,7 @@ class Infill , end_segment(end_segment) , end_polygon(end_polygon) , previous(nullptr) - , next(nullptr) - { - }; + , next(nullptr){}; /*! * Where the line segment starts. @@ -246,7 +271,7 @@ class Infill * This is necessary for putting line segments in a hash set. * \param other The line segment to compare this line segment with. */ - bool operator ==(const InfillLineSegment& other) const; + bool operator==(const InfillLineSegment& other) const; }; /*! @@ -262,7 +287,7 @@ class Infill * \param result_polygons (output) The resulting polygons, if zigzagging accidentally happened to connect gyroid lines in a circle. */ void generateGyroidInfill(Polygons& result_polylines, Polygons& result_polygons); - + /*! * Generate lightning fill aka minfill aka 'Ribbed Support Vault Infill', see Tricard,Claux,Lefebvre/'Ribbed Support Vaults for 3D Printing of Hollowed Objects' * see https://hal.archives-ouvertes.fr/hal-02155929/document @@ -272,7 +297,7 @@ class Infill /*! * Generate sparse concentric infill - * + * * \param toolpaths (output) The resulting toolpaths. Binned by inset_idx. * \param inset_value The offset between each consecutive two polygons */ @@ -305,7 +330,7 @@ class Infill /*! * Generate a single shifting square grid of infill lines. * This is used in tetrahedral infill (Octet infill) and in Quarter Cubic infill. - * + * * \param pattern_z_shift The amount by which to shift the whole pattern down * \param angle_shift The angle to add to the infill_angle * \param[out] result (output) The resulting lines @@ -342,94 +367,101 @@ class Infill /*! * Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule * \param[out] result (output) The resulting lines - * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill + * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill * \param scanline_min_idx The lowest index of all scanlines crossing the polygon * \param line_distance The distance between two lines which are in the same direction * \param boundary The axis aligned boundary box within which the polygon is * \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline * \param total_shift total shift of the scanlines in the direction perpendicular to the fill_angle. */ - void addLineInfill( Polygons& result, - const PointMatrix& rotation_matrix, - const int scanline_min_idx, - const int line_distance, - const AABB boundary, - std::vector>& cut_list, - coord_t total_shift); + void addLineInfill( + Polygons& result, + const PointMatrix& rotation_matrix, + const int scanline_min_idx, + const int line_distance, + const AABB boundary, + std::vector>& cut_list, + coord_t total_shift); /*! * generate lines within the area of \p in_outline, at regular intervals of \p line_distance - * + * * idea: * intersect a regular grid of 'scanlines' with the area inside \p in_outline - * + * * \param[out] result (output) The resulting lines * \param line_distance The distance between two lines which are in the same direction * \param infill_rotation The angle of the generated lines * \param extra_shift extra shift of the scanlines in the direction perpendicular to the infill_rotation */ void generateLineInfill(Polygons& result, int line_distance, const double& infill_rotation, coord_t extra_shift); - + /*! * Function for creating linear based infill types (Lines, ZigZag). - * + * * This function implements the basic functionality of Infill::generateLineInfill (see doc of that function), * but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection. - * + * * It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill. * * \param[out] result (output) The resulting lines * \param line_distance The distance between two lines which are in the same direction - * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill + * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill * \param zigzag_connector_processor The processor used to generate zigzag connectors * \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line * \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle */ - void generateLinearBasedInfill(Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, coord_t extra_shift); + void generateLinearBasedInfill( + Polygons& result, + const int line_distance, + const PointMatrix& rotation_matrix, + ZigzagConnectorProcessor& zigzag_connector_processor, + const bool connected_zigzags, + coord_t extra_shift); /*! - * + * * generate lines within the area of [in_outline], at regular intervals of [line_distance] * idea: * intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill) * zigzag: * include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_| - * + * * Note that ZigZag consists of 3 types: * - without endpieces * - with disconnected endpieces * - with connected endpieces - * + * * <-- * ___ * | | | * | | | * | |___| * --> - * + * * ^ = even scanline * ^ ^ no endpieces - * + * * start boundary from even scanline! :D - * - * + * + * * v disconnected end piece: leave out last line segment * _____ * | | | \ . * | | | | * |_____| |__/ - * + * * ^ ^ ^ scanlines - * - * + * + * * v connected end piece * ________ * | | | \ . * | | | | * |_____| |__/ . - * + * * ^ ^ ^ scanlines - * + * * \param[out] result (output) The resulting lines * \param line_distance The distance between two lines which are in the same direction * \param infill_rotation The angle of the generated lines @@ -456,6 +488,6 @@ class Infill void connectLines(Polygons& result_lines); }; -}//namespace cura +} // namespace cura #endif // INFILL_H diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 3706bd3962..f14d1308fd 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -267,16 +267,16 @@ struct postprocess_response struct infill_generate_request { - using value_type = slots::infill::v0::generate::CallRequest; - using native_value_type = Infill; - - value_type operator()(native_value_type infill, - const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh - ) const - { + using value_type = slots::infill::v0::generate::CallRequest; + using native_value_type = Infill; + + value_type operator()( + native_value_type infill, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh) const + { value_type message{}; if (infill.inner_contour.empty()) @@ -286,7 +286,7 @@ struct infill_generate_request auto* msg_infill_areas = message.mutable_infill_areas(); auto* msg_polygons = msg_infill_areas->mutable_polygons(); - for (auto& polygon: infill.inner_contour.splitIntoParts()) + for (auto& polygon : infill.inner_contour.splitIntoParts()) { auto* msg_polygon = msg_polygons->Add(); auto* msg_outline = msg_polygon->mutable_outline(); @@ -312,27 +312,27 @@ struct infill_generate_request } return message; - } + } }; struct infill_generate_response { - using value_type = slots::infill::v0::generate::CallResponse; - using native_value_type = std::tuple, Polygons, Polygons>; + using value_type = slots::infill::v0::generate::CallResponse; + using native_value_type = std::tuple, Polygons, Polygons>; - native_value_type operator()(const value_type& message) const - { + native_value_type operator()(const value_type& message) const + { VariableWidthLines toolpaths; Polygons result_polygons; Polygons result_lines; - for (auto& tool_path: message.tool_paths().tool_paths()) + for (auto& tool_path : message.tool_paths().tool_paths()) { ExtrusionLine lines; - for (auto& msg_junction: tool_path.junctions()) + for (auto& msg_junction : tool_path.junctions()) { auto& p = msg_junction.point(); - auto junction = ExtrusionJunction { p.x(), p.y(), msg_junction.width() }; + auto junction = ExtrusionJunction{ p.x(), p.y(), msg_junction.width() }; lines.emplace_back(junction); } @@ -342,22 +342,22 @@ struct infill_generate_response std::vector toolpaths_; toolpaths_.push_back(toolpaths); - for (auto& polygon_msg: message.polygons().polygons()) + for (auto& polygon_msg : message.polygons().polygons()) { - Polygons polygon {}; + Polygons polygon{}; - Polygon outline {}; - for (auto& path_msg: polygon_msg.outline().path()) + Polygon outline{}; + for (auto& path_msg : polygon_msg.outline().path()) { outline.add(Point{ path_msg.x(), path_msg.y() }); } polygon.add(outline); - for (auto& hole_msg: polygon_msg.holes()) + for (auto& hole_msg : polygon_msg.holes()) { - Polygon hole {}; - for (auto& path_msg: hole_msg.path()) + Polygon hole{}; + for (auto& path_msg : hole_msg.path()) { hole.add(Point{ path_msg.x(), path_msg.y() }); } @@ -367,10 +367,10 @@ struct infill_generate_response result_polygons.add(polygon); } - for (auto& polygon: message.poly_lines().paths()) + for (auto& polygon : message.poly_lines().paths()) { Polygon poly_line; - for (auto& p: polygon.path()) + for (auto& p : polygon.path()) { poly_line.emplace_back(Point{ p.x(), p.y() }); } @@ -378,7 +378,7 @@ struct infill_generate_response } return std::make_tuple(toolpaths_, result_polygons, result_lines); - } + } }; } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index de1a77cf0c..6a705fb446 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -5,9 +5,9 @@ #define PLUGINS_SLOTS_H #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" +#include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" -#include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" #include "infill.h" #include "plugins/converters.h" @@ -65,7 +65,14 @@ using slot_simplify_ = SlotProxy; template -using slot_infill_generate_ = SlotProxy; +using slot_infill_generate_ = SlotProxy< + v0::SlotID::INFILL_GENERATE, + "<=1.0.0", + slots::infill::v0::generate::InfillGenerateService::Stub, + Validator, + infill_generate_request, + infill_generate_response, + Default>; /** * @brief Alias for the Postprocess slot. diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index ffa95e8f21..7accc653bc 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -1,28 +1,29 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include "infill.h" -#include "LayerPlan.h" -#include "sliceDataStorage.h" #include "TopSurface.h" + #include "ExtruderTrain.h" +#include "LayerPlan.h" +#include "infill.h" +#include "sliceDataStorage.h" namespace cura { TopSurface::TopSurface() { - //Do nothing. Areas stays empty. + // Do nothing. Areas stays empty. } void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t layer_number) { - //The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first. + // The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first. Polygons mesh_above; if (layer_number < mesh.layers.size() - 1) { mesh_above = mesh.layers[layer_number + 1].getOutlines(); - } //If this is the top-most layer, mesh_above stays empty. + } // If this is the top-most layer, mesh_above stays empty. if (mesh.settings.get("magic_spiralize")) { @@ -39,13 +40,14 @@ void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t l } } -bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer, const FffGcodeWriter& gcode_writer) const +bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer, const FffGcodeWriter& gcode_writer) + const { if (areas.empty()) { - return false; //Nothing to do. + return false; // Nothing to do. } - //Generate the lines to cover the surface. + // Generate the lines to cover the surface. const int extruder_nr = mesh.settings.get("top_bottom_extruder_nr").extruder_nr; const EFillMethod pattern = mesh.settings.get("ironing_pattern"); const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG; @@ -55,7 +57,7 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); const std::vector& top_most_skin_angles = (roofing_layer_count > 0) ? mesh.roofing_angles : mesh.skin_angles; assert(top_most_skin_angles.size() > 0); - const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); //Always perpendicular to the skin lines. + const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); // Always perpendicular to the skin lines. constexpr coord_t infill_overlap = 0; constexpr int infill_multiplier = 1; constexpr coord_t shift = 0; @@ -71,17 +73,17 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage coord_t ironing_inset = -mesh.settings.get("ironing_inset"); if (pattern == EFillMethod::ZIG_ZAG) { - //Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern + // Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern const Ratio width_scale = (float)mesh.settings.get("layer_height") / mesh.settings.get("infill_sparse_thickness"); ironing_inset += width_scale * line_width / 2; - //Align the edge of the ironing line with the edge of the outer wall + // Align the edge of the ironing line with the edge of the outer wall ironing_inset -= ironing_flow * line_width / 2; } else if (pattern == EFillMethod::CONCENTRIC) { - //Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern + // Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern ironing_inset += line_spacing - line_width / 2; - //Align the edge of the ironing line with the edge of the outer wall + // Align the edge of the ironing line with the edge of the outer wall ironing_inset -= ironing_flow * line_width / 2; } Polygons ironed_areas = areas.offset(ironing_inset); @@ -103,38 +105,38 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage wall_line_count, small_area_width, infill_origin, - skip_line_stitching - ); + skip_line_stitching); std::vector ironing_paths; Polygons ironing_polygons; Polygons ironing_lines; infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, mesh.settings, layer.getLayerNr(), SectionType::IRONING); - if(ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty()) + if (ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty()) { - return false; //Nothing to do. + return false; // Nothing to do. } layer.mode_skip_agressive_merge = true; bool added = false; - if(!ironing_polygons.empty()) + if (! ironing_polygons.empty()) { constexpr bool force_comb_retract = false; layer.addTravel(ironing_polygons[0][0], force_comb_retract); layer.addPolygonsByOptimizer(ironing_polygons, line_config, ZSeamConfig()); added = true; } - if(!ironing_lines.empty()) + if (! ironing_lines.empty()) { if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG) { - //Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams. + // Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams. const AABB bounding_box(ironed_areas); PointMatrix rotate(-direction + 90); const Point center = bounding_box.getMiddle(); - const Point far_away = rotate.apply(Point(0, vSize(bounding_box.max - center) * 100)); //Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre. - //Two options to start, both perpendicular to the ironing lines. Which is closer? + const Point far_away = rotate.apply( + Point(0, vSize(bounding_box.max - center) * 100)); // Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre. + // Two options to start, both perpendicular to the ironing lines. Which is closer? const Point front_side = PolygonUtils::findNearestVert(center + far_away, ironed_areas).p(); const Point back_side = PolygonUtils::findNearestVert(center - far_away, ironed_areas).p(); if (vSize2(layer.getLastPlannedPositionOrStartingPosition() - front_side) < vSize2(layer.getLastPlannedPositionOrStartingPosition() - back_side)) @@ -147,25 +149,40 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage } } - if( ! enforce_monotonic_order) + if (! enforce_monotonic_order) { layer.addLinesByOptimizer(ironing_lines, line_config, SpaceFillType::PolyLines); } else { - const coord_t max_adjacent_distance = line_spacing * 1.1; //Lines are considered adjacent - meaning they need to be printed in monotonic order - if spaced 1 line apart, with 10% extra play. + const coord_t max_adjacent_distance + = line_spacing * 1.1; // Lines are considered adjacent - meaning they need to be printed in monotonic order - if spaced 1 line apart, with 10% extra play. layer.addLinesMonotonic(Polygons(), ironing_lines, line_config, SpaceFillType::PolyLines, AngleRadians(direction), max_adjacent_distance); } added = true; } - if(!ironing_paths.empty()) + if (! ironing_paths.empty()) { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0u; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); - InsetOrderOptimizer wall_orderer(gcode_writer, storage, layer, mesh.settings, extruder_nr, - line_config, line_config, line_config, line_config, - retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, ironing_paths); + InsetOrderOptimizer wall_orderer( + gcode_writer, + storage, + layer, + mesh.settings, + extruder_nr, + line_config, + line_config, + line_config, + line_config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + ironing_paths); wall_orderer.addToLayer(); added = true; } @@ -174,4 +191,4 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage return added; } -} +} // namespace cura diff --git a/src/infill.cpp b/src/infill.cpp index 7cc5546958..cae9b1ebc2 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -51,7 +51,15 @@ static inline int computeScanSegmentIdx(int x, int line_width) namespace cura { -Polygons Infill::generateWallToolPaths(std::vector& toolpaths, Polygons& outer_contour, const size_t wall_line_count, const coord_t line_width, const coord_t infill_overlap, const Settings& settings, int layer_idx, SectionType section_type) +Polygons Infill::generateWallToolPaths( + std::vector& toolpaths, + Polygons& outer_contour, + const size_t wall_line_count, + const coord_t line_width, + const coord_t infill_overlap, + const Settings& settings, + int layer_idx, + SectionType section_type) { outer_contour = outer_contour.offset(infill_overlap); scripta::log("infill_outer_contour", outer_contour, section_type, layer_idx, scripta::CellVDI{ "infill_overlap", infill_overlap }); @@ -71,15 +79,16 @@ Polygons Infill::generateWallToolPaths(std::vector& toolpath return inner_contour; } -void Infill::generate(std::vector& toolpaths, - Polygons& result_polygons, - Polygons& result_lines, - const Settings& settings, - int layer_idx, - SectionType section_type, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) +void Infill::generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + int layer_idx, + SectionType section_type, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh) { if (outer_contour.empty()) { @@ -106,10 +115,8 @@ void Infill::generate(std::vector& toolpaths, small_infill.clear(); for (const auto& small_infill_part : small_infill_parts) { - if ( - small_infill_part.offset(-infill_line_width / 2).offset(infill_line_width / 2).area() < infill_line_width * infill_line_width * 10 - && ! inner_contour.intersection(small_infill_part.offset(infill_line_width / 4)).empty() - ) + if (small_infill_part.offset(-infill_line_width / 2).offset(infill_line_width / 2).area() < infill_line_width * infill_line_width * 10 + && ! inner_contour.intersection(small_infill_part.offset(infill_line_width / 4)).empty()) { inner_contour.add(small_infill_part); } @@ -125,12 +132,16 @@ void Infill::generate(std::vector& toolpaths, const size_t narrow_wall_count = small_area_width / infill_line_width + 1; WallToolPaths wall_toolpaths(small_infill, infill_line_width, narrow_wall_count, 0, settings, layer_idx, section_type); std::vector small_infill_paths = wall_toolpaths.getToolPaths(); - scripta::log("infill_small_infill_paths_0", small_infill_paths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "infill_small_infill_paths_0", + small_infill_paths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); for (const auto& small_infill_path : small_infill_paths) { toolpaths.emplace_back(small_infill_path); @@ -142,9 +153,11 @@ void Infill::generate(std::vector& toolpaths, if (pattern == EFillMethod::ZIG_ZAG // Zig-zag prints the zags along the walls. || (zig_zaggify && (pattern == EFillMethod::LINES // Zig-zaggified infill patterns print their zags along the walls. - || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON - || pattern == EFillMethod::GYROID || pattern == EFillMethod::CROSS || pattern == EFillMethod::CROSS_3D)) - || infill_multiplier % 2 == 0) // Multiplied infill prints loops of infill, partly along the walls, if even. For odd multipliers >1 it gets offset by the multiply algorithm itself. + || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL + || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON || pattern == EFillMethod::GYROID || pattern == EFillMethod::CROSS + || pattern == EFillMethod::CROSS_3D)) + || infill_multiplier % 2 + == 0) // Multiplied infill prints loops of infill, partly along the walls, if even. For odd multipliers >1 it gets offset by the multiply algorithm itself. { inner_contour = inner_contour.offset(-infill_line_width / 2); inner_contour = Simplify(max_resolution, max_deviation, 0).polygon(inner_contour); @@ -161,7 +174,8 @@ void Infill::generate(std::vector& toolpaths, Polygons generated_result_polygons; Polygons generated_result_lines; - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] + = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); generated_result_polygons.add(generated_result_polygons_); generated_result_lines.add(generated_result_lines_); @@ -178,7 +192,8 @@ void Infill::generate(std::vector& toolpaths, Polygons generated_result_polygons; Polygons generated_result_lines; - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] + = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); generated_result_polygons.add(generated_result_polygons_); generated_result_lines.add(generated_result_lines_); @@ -188,17 +203,27 @@ void Infill::generate(std::vector& toolpaths, } scripta::log("infill_result_polygons_0", result_polygons, section_type, layer_idx); scripta::log("infill_result_lines_0", result_lines, section_type, layer_idx); - scripta::log("infill_toolpaths_0", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "infill_toolpaths_0", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); if (connect_polygons) { // remove too small polygons coord_t snap_distance = infill_line_width * 2; // polygons with a span of max 1 * nozzle_size are too small - auto it = std::remove_if(result_polygons.begin(), result_polygons.end(), [snap_distance](PolygonRef poly) { return poly.shorterThan(snap_distance); }); + auto it = std::remove_if( + result_polygons.begin(), + result_polygons.end(), + [snap_distance](PolygonRef poly) + { + return poly.shorterThan(snap_distance); + }); result_polygons.erase(it, result_polygons.end()); PolygonConnector connector(infill_line_width); @@ -211,21 +236,21 @@ void Infill::generate(std::vector& toolpaths, toolpaths = connected_paths; scripta::log("infill_result_polygons_1", result_polygons, section_type, layer_idx); scripta::log("infill_result_lines_1", result_lines, section_type, layer_idx); - scripta::log("infill_toolpaths_1", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "infill_toolpaths_1", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); } } -std::tuple, Polygons, Polygons> Infill::_generate( - const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh - ) +std::tuple, Polygons, Polygons> + Infill::_generate(const Settings& settings, const SierpinskiFillProvider* cross_fill_provider, const LightningLayer* lightning_trees, const SliceMeshStorage* mesh) { if (inner_contour.empty()) return {}; @@ -305,7 +330,9 @@ std::tuple, Polygons, Polygons> Infill::_generat Simplify simplifier(max_resolution, max_deviation, 0); result_polygons = simplifier.polygon(result_polygons); - if (! skip_line_stitching && (zig_zaggify || pattern == EFillMethod::CROSS || pattern == EFillMethod::CROSS_3D || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::GYROID || pattern == EFillMethod::ZIG_ZAG)) + if (! skip_line_stitching + && (zig_zaggify || pattern == EFillMethod::CROSS || pattern == EFillMethod::CROSS_3D || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::GYROID + || pattern == EFillMethod::ZIG_ZAG)) { // don't stich for non-zig-zagged line infill types Polygons stitched_lines; PolylineStitcher::stitch(result_lines, stitched_lines, result_polygons, infill_line_width); @@ -332,7 +359,8 @@ void Infill::multiplyInfill(Polygons& result_polygons, Polygons& result_lines) const Polygons first_offset_polygons_inward = result_polygons.offset(-offset); // make lines on the inside of the input polygons const Polygons first_offset_polygons_outward = result_polygons.offset(offset); // make lines on the other side of the input polygons const Polygons first_offset_polygons = first_offset_polygons_outward.difference(first_offset_polygons_inward); - first_offset = first_offset_lines.unionPolygons(first_offset_polygons); // usually we only have either lines or polygons, but this code also handles an infill pattern which generates both + first_offset = first_offset_lines.unionPolygons( + first_offset_polygons); // usually we only have either lines or polygons, but this code also handles an infill pattern which generates both if (zig_zaggify) { first_offset = inner_contour.difference(first_offset); @@ -419,7 +447,8 @@ void Infill::generateConcentricInfill(std::vector& toolpaths constexpr size_t inset_wall_count = 1; // 1 wall at a time. constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. - WallToolPaths wall_toolpaths(current_inset, infill_line_width, inset_wall_count, wall_0_inset, settings, 0, SectionType::CONCENTRIC_INFILL); // FIXME: @jellespijker pass the correct layer + WallToolPaths wall_toolpaths(current_inset, infill_line_width, inset_wall_count, wall_0_inset, settings, 0, SectionType::CONCENTRIC_INFILL); // FIXME: @jellespijker pass + // the correct layer const std::vector inset_paths = wall_toolpaths.getToolPaths(); toolpaths.insert(toolpaths.end(), inset_paths.begin(), inset_paths.end()); @@ -513,7 +542,14 @@ void Infill::generateCrossInfill(const SierpinskiFillProvider& cross_fill_provid } } -void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector>& cut_list, coord_t shift) +void Infill::addLineInfill( + Polygons& result, + const PointMatrix& rotation_matrix, + const int scanline_min_idx, + const int line_distance, + const AABB boundary, + std::vector>& cut_list, + coord_t shift) { assert(! connect_lines && "connectLines() should add the infill lines, not addLineInfill"); @@ -590,7 +626,13 @@ void Infill::generateZigZagInfill(Polygons& result, const coord_t line_distance, * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment, * while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment. */ -void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, coord_t extra_shift) +void Infill::generateLinearBasedInfill( + Polygons& result, + const int line_distance, + const PointMatrix& rotation_matrix, + ZigzagConnectorProcessor& zigzag_connector_processor, + const bool connected_zigzags, + coord_t extra_shift) { if (line_distance == 0 || inner_contour.empty()) // No infill to generate (0% density) or no area to generate it in. { @@ -621,7 +663,10 @@ void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance // Then we can later join two crossings together to form lines and still know what polygon line segments that infill line connected to. struct Crossing { - Crossing(Point coordinate, size_t polygon_index, size_t vertex_index) : coordinate(coordinate), polygon_index(polygon_index), vertex_index(vertex_index){}; + Crossing(Point coordinate, size_t polygon_index, size_t vertex_index) + : coordinate(coordinate) + , polygon_index(polygon_index) + , vertex_index(vertex_index){}; Point coordinate; size_t polygon_index; size_t vertex_index; @@ -670,12 +715,14 @@ void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance if (p0.X < p1.X) { scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment - scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >) + scanline_idx1 + = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >) } else { direction = -1; - scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >) + scanline_idx0 + = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >) scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment } @@ -715,7 +762,8 @@ void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance { continue; } - InfillLineSegment* new_segment = new InfillLineSegment(unrotated_first, first.vertex_index, first.polygon_index, unrotated_second, second.vertex_index, second.polygon_index); + InfillLineSegment* new_segment + = new InfillLineSegment(unrotated_first, first.vertex_index, first.polygon_index, unrotated_second, second.vertex_index, second.polygon_index); // Put the same line segment in the data structure twice: Once for each of the polygon line segment that it crosses. crossings_on_line[first.polygon_index][first.vertex_index].push_back(new_segment); crossings_on_line[second.polygon_index][second.vertex_index].push_back(new_segment); @@ -774,15 +822,18 @@ void Infill::connectLines(Polygons& result_lines) Point vertex_after = inner_contour_polygon[vertex_index]; // Sort crossings on every line by how far they are from their initial point. - std::sort(crossings_on_polygon_segment.begin(), - crossings_on_polygon_segment.end(), - [&vertex_before, polygon_index, vertex_index](InfillLineSegment* left_hand_side, InfillLineSegment* right_hand_side) - { - // Find the two endpoints that are relevant. - const Point left_hand_point = (left_hand_side->start_segment == vertex_index && left_hand_side->start_polygon == polygon_index) ? left_hand_side->start : left_hand_side->end; - const Point right_hand_point = (right_hand_side->start_segment == vertex_index && right_hand_side->start_polygon == polygon_index) ? right_hand_side->start : right_hand_side->end; - return vSize(left_hand_point - vertex_before) < vSize(right_hand_point - vertex_before); - }); + std::sort( + crossings_on_polygon_segment.begin(), + crossings_on_polygon_segment.end(), + [&vertex_before, polygon_index, vertex_index](InfillLineSegment* left_hand_side, InfillLineSegment* right_hand_side) + { + // Find the two endpoints that are relevant. + const Point left_hand_point + = (left_hand_side->start_segment == vertex_index && left_hand_side->start_polygon == polygon_index) ? left_hand_side->start : left_hand_side->end; + const Point right_hand_point + = (right_hand_side->start_segment == vertex_index && right_hand_side->start_polygon == polygon_index) ? right_hand_side->start : right_hand_side->end; + return vSize(left_hand_point - vertex_before) < vSize(right_hand_point - vertex_before); + }); for (InfillLineSegment* crossing : crossings_on_polygon_segment) { @@ -797,14 +848,16 @@ void Infill::connectLines(Polygons& result_lines) assert(crossing_handle != (size_t)-1); const size_t previous_crossing_handle = connected_lines.find(previous_crossing); assert(previous_crossing_handle != (size_t)-1); - if (crossing_handle == previous_crossing_handle) // These two infill lines are already connected. Don't create a loop now. Continue connecting with the next crossing. + if (crossing_handle + == previous_crossing_handle) // These two infill lines are already connected. Don't create a loop now. Continue connecting with the next crossing. { continue; } // Join two infill lines together with a connecting line. // Here the InfillLineSegments function as a linked list, so that they can easily be joined. - const Point previous_point = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) ? previous_segment->start : previous_segment->end; + const Point previous_point + = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) ? previous_segment->start : previous_segment->end; const Point next_point = (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index) ? crossing->start : crossing->end; InfillLineSegment* new_segment; // If the segment is zero length, we avoid creating it but still want to connect the crossing with the previous segment @@ -822,7 +875,8 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); // A connecting line between them. + new_segment + = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); // A connecting line between them. new_segment->previous = previous_segment; if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) { @@ -863,7 +917,13 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment(previous_segment->start, vertex_index, polygon_index, vertex_after, (vertex_index + 1) % inner_contour[polygon_index].size(), polygon_index); + new_segment = new InfillLineSegment( + previous_segment->start, + vertex_index, + polygon_index, + vertex_after, + (vertex_index + 1) % inner_contour[polygon_index].size(), + polygon_index); previous_segment->previous = new_segment; new_segment->previous = previous_segment; previous_segment = new_segment; @@ -879,7 +939,13 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment(previous_segment->end, vertex_index, polygon_index, vertex_after, (vertex_index + 1) % inner_contour[polygon_index].size(), polygon_index); + new_segment = new InfillLineSegment( + previous_segment->end, + vertex_index, + polygon_index, + vertex_after, + (vertex_index + 1) % inner_contour[polygon_index].size(), + polygon_index); previous_segment->next = new_segment; new_segment->previous = previous_segment; previous_segment = new_segment; From 787bad091b2a3807741fe4065416ab6156924f09 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 31 Jul 2023 17:06:29 +0200 Subject: [PATCH 266/656] Fixed merge conflict CURA-10619 --- include/infill.h | 63 +++++++++++++++++++++--------------- include/plugins/converters.h | 1 + 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/infill.h b/include/infill.h index 906f069421..f5ca5d1e43 100644 --- a/include/infill.h +++ b/include/infill.h @@ -35,32 +35,35 @@ class Infill friend class plugins::details::infill_generate_default; friend class plugins::infill_generate_request; - EFillMethod pattern; //!< the space filling pattern of the infill to generate - bool zig_zaggify; //!< Whether to connect the end pieces of the support lines via the wall - bool connect_lines; //!< Whether the lines and zig_zaggification are generated by the connectLines algorithm - bool connect_polygons; //!< Whether to connect as much polygons together into a single path - Polygons outer_contour; //!< The area that originally needs to be filled with infill. The input of the algorithm. - Polygons inner_contour; //!< The part of the contour that will get filled with an infill pattern. Equals outer_contour minus the extra infill walls. - coord_t infill_line_width; //!< The line width of the infill lines to generate - coord_t line_distance; //!< The distance between two infill lines / polygons - coord_t infill_overlap; //!< the distance by which to overlap with the actual area within which to generate infill - size_t infill_multiplier; //!< the number of infill lines next to each other - AngleDegrees fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid) - coord_t z; //!< height of the layer for which we generate infill - coord_t shift; //!< shift of the scanlines in the direction perpendicular to the fill_angle - coord_t max_resolution; //!< Min feature size of the output - coord_t max_deviation; //!< Max deviation fro the original poly when enforcing max_resolution - size_t wall_line_count; //!< Number of walls to generate at the boundary of the infill region, spaced \ref infill_line_width apart - coord_t small_area_width; //!< Maximum width of a small infill region to be filled with walls - Point infill_origin; //!< origin of the infill pattern - bool skip_line_stitching; //!< Whether to bypass the line stitching normally performed for polyline type infills - bool fill_gaps; //!< Whether to fill gaps in strips of infill that would be too thin to fit the infill lines. If disabled, those areas are left empty. - bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector - bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself - bool skip_some_zags; //!< (ZigZag) Whether to skip some zags - size_t zag_skip_count; //!< (ZigZag) To skip one zag in every N if skip some zags is enabled - coord_t pocket_size; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern - bool mirror_offset; //!< Indication in which offset direction the extra infill lines are made + EFillMethod pattern{}; //!< the space filling pattern of the infill to generate + bool zig_zaggify{}; //!< Whether to connect the end pieces of the support lines via the wall + bool connect_lines{ calcConnectLines(pattern, zig_zaggify) }; //!< Whether the lines and zig_zaggification are generated by the connectLines algorithm + // TODO: The connected lines algorithm is only available for linear-based infill, for now. + // We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. + // Cubic Subdivision ends lines in the center of the infill so it won't be effective. + bool connect_polygons{}; //!< Whether to connect as much polygons together into a single path + Polygons outer_contour{}; //!< The area that originally needs to be filled with infill. The input of the algorithm. + Polygons inner_contour{}; //!< The part of the contour that will get filled with an infill pattern. Equals outer_contour minus the extra infill walls. + coord_t infill_line_width{}; //!< The line width of the infill lines to generate + coord_t line_distance{}; //!< The distance between two infill lines / polygons + coord_t infill_overlap{}; //!< the distance by which to overlap with the actual area within which to generate infill + size_t infill_multiplier{}; //!< the number of infill lines next to each other + AngleDegrees fill_angle{}; //!< for linear infill types: the angle of the infill lines (or the angle of the grid) + coord_t z{}; //!< height of the layer for which we generate infill + coord_t shift{}; //!< shift of the scanlines in the direction perpendicular to the fill_angle + coord_t max_resolution{}; //!< Min feature size of the output + coord_t max_deviation{}; //!< Max deviation fro the original poly when enforcing max_resolution + size_t wall_line_count{}; //!< Number of walls to generate at the boundary of the infill region, spaced \ref infill_line_width apart + coord_t small_area_width{}; //!< Maximum width of a small infill region to be filled with walls + Point infill_origin{}; //!< origin of the infill pattern + bool skip_line_stitching{}; //!< Whether to bypass the line stitching normally performed for polyline type infills + bool fill_gaps{}; //!< Whether to fill gaps in strips of infill that would be too thin to fit the infill lines. If disabled, those areas are left empty. + bool connected_zigzags{}; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector + bool use_endpieces{}; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself + bool skip_some_zags{}; //!< (ZigZag) Whether to skip some zags + size_t zag_skip_count{}; //!< (ZigZag) To skip one zag in every N if skip some zags is enabled + coord_t pocket_size{}; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern + bool mirror_offset{}; //!< Indication in which offset direction the extra infill lines are made static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0) public: @@ -169,6 +172,13 @@ class Infill SectionType section_type); private: + constexpr bool calcConnectLines(const auto& pattern, const auto& zig_zaggify) const + { + return zig_zaggify + && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); + } + /*! * Generate the infill pattern without the infill_multiplier functionality */ @@ -487,6 +497,7 @@ class Infill */ void connectLines(Polygons& result_lines); }; +static_assert(std::semiregular, "Infill should be semiregular"); } // namespace cura diff --git a/include/plugins/converters.h b/include/plugins/converters.h index f14d1308fd..d359660675 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -9,6 +9,7 @@ #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.pb.h" +#include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" #include "cura/plugins/slots/infill/v0/generate.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.pb.h" From aa7e83a8072849ea19e8d689d03eae05c8b98a05 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 31 Jul 2023 17:47:28 +0200 Subject: [PATCH 267/656] Ensure default constructible CURA-10619 --- include/TopSurface.h | 5 +- include/infill.h | 150 ++++++++++++++++++++++++++++++------------- 2 files changed, 109 insertions(+), 46 deletions(-) diff --git a/include/TopSurface.h b/include/TopSurface.h index dcc5e5b9c2..05affb5ad9 100644 --- a/include/TopSurface.h +++ b/include/TopSurface.h @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef TOPSURFACE_H #define TOPSURFACE_H @@ -13,6 +13,7 @@ class GCodePathConfig; class FffGcodeWriter; class LayerPlan; class SliceMeshStorage; +class SliceDataStorage; class TopSurface { diff --git a/include/infill.h b/include/infill.h index f5ca5d1e43..2fbfa80b72 100644 --- a/include/infill.h +++ b/include/infill.h @@ -13,6 +13,8 @@ #include "utils/IntPoint.h" #include "utils/section_type.h" +#include + namespace cura { @@ -65,15 +67,81 @@ class Infill coord_t pocket_size{}; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern bool mirror_offset{}; //!< Indication in which offset direction the extra infill lines are made - static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0) + static constexpr auto one_over_sqrt_2 = 1.0 / std::numbers::sqrt2; + public: - Infill() = default; + constexpr Infill() noexcept = default; + + Infill( + EFillMethod pattern, + bool zig_zaggify, + bool connect_polygons, + Polygons in_outline, + coord_t infill_line_width, + coord_t line_distance, + coord_t infill_overlap, + size_t infill_multiplier, + AngleDegrees fill_angle, + coord_t z, + coord_t shift, + coord_t max_resolution, + coord_t max_deviation) noexcept + : pattern{ pattern } + , zig_zaggify{ zig_zaggify } + , connect_polygons{ connect_polygons } + , outer_contour{ in_outline } + , infill_line_width{ infill_line_width } + , line_distance{ line_distance } + , infill_overlap{ infill_overlap } + , infill_multiplier{ infill_multiplier } + , fill_angle{ fill_angle } + , z{ z } + , shift{ shift } + , max_resolution{ max_resolution } + , max_deviation{ max_deviation } {}; + + Infill( + EFillMethod pattern, + bool zig_zaggify, + bool connect_polygons, + Polygons in_outline, + coord_t infill_line_width, + coord_t line_distance, + coord_t infill_overlap, + size_t infill_multiplier, + AngleDegrees fill_angle, + coord_t z, + coord_t shift, + coord_t max_resolution, + coord_t max_deviation, + size_t wall_line_count, + coord_t small_area_width, + Point infill_origin, + bool skip_line_stitching) noexcept + : pattern{ pattern } + , zig_zaggify{ zig_zaggify } + , connect_polygons{ connect_polygons } + , outer_contour{ in_outline } + , infill_line_width{ infill_line_width } + , line_distance{ line_distance } + , infill_overlap{ infill_overlap } + , infill_multiplier{ infill_multiplier } + , fill_angle{ fill_angle } + , z{ z } + , shift{ shift } + , max_resolution{ max_resolution } + , max_deviation{ max_deviation } + , wall_line_count{ wall_line_count } + , small_area_width{ 0 } + // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.0 + , infill_origin{ infill_origin } + , skip_line_stitching{ skip_line_stitching } {}; Infill( EFillMethod pattern, bool zig_zaggify, bool connect_polygons, - const Polygons in_outline, + Polygons in_outline, coord_t infill_line_width, coord_t line_distance, coord_t infill_overlap, @@ -83,48 +151,42 @@ class Infill coord_t shift, coord_t max_resolution, coord_t max_deviation, - size_t wall_line_count = 0, - coord_t small_area_width = 0, - const Point& infill_origin = Point(), - bool skip_line_stitching = false, - bool fill_gaps = true, - bool connected_zigzags = false, - bool use_endpieces = false, - bool skip_some_zags = false, - size_t zag_skip_count = 0, - coord_t pocket_size = 0) - : pattern(pattern) - , zig_zaggify(zig_zaggify) - , connect_polygons(connect_polygons) - , outer_contour(in_outline) - , infill_line_width(infill_line_width) - , line_distance(line_distance) - , infill_overlap(infill_overlap) - , infill_multiplier(infill_multiplier) - , fill_angle(fill_angle) - , z(z) - , shift(shift) - , max_resolution(max_resolution) - , max_deviation(max_deviation) - , wall_line_count(wall_line_count) - , small_area_width( - 0) // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.x - , infill_origin(infill_origin) - , skip_line_stitching(skip_line_stitching) - , fill_gaps(fill_gaps) - , connected_zigzags(connected_zigzags) - , use_endpieces(use_endpieces) - , skip_some_zags(skip_some_zags) - , zag_skip_count(zag_skip_count) - , pocket_size(pocket_size) - , mirror_offset(zig_zaggify) + size_t wall_line_count, + coord_t small_area_width, + Point infill_origin, + bool skip_line_stitching, + bool fill_gaps, + bool connected_zigzags, + bool use_endpieces, + bool skip_some_zags, + size_t zag_skip_count, + coord_t pocket_size) noexcept + : pattern{ pattern } + , zig_zaggify{ zig_zaggify } + , connect_polygons{ connect_polygons } + , outer_contour{ in_outline } + , infill_line_width{ infill_line_width } + , line_distance{ line_distance } + , infill_overlap{ infill_overlap } + , infill_multiplier{ infill_multiplier } + , fill_angle{ fill_angle } + , z{ z } + , shift{ shift } + , max_resolution{ max_resolution } + , max_deviation{ max_deviation } + , wall_line_count{ wall_line_count } + , small_area_width{ 0 } + // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.0 + , infill_origin{ infill_origin } + , skip_line_stitching{ skip_line_stitching } + , fill_gaps{ fill_gaps } + , connected_zigzags{ connected_zigzags } + , use_endpieces{ use_endpieces } + , skip_some_zags{ skip_some_zags } + , zag_skip_count{ zag_skip_count } + , pocket_size{ pocket_size } + , mirror_offset{ zig_zaggify } { - // TODO: The connected lines algorithm is only available for linear-based infill, for now. - // We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. - // Cubic Subdivision ends lines in the center of the infill so it won't be effective. - connect_lines = zig_zaggify - && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC - || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); } /*! From 967912560cabc5cae0aa3a401f78202f24cd632b Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 31 Jul 2023 15:48:12 +0000 Subject: [PATCH 268/656] Applied clang-format. --- include/TopSurface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TopSurface.h b/include/TopSurface.h index 05affb5ad9..bfebc3f3a9 100644 --- a/include/TopSurface.h +++ b/include/TopSurface.h @@ -60,6 +60,6 @@ class TopSurface Polygons areas; }; -} +} // namespace cura #endif /* TOPSURFACE_H */ From 73ca065a613c160249a0eef0a0ec8e8dc86495bf Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 1 Aug 2023 14:54:47 +0200 Subject: [PATCH 269/656] Make it work on apple CURA-10619 --- include/infill.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/infill.h b/include/infill.h index 2fbfa80b72..20f44b64da 100644 --- a/include/infill.h +++ b/include/infill.h @@ -69,6 +69,13 @@ class Infill static constexpr auto one_over_sqrt_2 = 1.0 / std::numbers::sqrt2; + constexpr bool calcConnectLines(const EFillMethod pattern, const bool zig_zaggify) + { + return zig_zaggify + && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); + } + public: constexpr Infill() noexcept = default; @@ -234,12 +241,7 @@ class Infill SectionType section_type); private: - constexpr bool calcConnectLines(const auto& pattern, const auto& zig_zaggify) const - { - return zig_zaggify - && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC - || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); - } + /*! * Generate the infill pattern without the infill_multiplier functionality From 0d3eb73e6f818011a83e82707c3dfb658d3d04c8 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 1 Aug 2023 12:55:23 +0000 Subject: [PATCH 270/656] Applied clang-format. --- include/infill.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/infill.h b/include/infill.h index 20f44b64da..6d462e2c6a 100644 --- a/include/infill.h +++ b/include/infill.h @@ -241,8 +241,6 @@ class Infill SectionType section_type); private: - - /*! * Generate the infill pattern without the infill_multiplier functionality */ From dd6f138ff5dc59b5e05599e0fbb6527532c231f7 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 1 Aug 2023 16:17:13 +0200 Subject: [PATCH 271/656] Move slot generation step in infill switch This was needed because otherwise, if a infill generate slot was loaded _all_ infill would automatically be routed through the plugin, bypassing the regular engine behavior. By executing the slot within the infill pattern type switch the plugin infill pattern generation is only executed when that infill pattern is set to plugin. CURA-10619 --- include/infill.h | 18 +--------------- include/plugins/converters.h | 15 +++++-------- include/plugins/slots.h | 7 ++++-- src/infill.cpp | 41 ++++++++++++++++++------------------ 4 files changed, 32 insertions(+), 49 deletions(-) diff --git a/include/infill.h b/include/infill.h index 6d462e2c6a..7ce22ecc3e 100644 --- a/include/infill.h +++ b/include/infill.h @@ -22,20 +22,9 @@ class AABB; class SierpinskiFillProvider; class SliceMeshStorage; -namespace plugins -{ -namespace details -{ -struct infill_generate_default; -} -struct infill_generate_request; -} // namespace plugins - class Infill { friend class InfillTest; - friend class plugins::details::infill_generate_default; - friend class plugins::infill_generate_request; EFillMethod pattern{}; //!< the space filling pattern of the infill to generate bool zig_zaggify{}; //!< Whether to connect the end pieces of the support lines via the wall @@ -244,12 +233,7 @@ class Infill /*! * Generate the infill pattern without the infill_multiplier functionality */ - std::tuple, Polygons, Polygons> _generate( - const Settings& settings, - const SierpinskiFillProvider* cross_fill_pattern = nullptr, - const LightningLayer* lightning_layer = nullptr, - const SliceMeshStorage* mesh = nullptr); - /*! + void _generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, const SierpinskiFillProvider* cross_fill_pattern = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. * * This is done in a way such that there is not overlap between the lines diff --git a/include/plugins/converters.h b/include/plugins/converters.h index d359660675..38e6c21958 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -269,25 +269,20 @@ struct postprocess_response struct infill_generate_request { using value_type = slots::infill::v0::generate::CallRequest; - using native_value_type = Infill; - - value_type operator()( - native_value_type infill, - const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) const + using native_value_type = Polygons; + + value_type operator()(const native_value_type& inner_contour) const { value_type message{}; - if (infill.inner_contour.empty()) + if (inner_contour.empty()) { return message; } auto* msg_infill_areas = message.mutable_infill_areas(); auto* msg_polygons = msg_infill_areas->mutable_polygons(); - for (auto& polygon : infill.inner_contour.splitIntoParts()) + for (auto& polygon : inner_contour.splitIntoParts()) { auto* msg_polygon = msg_polygons->Add(); auto* msg_outline = msg_polygon->mutable_outline(); diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 6a705fb446..6a11a48235 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -47,9 +47,12 @@ struct simplify_default struct infill_generate_default { - auto operator()(Infill infill, auto&&... args) + std::tuple, Polygons, Polygons> operator()(Polygons _infill, auto&&... args) { - return infill._generate(std::forward(args)...); + // this code is only reachable when no slot is registered while the infill type is requested to be + // generated by a plugin; this should not be possible to set up in the first place. Return an empty + // infill. + return {}; } }; diff --git a/src/infill.cpp b/src/infill.cpp index cae9b1ebc2..06690f6df9 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -174,11 +174,7 @@ void Infill::generate( Polygons generated_result_polygons; Polygons generated_result_lines; - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] - = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); - toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); - generated_result_polygons.add(generated_result_polygons_); - generated_result_lines.add(generated_result_lines_); + _generate(toolpaths, generated_result_polygons, generated_result_lines, settings, cross_fill_provider, lightning_trees, mesh); zig_zaggify = zig_zaggify_real; multiplyInfill(generated_result_polygons, generated_result_lines); @@ -192,11 +188,7 @@ void Infill::generate( Polygons generated_result_polygons; Polygons generated_result_lines; - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] - = slots::instance().generate(*this, settings, cross_fill_provider, lightning_trees, mesh); - toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); - generated_result_polygons.add(generated_result_polygons_); - generated_result_lines.add(generated_result_lines_); + _generate(toolpaths, generated_result_polygons, generated_result_lines, settings, cross_fill_provider, lightning_trees, mesh); result_polygons.add(generated_result_polygons); result_lines.add(generated_result_lines); @@ -249,17 +241,17 @@ void Infill::generate( } } -std::tuple, Polygons, Polygons> - Infill::_generate(const Settings& settings, const SierpinskiFillProvider* cross_fill_provider, const LightningLayer* lightning_trees, const SliceMeshStorage* mesh) -{ +void Infill::_generate(std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh){ if (inner_contour.empty()) - return {}; + return; if (line_distance == 0) - return {}; - - std::vector toolpaths; - Polygons result_polygons; - Polygons result_lines; + return; switch (pattern) { @@ -314,6 +306,15 @@ std::tuple, Polygons, Polygons> assert(lightning_trees); // "Cannot generate Lightning infill without a generator!\n" generateLightningInfill(lightning_trees, result_lines); break; + case EFillMethod::PLUGIN: + { + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] + = slots::instance().generate(inner_contour); + toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); + result_polygons.add(generated_result_polygons_); + result_lines.add(generated_result_lines_); + break; + } default: spdlog::error("Fill pattern has unknown value.\n"); break; @@ -321,7 +322,7 @@ std::tuple, Polygons, Polygons> if (connect_lines) { - // The list should be empty because it will be again filled completely. Otherwise might have double lines. + // The list should be empty because it will be again filled completely. Otherwise, might have double lines. assert(result_lines.empty()); result_lines.clear(); connectLines(result_lines); From 7da387413409a8dc07aa238f09ed41c9bbe51e03 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 1 Aug 2023 14:31:21 +0000 Subject: [PATCH 272/656] Applied clang-format. --- include/infill.h | 29 ++++++++++++++++++----------- src/infill.cpp | 25 +++++++++++++------------ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/include/infill.h b/include/infill.h index 7ce22ecc3e..92bd5749d0 100644 --- a/include/infill.h +++ b/include/infill.h @@ -233,17 +233,24 @@ class Infill /*! * Generate the infill pattern without the infill_multiplier functionality */ - void _generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, const SierpinskiFillProvider* cross_fill_pattern = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! - * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. - * - * This is done in a way such that there is not overlap between the lines - * except the middle original one if the multiplier is odd. - * - * This introduces a lot of line segments. - * - * \param[in,out] result_polygons The polygons to be multiplied (input and output) - * \param[in,out] result_lines The lines to be multiplied (input and output) - */ + void _generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_pattern = nullptr, + const LightningLayer* lightning_layer = nullptr, + const SliceMeshStorage* mesh = nullptr); /*! + * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. + * + * This is done in a way such that there is not overlap between the lines + * except the middle original one if the multiplier is odd. + * + * This introduces a lot of line segments. + * + * \param[in,out] result_polygons The polygons to be multiplied (input and output) + * \param[in,out] result_lines The lines to be multiplied (input and output) + */ void multiplyInfill(Polygons& result_polygons, Polygons& result_lines); struct InfillLineSegment diff --git a/src/infill.cpp b/src/infill.cpp index 06690f6df9..0e5665fd31 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -241,13 +241,15 @@ void Infill::generate( } } -void Infill::_generate(std::vector& toolpaths, - Polygons& result_polygons, - Polygons& result_lines, - const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh){ +void Infill::_generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh) +{ if (inner_contour.empty()) return; if (line_distance == 0) @@ -308,11 +310,10 @@ void Infill::_generate(std::vector& toolpaths, break; case EFillMethod::PLUGIN: { - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] - = slots::instance().generate(inner_contour); - toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); - result_polygons.add(generated_result_polygons_); - result_lines.add(generated_result_lines_); + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate(inner_contour); + toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); + result_polygons.add(generated_result_polygons_); + result_lines.add(generated_result_lines_); break; } default: From 4e850d7d9e4d2ad875fe331f7c8d2d24781cf047 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 2 Aug 2023 08:55:27 +0200 Subject: [PATCH 273/656] Send pattern with the plugin generate Retrieving the used pattern would be nearly impossible from the SettingsBroadcast alone. We need to be aware of the context in which this is called. CURA-10619 --- include/plugins/converters.h | 3 ++- src/infill.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 38e6c21958..3184bcf6e3 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -271,9 +271,10 @@ struct infill_generate_request using value_type = slots::infill::v0::generate::CallRequest; using native_value_type = Polygons; - value_type operator()(const native_value_type& inner_contour) const + value_type operator()(const native_value_type& inner_contour, const std::string& pattern) const { value_type message{}; + message.set_pattern(pattern); if (inner_contour.empty()) { diff --git a/src/infill.cpp b/src/infill.cpp index 0e5665fd31..f09e09f7bc 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -310,7 +310,8 @@ void Infill::_generate( break; case EFillMethod::PLUGIN: { - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate(inner_contour); + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] + = slots::instance().generate(inner_contour, mesh->settings.get("infill_pattern")); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); result_polygons.add(generated_result_polygons_); result_lines.add(generated_result_lines_); From 3fbca78d8b2527a61257e2b6706b67313f41d675 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 2 Aug 2023 09:18:31 +0200 Subject: [PATCH 274/656] Make 'slot connection factory' implicit. Don't need the cumbersome way if we can do it this simply. done as part of CURA-10851 (but maybe should have been part of CURA-10805). --- include/plugins/slots.h | 54 +++++------------------- src/communication/ArcusCommunication.cpp | 2 +- 2 files changed, 12 insertions(+), 44 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 700b7c3989..43b1c08ef1 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -67,8 +67,6 @@ template using slot_settings_broadcast_ = SlotProxy; -using slot_to_connect_map_t = std::map)>>; - template struct Typelist { @@ -81,9 +79,10 @@ template class Unit> class Registry, Unit> { public: - constexpr void append_to_connect_map(slot_to_connect_map_t& function_map) + void connect(const v0::SlotID& slot_id, auto&& channel) { - } // Base case, do nothing. + assert(false); + } // Base case, should not be executed template void broadcast(auto&&... args) @@ -100,16 +99,6 @@ class Registry, Unit> : public Registry using Base::broadcast; friend Base; - constexpr void append_to_connect_map(slot_to_connect_map_t& function_map) - { - function_map.insert({ T::slot_id, - [&](std::shared_ptr plugin) - { - connect(plugin); - } }); - Base::append_to_connect_map(function_map); - } - template constexpr auto& get() { @@ -122,11 +111,15 @@ class Registry, Unit> : public Registry return get().modify(std::forward(args)...); } - template - void connect(auto&& plugin) + void connect(const v0::SlotID& slot_id, auto&& channel) { - using Tp = decltype(get_type().proxy); - get_type().proxy = Tp{ std::forward(std::move(plugin)) }; + if (slot_id == T::slot_id) + { + using Tp = decltype(get_type().proxy); + get_type().proxy = Tp{ std::forward(std::move(channel)) }; + return; + } + Base::connect(slot_id, channel); } template @@ -187,33 +180,8 @@ using slot_settings_broadcast = details::slot_settings_broadcast_<>; using SlotTypes = details::Typelist; -template -class SlotConnectionFactory_ -{ -public: - static SlotConnectionFactory_& instance() - { - static SlotConnectionFactory_ instance; - return instance; - } - - void connect(const plugins::v0::SlotID& slot_id, std::shared_ptr plugin) - { - slot_to_connect_map[slot_id](plugin); - } - -private: - SlotConnectionFactory_() - { - S::instance().append_to_connect_map(slot_to_connect_map); - } - - plugins::details::slot_to_connect_map_t slot_to_connect_map; -}; - } // namespace plugins using slots = plugins::details::SingletonRegistry; -using SlotConnectionFactory = plugins::SlotConnectionFactory_; } // namespace cura diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 78377ed2f7..2ff13196b9 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -522,7 +522,7 @@ void ArcusCommunication::sliceNext() if (plugin.has_address() && plugin.has_port()) { const auto slot_id = static_cast(plugin.id()); - SlotConnectionFactory::instance().connect(slot_id, utils::createChannel({ plugin.address(), plugin.port() })); + slots::instance().connect(slot_id, utils::createChannel({ plugin.address(), plugin.port() })); } } #endif // ENABLE_PLUGINS From 21fcdae9accb1131f9e11d598d5f2355b9eb4eef Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 2 Aug 2023 12:31:45 +0200 Subject: [PATCH 275/656] Make modify component independant of plugin-proxy. part of CURA-10851 --- include/plugins/pluginproxy.h | 159 ++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 64 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index de3c9d8af6..e6483822ac 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -35,55 +35,103 @@ namespace cura::plugins { +namespace details +{ template -concept plugin_modifier_v = requires(T value) +concept plugin_modifier = requires(T value) { requires std::is_member_function_pointer_v; }; template -concept default_modifier_v = requires(T value) +concept not_plugin_modifier = requires(T value) +{ + requires ! plugin_modifier; +}; + +template +concept default_modifier = requires(T value) { - requires ! plugin_modifier_v; + requires not_plugin_modifier; requires std::is_member_function_pointer_v; }; +} // namespace details -template -class PluginProxyModifyComponent +/** + * @brief Prepares client_context for the remote call. + * + * Sets timeout for the call and adds metadata to context. + * + * @param client_context - Client context to prepare + * @param timeout - Call timeout duration (optional, default = 500ms) + */ +inline void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) { - using value_type = typename ResponseTp::native_value_type; + // Set time-out + client_context.set_deadline(std::chrono::system_clock::now() + timeout); + + // Metadata + client_context.AddMetadata("cura-engine-uuid", slot_info.engine_uuid.data()); + client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); +} +template +class PluginProxyModifyComponent +{ public: - PluginProxyModifyComponent(Parent& parent); - value_type modify(auto&&... args); + PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel); + auto modify(auto&&... args); }; -template -class PluginProxyModifyComponent +template +class PluginProxyModifyComponent { - using value_type = typename ResponseTp::native_value_type; +public: + PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) + { + } + + auto modify(auto&&... args) + { + return default_modify_(std::forward(args)...); + } + +private: + Default default_modify_; +}; +template +class PluginProxyModifyComponent +{ public: - PluginProxyModifyComponent(Parent& parent) + PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) { } - value_type modify(auto&&... args) + auto modify(auto&&... args) { - return Default(std::forward(args)...); + assert(false); // Modify on a stub which it isn't meant for (for example, broadcast), should not actually be called. + return 0; } }; -template -class PluginProxyModifyComponent +template +class PluginProxyModifyComponent { using value_type = typename ResponseTp::native_value_type; + + using req_converter_type = RequestTp; + using rsp_converter_type = ResponseTp; + using req_msg_type = typename RequestTp::value_type; using rsp_msg_type = typename ResponseTp::value_type; + using modify_stub_t = Stub; public: - PluginProxyModifyComponent(Parent& parent) - : parent_(parent) + PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) + : slot_info_{ slot_info } + , plugin_info_{ plugin_info } + , modify_stub_{ channel } { } @@ -99,7 +147,7 @@ class PluginProxyModifyComponent * * @throws std::runtime_error if communication with the plugin fails. */ - value_type modify(auto&&... args) + auto modify(auto&&... args) { agrpc::GrpcContext grpc_context; value_type ret_value{}; @@ -116,11 +164,11 @@ class PluginProxyModifyComponent if (! status.ok()) // TODO: handle different kind of status codes { - if (parent_.plugin_info_.has_value()) + if (plugin_info_.has_value()) { - throw exceptions::RemoteException(parent_.slot_info_, parent_.plugin_info_.value(), status.error_message()); + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); } - throw exceptions::RemoteException(parent_.slot_info_, status.error_message()); + throw exceptions::RemoteException(slot_info_, status.error_message()); } return ret_value; } @@ -141,19 +189,24 @@ class PluginProxyModifyComponent { using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; - parent_.prep_client_context(client_context); + prep_client_context(client_context, slot_info_); // Construct request - auto request{ parent_.req_(std::forward(args)...) }; + auto request{ req_(std::forward(args)...) }; // Make unary request rsp_msg_type response; - status = co_await RPC::request(grpc_context, parent_.modify_stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = parent_.rsp_(response); + status = co_await RPC::request(grpc_context, modify_stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = rsp_(response); co_return; } - Parent& parent_; + req_converter_type req_{}; ///< The Modify request converter object. + rsp_converter_type rsp_{}; ///< The Modify response converter object. + + slot_metadata/*&*/ slot_info_; + std::optional/*&*/ plugin_info_; + ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. }; /** @@ -164,30 +217,27 @@ class PluginProxyModifyComponent * SlotVersionRng - plugin version range * Stub - process stub type * ValidatorTp - validator type - * RequestTp - gRPC convertible request type, - * ResponseTp - gRPC convertible response type. + * RequestTp - gRPC convertible request type, or dummy -- if stub is a proper modify-stub, and not default, this is enforced by the specialization of the modify component + * ResponseTp - gRPC convertible response type, or dummy -- if stub is a proper modify-stub, and not default, this is enforced by the specialization of the modify component * * Class provides methods for validating the plugin, making requests and processing responses. */ -template +template class PluginProxy { - friend PluginProxyModifyComponent; - public: // type aliases for easy use using value_type = typename ResponseTp::native_value_type; using validator_type = ValidatorTp; - using req_msg_type = typename RequestTp::value_type; - using rsp_msg_type = typename ResponseTp::value_type; - using req_converter_type = RequestTp; using rsp_converter_type = ResponseTp; using modify_stub_t = Stub; using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; + using modify_component_t = PluginProxyModifyComponent; + /** * @brief Constructs a PluginProxy object. * @@ -202,8 +252,8 @@ class PluginProxy constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) - : modify_stub_(channel) - , broadcast_stub_(channel) + : broadcast_stub_(channel) + , modify_component_(slot_info_, plugin_info_, channel) { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -217,7 +267,7 @@ class PluginProxy { using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; grpc::ClientContext client_context{}; - prep_client_context(client_context); + prep_client_context(client_context, slot_info_); // Construct request handshake_request handshake_req; @@ -258,7 +308,7 @@ class PluginProxy if (this != &other) { valid_ = other.valid_; - modify_stub_ = other.modify_stub_; + modify_component_ = other.modify_component_; broadcast_stub_ = other.broadcast_stub_; plugin_info_ = other.plugin_info_; slot_info_ = other.slot_info_; @@ -270,7 +320,7 @@ class PluginProxy if (this != &other) { valid_ = std::move(other.valid_); - modify_stub_ = std::move(other.modify_stub_); + modify_component_ = std::move(other.modify_component_); broadcast_stub_ = std::move(other.broadcast_stub_); plugin_info_ = std::move(other.plugin_info_); slot_info_ = std::move(other.slot_info_); @@ -281,8 +331,7 @@ class PluginProxy value_type modify(auto&&... args) { - static auto modify_component = PluginProxyModifyComponent(*this); - return modify_component.modify(std::forward(args)...); + return modify_component_.modify(std::forward(args)...); } template @@ -316,10 +365,7 @@ class PluginProxy private: validator_type valid_{}; ///< The validator object for plugin validation. - req_converter_type req_{}; ///< The Modify request converter object. - rsp_converter_type rsp_{}; ///< The Modify response converter object. - ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. slot_metadata slot_info_{ .slot_id = SlotID, @@ -327,11 +373,13 @@ class PluginProxy .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake + modify_component_t modify_component_; + template boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) { grpc::ClientContext client_context{}; - prep_client_context(client_context); + prep_client_context(client_context, slot_info_); auto broadcaster{ details::broadcast_factory() }; auto request = details::broadcast_message_factory(std::forward(args)...); @@ -339,25 +387,8 @@ class PluginProxy status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); co_return; } - - /** - * @brief Prepares client_context for the remote call. - * - * Sets timeout for the call and adds metadata to context. - * - * @param client_context - Client context to prepare - * @param timeout - Call timeout duration (optional, default = 500ms) - */ - void prep_client_context(grpc::ClientContext& client_context, std::chrono::milliseconds timeout = std::chrono::milliseconds(500)) - { - // Set time-out - client_context.set_deadline(std::chrono::system_clock::now() + timeout); - - // Metadata - client_context.AddMetadata("cura-engine-uuid", slot_info_.engine_uuid.data()); - client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); - } }; + } // namespace cura::plugins #endif // PLUGINS_PLUGINPROXY_H From f431744291f2e5e94253ea7904faa7c967f30ae0 Mon Sep 17 00:00:00 2001 From: rburema Date: Wed, 2 Aug 2023 10:36:12 +0000 Subject: [PATCH 276/656] Applied clang-format. --- include/plugins/pluginproxy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index e6483822ac..d23762c1f2 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -204,8 +204,8 @@ class PluginProxyModifyComponent req_converter_type req_{}; ///< The Modify request converter object. rsp_converter_type rsp_{}; ///< The Modify response converter object. - slot_metadata/*&*/ slot_info_; - std::optional/*&*/ plugin_info_; + slot_metadata /*&*/ slot_info_; + std::optional /*&*/ plugin_info_; ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. }; From 46e45333093d4a63037a03b4d887458ea40dba53 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 2 Aug 2023 14:23:00 +0200 Subject: [PATCH 277/656] Use sample Polygon logic as Simplify converter CURA-10619 --- include/plugins/converters.h | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 3184bcf6e3..01109c9733 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -281,30 +281,26 @@ struct infill_generate_request return message; } - auto* msg_infill_areas = message.mutable_infill_areas(); - auto* msg_polygons = msg_infill_areas->mutable_polygons(); - for (auto& polygon : inner_contour.splitIntoParts()) - { - auto* msg_polygon = msg_polygons->Add(); - auto* msg_outline = msg_polygon->mutable_outline(); + auto* msg_polygons = message.mutable_infill_areas(); + auto* msg_polygon = msg_polygons->add_polygons(); + auto* msg_outline = msg_polygon->mutable_outline(); + for (const auto& point : ranges::front(inner_contour.paths)) + { auto* msg_outline_path = msg_outline->add_path(); - for (const auto& point : ranges::front(polygon)) - { - msg_outline_path->set_x(point.X); - msg_outline_path->set_y(point.Y); - } + msg_outline_path->set_x(point.X); + msg_outline_path->set_y(point.Y); + } - auto* msg_holes = msg_polygon->mutable_holes(); - for (const auto& polygon : polygon.paths | ranges::views::drop(1)) + auto* msg_holes = msg_polygon->mutable_holes(); + for (const auto& polygon : inner_contour.paths | ranges::views::drop(1)) + { + auto* msg_hole = msg_holes->Add(); + for (const auto& point : polygon) { - auto* msg_hole = msg_holes->Add(); - for (const auto& point : polygon) - { - auto* msg_path = msg_hole->add_path(); - msg_path->set_x(point.X); - msg_path->set_y(point.Y); - } + auto* msg_path = msg_hole->add_path(); + msg_path->set_x(point.X); + msg_path->set_y(point.Y); } } From 643f047d1aa2d7473443bce3d8ad82845d81e032 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 2 Aug 2023 18:03:53 +0200 Subject: [PATCH 278/656] Split broadcast-functionality component out of plugin-proxy. Also remove unclear and unused 'default' behaviour made earlier in this feature-branch. Might return to this later, but that depends on implementation of the other parts of version 0 of the engine plugins. part of CURA-10851 --- include/plugins/pluginproxy.h | 159 ++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 77 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index d23762c1f2..d79efc4a62 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -37,6 +37,7 @@ namespace cura::plugins namespace details { + template concept plugin_modifier = requires(T value) { @@ -44,17 +45,8 @@ concept plugin_modifier = requires(T value) }; template -concept not_plugin_modifier = requires(T value) -{ - requires ! plugin_modifier; -}; +concept not_plugin_modifier = ! plugin_modifier; -template -concept default_modifier = requires(T value) -{ - requires not_plugin_modifier; - requires std::is_member_function_pointer_v; -}; } // namespace details /** @@ -79,31 +71,17 @@ template class PluginProxyModifyComponent { public: + constexpr PluginProxyModifyComponent(); PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel); auto modify(auto&&... args); }; -template -class PluginProxyModifyComponent +template +class PluginProxyModifyComponent { public: - PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) - { - } - - auto modify(auto&&... args) - { - return default_modify_(std::forward(args)...); - } + constexpr PluginProxyModifyComponent() = default; -private: - Default default_modify_; -}; - -template -class PluginProxyModifyComponent -{ -public: PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) { } @@ -122,12 +100,13 @@ class PluginProxyModifyComponent using req_converter_type = RequestTp; using rsp_converter_type = ResponseTp; - using req_msg_type = typename RequestTp::value_type; using rsp_msg_type = typename ResponseTp::value_type; using modify_stub_t = Stub; public: + constexpr PluginProxyModifyComponent() = default; + PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) : slot_info_{ slot_info } , plugin_info_{ plugin_info } @@ -209,6 +188,69 @@ class PluginProxyModifyComponent ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. }; +template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. +class PluginProxyBroadcastComponent +{ + using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; + +public: + constexpr PluginProxyBroadcastComponent() = default; + + PluginProxyBroadcastComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) + : slot_info_{ slot_info } + , plugin_info_{ plugin_info } + , broadcast_stub_{ channel } + { + } + + template + void broadcast(auto&&... args) + { + if (! plugin_info_->broadcast_subscriptions.contains(Subscription)) + { + return; + } + agrpc::GrpcContext grpc_context; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &args...]() + { + return this->broadcastCall(grpc_context, status, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_.has_value()) + { + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + } + throw exceptions::RemoteException(slot_info_, status.error_message()); + } + } + +private: + template + boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) + { + grpc::ClientContext client_context{}; + prep_client_context(client_context, slot_info_); + + auto broadcaster{ details::broadcast_factory() }; + auto request = details::broadcast_message_factory(std::forward(args)...); + auto response = google::protobuf::Empty{}; + status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); + co_return; + } + + slot_metadata/*&*/ slot_info_; + std::optional/*&*/ plugin_info_; + ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. +}; + /** * @brief A plugin proxy class template. * @@ -233,10 +275,8 @@ class PluginProxy using req_converter_type = RequestTp; using rsp_converter_type = ResponseTp; - using modify_stub_t = Stub; - using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; - - using modify_component_t = PluginProxyModifyComponent; + using modify_component_t = PluginProxyModifyComponent; + using broadcast_component_t = PluginProxyBroadcastComponent; /** * @brief Constructs a PluginProxy object. @@ -252,8 +292,6 @@ class PluginProxy constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) - : broadcast_stub_(channel) - , modify_component_(slot_info_, plugin_info_, channel) { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -299,6 +337,10 @@ class PluginProxy { plugin_info_ = plugin_info; } + + // Can only do this _after_ it's sure the slot_info and plugin_info have been set properly. + broadcast_component_ = broadcast_component_t(slot_info_, plugin_info_, channel); + modify_component_ = modify_component_t(slot_info_, plugin_info_, channel); }; constexpr PluginProxy(const PluginProxy&) = default; @@ -309,7 +351,7 @@ class PluginProxy { valid_ = other.valid_; modify_component_ = other.modify_component_; - broadcast_stub_ = other.broadcast_stub_; + broadcast_component_ = other.broadcast_component_; plugin_info_ = other.plugin_info_; slot_info_ = other.slot_info_; } @@ -321,7 +363,7 @@ class PluginProxy { valid_ = std::move(other.valid_); modify_component_ = std::move(other.modify_component_); - broadcast_stub_ = std::move(other.broadcast_stub_); + broadcast_component_ = std::move(other.broadcast_component_); plugin_info_ = std::move(other.plugin_info_); slot_info_ = std::move(other.slot_info_); } @@ -334,59 +376,22 @@ class PluginProxy return modify_component_.modify(std::forward(args)...); } - template + template void broadcast(auto&&... args) { - if (! plugin_info_->broadcast_subscriptions.contains(S)) - { - return; - } - agrpc::GrpcContext grpc_context; - grpc::Status status; - - boost::asio::co_spawn( - grpc_context, - [this, &grpc_context, &status, &args...]() - { - return this->broadcastCall(grpc_context, status, std::forward(args)...); - }, - boost::asio::detached); - grpc_context.run(); - - if (! status.ok()) // TODO: handle different kind of status codes - { - if (plugin_info_.has_value()) - { - throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); - } - throw exceptions::RemoteException(slot_info_, status.error_message()); - } + return broadcast_component_.broadcast(std::forward(args)...); } private: validator_type valid_{}; ///< The validator object for plugin validation. - ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. - slot_metadata slot_info_{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake modify_component_t modify_component_; - - template - boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) - { - grpc::ClientContext client_context{}; - prep_client_context(client_context, slot_info_); - - auto broadcaster{ details::broadcast_factory() }; - auto request = details::broadcast_message_factory(std::forward(args)...); - auto response = google::protobuf::Empty{}; - status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); - co_return; - } + broadcast_component_t broadcast_component_; }; } // namespace cura::plugins From c35275197192f47796c23fc80b04c684d575f8f3 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 2 Aug 2023 19:29:25 +0200 Subject: [PATCH 279/656] Don't copy metadata all over the place. References where a bit hard to use here, as PluginProxy needs to be copied at some point, so a std::move wouldn't have worked (at least not without extra work). Used pointers instead. part of CURA-10851 --- include/plugins/pluginproxy.h | 70 +++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index d79efc4a62..c680b42711 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -47,6 +47,9 @@ concept plugin_modifier = requires(T value) template concept not_plugin_modifier = ! plugin_modifier; +using slot_info_ptr = std::shared_ptr; +using plugin_info_ptr = std::shared_ptr>; + } // namespace details /** @@ -72,7 +75,7 @@ class PluginProxyModifyComponent { public: constexpr PluginProxyModifyComponent(); - PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel); + PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel); auto modify(auto&&... args); }; @@ -82,7 +85,7 @@ class PluginProxyModifyComponent public: constexpr PluginProxyModifyComponent() = default; - PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) + PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) { } @@ -107,7 +110,7 @@ class PluginProxyModifyComponent public: constexpr PluginProxyModifyComponent() = default; - PluginProxyModifyComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) + PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) : slot_info_{ slot_info } , plugin_info_{ plugin_info } , modify_stub_{ channel } @@ -143,11 +146,11 @@ class PluginProxyModifyComponent if (! status.ok()) // TODO: handle different kind of status codes { - if (plugin_info_.has_value()) + if (plugin_info_->has_value()) { - throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); } - throw exceptions::RemoteException(slot_info_, status.error_message()); + throw exceptions::RemoteException(*slot_info_, status.error_message()); } return ret_value; } @@ -168,7 +171,7 @@ class PluginProxyModifyComponent { using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; - prep_client_context(client_context, slot_info_); + prep_client_context(client_context, *slot_info_); // Construct request auto request{ req_(std::forward(args)...) }; @@ -183,8 +186,8 @@ class PluginProxyModifyComponent req_converter_type req_{}; ///< The Modify request converter object. rsp_converter_type rsp_{}; ///< The Modify response converter object. - slot_metadata /*&*/ slot_info_; - std::optional /*&*/ plugin_info_; + details::slot_info_ptr slot_info_; + details::plugin_info_ptr plugin_info_; ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. }; @@ -196,7 +199,7 @@ class PluginProxyBroadcastComponent public: constexpr PluginProxyBroadcastComponent() = default; - PluginProxyBroadcastComponent(const slot_metadata& slot_info, const std::optional& plugin_info, std::shared_ptr channel) + PluginProxyBroadcastComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) : slot_info_{ slot_info } , plugin_info_{ plugin_info } , broadcast_stub_{ channel } @@ -206,7 +209,7 @@ class PluginProxyBroadcastComponent template void broadcast(auto&&... args) { - if (! plugin_info_->broadcast_subscriptions.contains(Subscription)) + if (! plugin_info_->value().broadcast_subscriptions.contains(Subscription)) { return; } @@ -224,11 +227,11 @@ class PluginProxyBroadcastComponent if (! status.ok()) // TODO: handle different kind of status codes { - if (plugin_info_.has_value()) + if (plugin_info_->has_value()) { - throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); } - throw exceptions::RemoteException(slot_info_, status.error_message()); + throw exceptions::RemoteException(*slot_info_, status.error_message()); } } @@ -237,7 +240,7 @@ class PluginProxyBroadcastComponent boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) { grpc::ClientContext client_context{}; - prep_client_context(client_context, slot_info_); + prep_client_context(client_context, *slot_info_); auto broadcaster{ details::broadcast_factory() }; auto request = details::broadcast_message_factory(std::forward(args)...); @@ -246,8 +249,8 @@ class PluginProxyBroadcastComponent co_return; } - slot_metadata/*&*/ slot_info_; - std::optional/*&*/ plugin_info_; + details::slot_info_ptr slot_info_; + details::plugin_info_ptr plugin_info_; ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. }; @@ -292,6 +295,8 @@ class PluginProxy constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) + : modify_component_{ slot_info_, plugin_info_, channel } + , broadcast_component_{ slot_info_, plugin_info_, channel } { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -305,21 +310,21 @@ class PluginProxy { using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; grpc::ClientContext client_context{}; - prep_client_context(client_context, slot_info_); + prep_client_context(client_context, *slot_info_); // Construct request handshake_request handshake_req; - handshake_request::value_type request{ handshake_req(slot_info_) }; + handshake_request::value_type request{ handshake_req(*slot_info_) }; // Make unary request handshake_response::value_type response; status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); handshake_response handshake_rsp; plugin_info = handshake_rsp(response, client_context.peer()); - valid_ = validator_type{ slot_info_, plugin_info_.value() }; + valid_ = validator_type{ *slot_info_, plugin_info }; if (valid_) { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_->slot_id); if (! plugin_info.broadcast_subscriptions.empty()) { spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); @@ -331,16 +336,12 @@ class PluginProxy if (! status.ok()) // TODO: handle different kind of status codes { - throw exceptions::RemoteException(slot_info_, status.error_message()); + throw exceptions::RemoteException(*slot_info_, status.error_message()); } if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) { - plugin_info_ = plugin_info; + plugin_info_->emplace(plugin_info); } - - // Can only do this _after_ it's sure the slot_info and plugin_info have been set properly. - broadcast_component_ = broadcast_component_t(slot_info_, plugin_info_, channel); - modify_component_ = modify_component_t(slot_info_, plugin_info_, channel); }; constexpr PluginProxy(const PluginProxy&) = default; @@ -385,10 +386,17 @@ class PluginProxy private: validator_type valid_{}; ///< The validator object for plugin validation. - slot_metadata slot_info_{ .slot_id = SlotID, - .version_range = SlotVersionRng.value, - .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. - std::optional plugin_info_{ std::nullopt }; ///< Optional object that holds the plugin metadata, set after handshake + details::slot_info_ptr slot_info_ ///< Holds information about the plugin slot. + { + std::make_shared( + slot_metadata + { + .slot_id = SlotID, + .version_range = SlotVersionRng.value, + .engine_uuid = Application::getInstance().instance_uuid + }) + }; + details::plugin_info_ptr plugin_info_{ std::make_shared>(std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake modify_component_t modify_component_; broadcast_component_t broadcast_component_; From ecc7ecb5c1a5698de325982648962362a84c56f4 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 2 Aug 2023 20:10:04 +0200 Subject: [PATCH 280/656] Move (plugin-proxy) components to their own files. part of CURA-10851 --- include/plugins/components/broadcast.h | 95 +++++++++++ include/plugins/components/common.h | 43 +++++ include/plugins/components/modify.h | 166 ++++++++++++++++++ include/plugins/pluginproxy.h | 222 +------------------------ 4 files changed, 307 insertions(+), 219 deletions(-) create mode 100644 include/plugins/components/broadcast.h create mode 100644 include/plugins/components/common.h create mode 100644 include/plugins/components/modify.h diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h new file mode 100644 index 0000000000..cb9fc96c09 --- /dev/null +++ b/include/plugins/components/broadcast.h @@ -0,0 +1,95 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PLUGINS_BROADCASTCOMPONENT_H +#define PLUGINS_BROADCASTCOMPONENT_H + +#include "common.h" +#include "cura/plugins/v0/slot_id.pb.h" +#include "plugins/broadcasts.h" +#include "plugins/exception.h" +#include "plugins/metadata.h" +#include "utils/format/thread_id.h" +#include "utils/types/char_range_literal.h" +#include "utils/types/generic.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cura::plugins +{ + +template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. +class PluginProxyBroadcastComponent +{ + using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; + +public: + constexpr PluginProxyBroadcastComponent() = default; + + PluginProxyBroadcastComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) + : slot_info_{ slot_info } + , plugin_info_{ plugin_info } + , broadcast_stub_{ channel } + { + } + + template + void broadcast(auto&&... args) + { + if (! plugin_info_->value().broadcast_subscriptions.contains(Subscription)) + { + return; + } + agrpc::GrpcContext grpc_context; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &args...]() + { + return this->broadcastCall(grpc_context, status, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_->has_value()) + { + throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); + } + throw exceptions::RemoteException(*slot_info_, status.error_message()); + } + } + +private: + template + boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) + { + grpc::ClientContext client_context{}; + prep_client_context(client_context, *slot_info_); + + auto broadcaster{ details::broadcast_factory() }; + auto request = details::broadcast_message_factory(std::forward(args)...); + auto response = google::protobuf::Empty{}; + status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); + co_return; + } + + details::slot_info_ptr slot_info_; + details::plugin_info_ptr plugin_info_; + ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. +}; + +} // namespace cura::plugins + +#endif // PLUGINS_BROADCASTCOMPONENT_H diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h new file mode 100644 index 0000000000..45b04d80ea --- /dev/null +++ b/include/plugins/components/common.h @@ -0,0 +1,43 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PLUGINS_COMPONENTCOMMON_H +#define PLUGINS_COMPONENTCOMMON_H + +#include "cura/plugins/v0/slot_id.pb.h" +#include "plugins/metadata.h" +#include "utils/format/thread_id.h" + +#include +#include + +namespace cura::plugins +{ + +namespace details +{ +using slot_info_ptr = std::shared_ptr; +using plugin_info_ptr = std::shared_ptr>; +} // namespace details + +/** + * @brief Prepares client_context for the remote call. + * + * Sets timeout for the call and adds metadata to context. + * + * @param client_context - Client context to prepare + * @param timeout - Call timeout duration (optional, default = 500ms) + */ +inline void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) +{ + // Set time-out + client_context.set_deadline(std::chrono::system_clock::now() + timeout); + + // Metadata + client_context.AddMetadata("cura-engine-uuid", slot_info.engine_uuid.data()); + client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); +} + +} // namespace cura::plugins + +#endif // PLUGINS_COMPONENTCOMMON_H diff --git a/include/plugins/components/modify.h b/include/plugins/components/modify.h new file mode 100644 index 0000000000..830c0596a9 --- /dev/null +++ b/include/plugins/components/modify.h @@ -0,0 +1,166 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PLUGINS_MODIFYCOMPONENT_H +#define PLUGINS_MODIFYCOMPONENT_H + +#include "common.h" +#include "cura/plugins/v0/slot_id.pb.h" +#include "plugins/broadcasts.h" +#include "plugins/exception.h" +#include "plugins/metadata.h" +#include "utils/format/thread_id.h" +#include "utils/types/char_range_literal.h" +#include "utils/types/generic.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cura::plugins +{ +namespace details +{ + +template +concept plugin_modifier = requires(T value) +{ + requires std::is_member_function_pointer_v; +}; + +template +concept not_plugin_modifier = ! plugin_modifier; + +} // namespace details + +template +class PluginProxyModifyComponent +{ +public: + constexpr PluginProxyModifyComponent(); + PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel); + auto modify(auto&&... args); +}; + +template +class PluginProxyModifyComponent +{ +public: + constexpr PluginProxyModifyComponent() = default; + + PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) + { + } + + auto modify(auto&&... args) + { + assert(false); // Modify on a stub which it isn't meant for (for example, broadcast), should not actually be called. + return 0; + } +}; + +template +class PluginProxyModifyComponent +{ + using value_type = typename ResponseTp::native_value_type; + + using req_converter_type = RequestTp; + using rsp_converter_type = ResponseTp; + using rsp_msg_type = typename ResponseTp::value_type; + + using modify_stub_t = Stub; + +public: + constexpr PluginProxyModifyComponent() = default; + + PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) + : slot_info_{ slot_info } + , plugin_info_{ plugin_info } + , modify_stub_{ channel } + { + } + + /** + * @brief Executes to plugin Modify operation. + * + * As part of this operation, a request is sent to the plugin + * and the returned response is processed. + * + * @tparam Args - argument types for the plugin request + * @param args - arguments for the plugin request + * @return The converted response value from plugin. + * + * @throws std::runtime_error if communication with the plugin fails. + */ + auto modify(auto&&... args) + { + agrpc::GrpcContext grpc_context; + value_type ret_value{}; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &ret_value, &args...]() + { + return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_->has_value()) + { + throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); + } + throw exceptions::RemoteException(*slot_info_, status.error_message()); + } + return ret_value; + } + +private: + /** + * @brief Executes the modifyCall operation with the plugin. + * + * Sends a request to the plugin and saves the response. + * + * @param grpc_context - The gRPC context to use for the call + * @param status - Status of the gRPC call which gets updated in this method + * @param ret_value - Reference to the value in which response to be stored + * @param args - Request arguments + * @return A boost::asio::awaitable indicating completion of the operation + */ + boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) + { + using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context, *slot_info_); + + // Construct request + auto request{ req_(std::forward(args)...) }; + + // Make unary request + rsp_msg_type response; + status = co_await RPC::request(grpc_context, modify_stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = rsp_(response); + co_return; + } + + req_converter_type req_{}; ///< The Modify request converter object. + rsp_converter_type rsp_{}; ///< The Modify response converter object. + + details::slot_info_ptr slot_info_; + details::plugin_info_ptr plugin_info_; + ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. +}; + +} // namespace cura::plugins + +#endif // PLUGINS_MODIFYCOMPONENT_H diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index c680b42711..370ae8bba1 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -5,6 +5,9 @@ #define PLUGINS_PLUGINPROXY_H #include "Application.h" +#include "components/common.h" +#include "components/broadcast.h" +#include "components/modify.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" @@ -35,225 +38,6 @@ namespace cura::plugins { -namespace details -{ - -template -concept plugin_modifier = requires(T value) -{ - requires std::is_member_function_pointer_v; -}; - -template -concept not_plugin_modifier = ! plugin_modifier; - -using slot_info_ptr = std::shared_ptr; -using plugin_info_ptr = std::shared_ptr>; - -} // namespace details - -/** - * @brief Prepares client_context for the remote call. - * - * Sets timeout for the call and adds metadata to context. - * - * @param client_context - Client context to prepare - * @param timeout - Call timeout duration (optional, default = 500ms) - */ -inline void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) -{ - // Set time-out - client_context.set_deadline(std::chrono::system_clock::now() + timeout); - - // Metadata - client_context.AddMetadata("cura-engine-uuid", slot_info.engine_uuid.data()); - client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); -} - -template -class PluginProxyModifyComponent -{ -public: - constexpr PluginProxyModifyComponent(); - PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel); - auto modify(auto&&... args); -}; - -template -class PluginProxyModifyComponent -{ -public: - constexpr PluginProxyModifyComponent() = default; - - PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) - { - } - - auto modify(auto&&... args) - { - assert(false); // Modify on a stub which it isn't meant for (for example, broadcast), should not actually be called. - return 0; - } -}; - -template -class PluginProxyModifyComponent -{ - using value_type = typename ResponseTp::native_value_type; - - using req_converter_type = RequestTp; - using rsp_converter_type = ResponseTp; - using rsp_msg_type = typename ResponseTp::value_type; - - using modify_stub_t = Stub; - -public: - constexpr PluginProxyModifyComponent() = default; - - PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) - : slot_info_{ slot_info } - , plugin_info_{ plugin_info } - , modify_stub_{ channel } - { - } - - /** - * @brief Executes to plugin Modify operation. - * - * As part of this operation, a request is sent to the plugin - * and the returned response is processed. - * - * @tparam Args - argument types for the plugin request - * @param args - arguments for the plugin request - * @return The converted response value from plugin. - * - * @throws std::runtime_error if communication with the plugin fails. - */ - auto modify(auto&&... args) - { - agrpc::GrpcContext grpc_context; - value_type ret_value{}; - grpc::Status status; - - boost::asio::co_spawn( - grpc_context, - [this, &grpc_context, &status, &ret_value, &args...]() - { - return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); - }, - boost::asio::detached); - grpc_context.run(); - - if (! status.ok()) // TODO: handle different kind of status codes - { - if (plugin_info_->has_value()) - { - throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); - } - throw exceptions::RemoteException(*slot_info_, status.error_message()); - } - return ret_value; - } - -private: - /** - * @brief Executes the modifyCall operation with the plugin. - * - * Sends a request to the plugin and saves the response. - * - * @param grpc_context - The gRPC context to use for the call - * @param status - Status of the gRPC call which gets updated in this method - * @param ret_value - Reference to the value in which response to be stored - * @param args - Request arguments - * @return A boost::asio::awaitable indicating completion of the operation - */ - boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) - { - using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context, *slot_info_); - - // Construct request - auto request{ req_(std::forward(args)...) }; - - // Make unary request - rsp_msg_type response; - status = co_await RPC::request(grpc_context, modify_stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = rsp_(response); - co_return; - } - - req_converter_type req_{}; ///< The Modify request converter object. - rsp_converter_type rsp_{}; ///< The Modify response converter object. - - details::slot_info_ptr slot_info_; - details::plugin_info_ptr plugin_info_; - ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. -}; - -template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. -class PluginProxyBroadcastComponent -{ - using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; - -public: - constexpr PluginProxyBroadcastComponent() = default; - - PluginProxyBroadcastComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) - : slot_info_{ slot_info } - , plugin_info_{ plugin_info } - , broadcast_stub_{ channel } - { - } - - template - void broadcast(auto&&... args) - { - if (! plugin_info_->value().broadcast_subscriptions.contains(Subscription)) - { - return; - } - agrpc::GrpcContext grpc_context; - grpc::Status status; - - boost::asio::co_spawn( - grpc_context, - [this, &grpc_context, &status, &args...]() - { - return this->broadcastCall(grpc_context, status, std::forward(args)...); - }, - boost::asio::detached); - grpc_context.run(); - - if (! status.ok()) // TODO: handle different kind of status codes - { - if (plugin_info_->has_value()) - { - throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); - } - throw exceptions::RemoteException(*slot_info_, status.error_message()); - } - } - -private: - template - boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) - { - grpc::ClientContext client_context{}; - prep_client_context(client_context, *slot_info_); - - auto broadcaster{ details::broadcast_factory() }; - auto request = details::broadcast_message_factory(std::forward(args)...); - auto response = google::protobuf::Empty{}; - status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); - co_return; - } - - details::slot_info_ptr slot_info_; - details::plugin_info_ptr plugin_info_; - ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. -}; - /** * @brief A plugin proxy class template. * From bde1cbfae7777dd066a8ff85c8e6e205e6b38cee Mon Sep 17 00:00:00 2001 From: rburema Date: Wed, 2 Aug 2023 18:16:46 +0000 Subject: [PATCH 281/656] Applied clang-format. --- include/plugins/components/broadcast.h | 2 +- include/plugins/pluginproxy.h | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index cb9fc96c09..57720a61cc 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -27,7 +27,7 @@ namespace cura::plugins { -template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. +template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. class PluginProxyBroadcastComponent { using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 370ae8bba1..7a6fd15376 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -5,8 +5,8 @@ #define PLUGINS_PLUGINPROXY_H #include "Application.h" -#include "components/common.h" #include "components/broadcast.h" +#include "components/common.h" #include "components/modify.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" @@ -171,16 +171,9 @@ class PluginProxy validator_type valid_{}; ///< The validator object for plugin validation. details::slot_info_ptr slot_info_ ///< Holds information about the plugin slot. - { - std::make_shared( - slot_metadata - { - .slot_id = SlotID, - .version_range = SlotVersionRng.value, - .engine_uuid = Application::getInstance().instance_uuid - }) - }; - details::plugin_info_ptr plugin_info_{ std::make_shared>(std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake + { std::make_shared(slot_metadata{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }) }; + details::plugin_info_ptr plugin_info_{ std::make_shared>( + std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake modify_component_t modify_component_; broadcast_component_t broadcast_component_; From c725c49274c744b08c939ab0a7bc3a6f58f194fd Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 3 Aug 2023 08:25:03 +0200 Subject: [PATCH 282/656] Make it work on Linux. done as part of CURA-10851 --- include/plugins/pluginproxy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 7a6fd15376..cb8b246e13 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -164,7 +164,7 @@ class PluginProxy template void broadcast(auto&&... args) { - return broadcast_component_.broadcast(std::forward(args)...); + return broadcast_component_.template broadcast(std::forward(args)...); } private: From 8b08b21e42599f81ac09816fd8237132aa589b54 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 3 Aug 2023 09:00:43 +0200 Subject: [PATCH 283/656] Rename modify to invoke as it also (will) handle(s) generate. part of CURA-10851 and CURA-10619 --- .../plugins/components/{modify.h => invoke.h} | 62 +++++++++---------- include/plugins/pluginproxy.h | 18 +++--- 2 files changed, 40 insertions(+), 40 deletions(-) rename include/plugins/components/{modify.h => invoke.h} (67%) diff --git a/include/plugins/components/modify.h b/include/plugins/components/invoke.h similarity index 67% rename from include/plugins/components/modify.h rename to include/plugins/components/invoke.h index 830c0596a9..616f33c087 100644 --- a/include/plugins/components/modify.h +++ b/include/plugins/components/invoke.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef PLUGINS_MODIFYCOMPONENT_H -#define PLUGINS_MODIFYCOMPONENT_H +#ifndef PLUGINS_INVOKECOMPONENT_H +#define PLUGINS_INVOKECOMPONENT_H #include "common.h" #include "cura/plugins/v0/slot_id.pb.h" @@ -30,44 +30,44 @@ namespace details { template -concept plugin_modifier = requires(T value) +concept plugin_invoker = requires(T value) { requires std::is_member_function_pointer_v; }; template -concept not_plugin_modifier = ! plugin_modifier; +concept not_plugin_invoker = ! plugin_invoker; } // namespace details template -class PluginProxyModifyComponent +class PluginProxyInvokeComponent { public: - constexpr PluginProxyModifyComponent(); - PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel); - auto modify(auto&&... args); + constexpr PluginProxyInvokeComponent(); + PluginProxyInvokeComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel); + auto invoke(auto&&... args); }; -template -class PluginProxyModifyComponent +template +class PluginProxyInvokeComponent { public: - constexpr PluginProxyModifyComponent() = default; + constexpr PluginProxyInvokeComponent() = default; - PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) + PluginProxyInvokeComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) { } - auto modify(auto&&... args) + auto invoke(auto&&... args) { - assert(false); // Modify on a stub which it isn't meant for (for example, broadcast), should not actually be called. + assert(false); // Invoke called on a stub which it isn't meant for (for example, broadcast), should not actually be called. return 0; } }; -template -class PluginProxyModifyComponent +template +class PluginProxyInvokeComponent { using value_type = typename ResponseTp::native_value_type; @@ -75,20 +75,20 @@ class PluginProxyModifyComponent using rsp_converter_type = ResponseTp; using rsp_msg_type = typename ResponseTp::value_type; - using modify_stub_t = Stub; + using invoke_stub_t = Stub; public: - constexpr PluginProxyModifyComponent() = default; + constexpr PluginProxyInvokeComponent() = default; - PluginProxyModifyComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) + PluginProxyInvokeComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) : slot_info_{ slot_info } , plugin_info_{ plugin_info } - , modify_stub_{ channel } + , invoke_stub_{ channel } { } /** - * @brief Executes to plugin Modify operation. + * @brief Executes to plugin Invoke (modify/generate) operation. * * As part of this operation, a request is sent to the plugin * and the returned response is processed. @@ -99,7 +99,7 @@ class PluginProxyModifyComponent * * @throws std::runtime_error if communication with the plugin fails. */ - auto modify(auto&&... args) + auto invoke(auto&&... args) { agrpc::GrpcContext grpc_context; value_type ret_value{}; @@ -109,7 +109,7 @@ class PluginProxyModifyComponent grpc_context, [this, &grpc_context, &status, &ret_value, &args...]() { - return this->modifyCall(grpc_context, status, ret_value, std::forward(args)...); + return this->invokeCall(grpc_context, status, ret_value, std::forward(args)...); }, boost::asio::detached); grpc_context.run(); @@ -127,7 +127,7 @@ class PluginProxyModifyComponent private: /** - * @brief Executes the modifyCall operation with the plugin. + * @brief Executes the invokeCall operation with the plugin. * * Sends a request to the plugin and saves the response. * @@ -137,9 +137,9 @@ class PluginProxyModifyComponent * @param args - Request arguments * @return A boost::asio::awaitable indicating completion of the operation */ - boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) + boost::asio::awaitable invokeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) { - using RPC = agrpc::RPC<&modify_stub_t::PrepareAsyncCall>; + using RPC = agrpc::RPC<&invoke_stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; prep_client_context(client_context, *slot_info_); @@ -148,19 +148,19 @@ class PluginProxyModifyComponent // Make unary request rsp_msg_type response; - status = co_await RPC::request(grpc_context, modify_stub_, client_context, request, response, boost::asio::use_awaitable); + status = co_await RPC::request(grpc_context, invoke_stub_, client_context, request, response, boost::asio::use_awaitable); ret_value = rsp_(response); co_return; } - req_converter_type req_{}; ///< The Modify request converter object. - rsp_converter_type rsp_{}; ///< The Modify response converter object. + req_converter_type req_{}; ///< The Invoke request converter object. + rsp_converter_type rsp_{}; ///< The Invoke response converter object. details::slot_info_ptr slot_info_; details::plugin_info_ptr plugin_info_; - ranges::semiregular_box modify_stub_; ///< The gRPC Modify stub for communication. + ranges::semiregular_box invoke_stub_; ///< The gRPC Invoke stub for communication. }; } // namespace cura::plugins -#endif // PLUGINS_MODIFYCOMPONENT_H +#endif // PLUGINS_INVOKECOMPONENT_H diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index b9fde6a3a5..2b6c7e5b38 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -7,7 +7,7 @@ #include "Application.h" #include "components/broadcast.h" #include "components/common.h" -#include "components/modify.h" +#include "components/invoke.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" @@ -46,8 +46,8 @@ namespace cura::plugins * SlotVersionRng - plugin version range * Stub - process stub type * ValidatorTp - validator type - * RequestTp - gRPC convertible request type, or dummy -- if stub is a proper modify-stub, and not default, this is enforced by the specialization of the modify component - * ResponseTp - gRPC convertible response type, or dummy -- if stub is a proper modify-stub, and not default, this is enforced by the specialization of the modify component + * RequestTp - gRPC convertible request type, or dummy -- if stub is a proper invoke-stub, this is enforced by the specialization of the invoke component + * ResponseTp - gRPC convertible response type, or dummy -- if stub is a proper invoke-stub, this is enforced by the specialization of the invoke component * * Class provides methods for validating the plugin, making requests and processing responses. */ @@ -62,7 +62,7 @@ class PluginProxy using req_converter_type = RequestTp; using rsp_converter_type = ResponseTp; - using modify_component_t = PluginProxyModifyComponent; + using invoke_component_t = PluginProxyInvokeComponent; using broadcast_component_t = PluginProxyBroadcastComponent; /** @@ -79,7 +79,7 @@ class PluginProxy constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) - : modify_component_{ slot_info_, plugin_info_, channel } + : invoke_component_{ slot_info_, plugin_info_, channel } , broadcast_component_{ slot_info_, plugin_info_, channel } { // Connect to the plugin and exchange a handshake @@ -135,7 +135,7 @@ class PluginProxy if (this != &other) { valid_ = other.valid_; - modify_component_ = other.modify_component_; + invoke_component_ = other.invoke_component_; broadcast_component_ = other.broadcast_component_; plugin_info_ = other.plugin_info_; slot_info_ = other.slot_info_; @@ -147,7 +147,7 @@ class PluginProxy if (this != &other) { valid_ = std::move(other.valid_); - modify_component_ = std::move(other.modify_component_); + invoke_component_ = std::move(other.invoke_component_); broadcast_component_ = std::move(other.broadcast_component_); plugin_info_ = std::move(other.plugin_info_); slot_info_ = std::move(other.slot_info_); @@ -158,7 +158,7 @@ class PluginProxy value_type invoke(auto&&... args) { - return modify_component_.modify(std::forward(args)...); + return invoke_component_.invoke(std::forward(args)...); } template @@ -175,7 +175,7 @@ class PluginProxy details::plugin_info_ptr plugin_info_{ std::make_shared>( std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake - modify_component_t modify_component_; + invoke_component_t invoke_component_; broadcast_component_t broadcast_component_; }; From 595ef2bf508b228c0f000fffac4f2fbe12a5c59d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 3 Aug 2023 12:43:30 +0200 Subject: [PATCH 284/656] skirt printed before support The two printings are scheduled one after the other CURA-10758 --- src/FffGcodeWriter.cpp | 70 +++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1ae71f4c0f..4f9ad12734 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -385,13 +385,13 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) start_extruder_nr = skirt_brim_extruder->extruder_nr; } else if ((adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) - && skirt_brim_extruder - && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) + && skirt_brim_extruder + && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } else if (adhesion_type == EPlatformAdhesion::RAFT - && skirt_brim_extruder + && skirt_brim_extruder ) { start_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; @@ -1050,18 +1050,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan total_line_count += line.open_polylines.size(); } Polygons all_brim_lines; - - // Add the support brim before the below algorithm which takes order requirements into account - // For support brim we don't care about the order, because support doesn't need to be accurate. - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) - { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - all_brim_lines = support_brim_lines; - } - + all_brim_lines.reserve(total_line_count); const coord_t line_w = train.settings.get("skirt_brim_line_width") * train.settings.get("initial_layer_line_width_factor"); @@ -1145,7 +1134,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan } } assert(all_brim_lines.size() == total_line_count); // Otherwise pointers would have gotten invalidated - + const bool enable_travel_optimization = true; // Use the combing outline while deciding in which order to print the lines. Can't hurt for only one layer. const coord_t wipe_dist = 0u; const Ratio flow_ratio = 1.0; @@ -1157,18 +1146,43 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan inner_brim_line.add(all_brim_lines[0]); gcode_layer.addLinesByOptimizer - ( - layer_nr == 0 ? all_brim_lines : inner_brim_line, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements - ); + ( + layer_nr == 0 ? all_brim_lines : inner_brim_line, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements + ); + + // Add the support brim after the skirt_brim to gcode_layer + // For support brim we don't care about the order, because support doesn't need to be accurate. + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + { + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + all_brim_lines = support_brim_lines; + } + + gcode_layer.addLinesByOptimizer + ( + layer_nr == 0 ? all_brim_lines : inner_brim_line, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements={} + ); } void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const From 8c55c3f83181905af453cd57812c69bac5132702 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Thu, 3 Aug 2023 10:44:19 +0000 Subject: [PATCH 285/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 1288 +++++++++++++++++++++++++--------------- 1 file changed, 822 insertions(+), 466 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 4f9ad12734..da7a3c1dc6 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1,20 +1,10 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include // numeric_limits -#include -#include -#include - -#include //For generating a UUID. -#include //For generating a UUID. -#include -#include +#include "FffGcodeWriter.h" #include "Application.h" #include "ExtruderTrain.h" -#include "FffGcodeWriter.h" #include "FffProcessor.h" #include "InsetOrderOptimizer.h" #include "LayerPlan.h" @@ -31,10 +21,24 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include +#include + +#include +#include //For generating a UUID. +#include //For generating a UUID. +#include // numeric_limits +#include +#include +#include + namespace cura { -FffGcodeWriter::FffGcodeWriter() : max_object_height(0), layer_plan_buffer(gcode), slice_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) +FffGcodeWriter::FffGcodeWriter() + : max_object_height(0) + , layer_plan_buffer(gcode) + , slice_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) { for (unsigned int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++) { // initialize all as max layer_nr, so that they get updated to the lowest layer on which they are used. @@ -91,7 +95,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep { auto should_prime_extruder = gcode.initializeExtruderTrains(storage, start_extruder_nr); - if (!should_prime_extruder) + if (! should_prime_extruder) { // set to most negative number so that layer processing never primes this extruder anymore. extruder_prime_layer_nr[start_extruder_nr] = std::numeric_limits::min(); @@ -159,7 +163,10 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep run_multiple_producers_ordered_consumer( process_layer_starting_layer_nr, total_layers, - [&storage, total_layers, this](int layer_nr) { return &processLayer(storage, layer_nr, total_layers); }, + [&storage, total_layers, this](int layer_nr) + { + return &processLayer(storage, layer_nr, total_layers); + }, [this, total_layers](LayerPlan* gcode_layer) { Progress::messageProgress(Progress::Stage::EXPORT, std::max(0, gcode_layer->getLayerNr()) + 1, total_layers); @@ -328,7 +335,8 @@ static void retractionAndWipeConfigFromSettings(const Settings& settings, Retrac switch_retraction_config.primeSpeed = settings.get("switch_extruder_prime_speed"); switch_retraction_config.zHop = settings.get("retraction_hop_after_extruder_switch_height"); switch_retraction_config.retraction_min_travel_distance = 0; // No limitation on travel distance for an extruder switch retract. - switch_retraction_config.retraction_extrusion_window = 99999.9; // So that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions). + switch_retraction_config.retraction_extrusion_window + = 99999.9; // So that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions). switch_retraction_config.retraction_count_max = 9999999; // Extruder switch retraction is never limited. WipeScriptConfig& wipe_config = config->wipe_config; @@ -364,7 +372,7 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for(SliceMeshStorage& mesh: storage.meshes) + for (SliceMeshStorage& mesh : storage.meshes) { retractionAndWipeConfigFromSettings(mesh.settings, &mesh.retraction_wipe_config); } @@ -375,24 +383,21 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const EPlatformAdhesion adhesion_type = mesh_group_settings.get("adhesion_type"); const int skirt_brim_extruder_nr = mesh_group_settings.get("skirt_brim_extruder_nr"); - const ExtruderTrain* skirt_brim_extruder = (skirt_brim_extruder_nr < 0)? nullptr : &mesh_group_settings.get("skirt_brim_extruder_nr"); + const ExtruderTrain* skirt_brim_extruder = (skirt_brim_extruder_nr < 0) ? nullptr : &mesh_group_settings.get("skirt_brim_extruder_nr"); size_t start_extruder_nr; - if (adhesion_type == EPlatformAdhesion::SKIRT - && skirt_brim_extruder + if (adhesion_type == EPlatformAdhesion::SKIRT && skirt_brim_extruder && (skirt_brim_extruder->settings.get("skirt_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } - else if ((adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) - && skirt_brim_extruder - && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) + else if ( + (adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) && skirt_brim_extruder + && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } - else if (adhesion_type == EPlatformAdhesion::RAFT - && skirt_brim_extruder - ) + else if (adhesion_type == EPlatformAdhesion::RAFT && skirt_brim_extruder) { start_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; } @@ -484,7 +489,8 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) storage.support.support_infill_angles_layer_0.push_back(0); } - auto getInterfaceAngles = [&storage](const ExtruderTrain& extruder, const std::string& interface_angles_setting, const EFillMethod pattern, const std::string& interface_height_setting) + auto getInterfaceAngles + = [&storage](const ExtruderTrain& extruder, const std::string& interface_angles_setting, const EFillMethod pattern, const std::string& interface_height_setting) { std::vector angles = extruder.settings.get>(interface_angles_setting); if (angles.empty()) @@ -501,7 +507,8 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) { for (const SliceMeshStorage& mesh : storage.meshes) { - if (mesh.settings.get(interface_height_setting) >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) + if (mesh.settings.get(interface_height_setting) + >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) { // Some roofs are quite thick. // Alternate between the two kinds of diagonal: / and \ . @@ -519,10 +526,12 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) }; const ExtruderTrain& roof_extruder = mesh_group_settings.get("support_roof_extruder_nr"); - storage.support.support_roof_angles = getInterfaceAngles(roof_extruder, "support_roof_angles", roof_extruder.settings.get("support_roof_pattern"), "support_roof_height"); + storage.support.support_roof_angles + = getInterfaceAngles(roof_extruder, "support_roof_angles", roof_extruder.settings.get("support_roof_pattern"), "support_roof_height"); const ExtruderTrain& bottom_extruder = mesh_group_settings.get("support_bottom_extruder_nr"); - storage.support.support_bottom_angles = getInterfaceAngles(bottom_extruder, "support_bottom_angles", bottom_extruder.settings.get("support_bottom_pattern"), "support_bottom_height"); + storage.support.support_bottom_angles + = getInterfaceAngles(bottom_extruder, "support_bottom_angles", bottom_extruder.settings.get("support_bottom_pattern"), "support_bottom_height"); } void FffGcodeWriter::processNextMeshGroupCode(const SliceDataStorage& storage) @@ -570,7 +579,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) z += layer_height; const coord_t comb_offset = base_settings.get("raft_base_line_spacing"); - std::vector fan_speed_layer_time_settings_per_extruder_raft_base = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_base + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_base) { double regular_fan_speed = base_settings.get("raft_base_fan_speed") * 100.0; @@ -580,7 +590,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const coord_t line_width = base_settings.get("raft_base_line_width"); const coord_t avoid_distance = base_settings.get("travel_avoid_distance"); - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); + LayerPlan& gcode_layer + = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); gcode_layer.setIsInside(true); gcode_layer.setExtruder(base_extruder_nr); @@ -616,29 +627,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::LINES, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - gcode_layer.configs_storage.raft_base_config.getLineWidth(), - line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::LINES, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + gcode_layer.configs_storage.raft_base_config.getLineWidth(), + line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); if (! raft_paths.empty()) @@ -646,7 +658,22 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, base_settings, base_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, base_extruder_nr, base_extruder_nr, z_seam_config, raft_paths); + *this, + storage, + gcode_layer, + base_settings, + base_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + base_extruder_nr, + base_extruder_nr, + z_seam_config, + raft_paths); wall_orderer.addToLayer(); } gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); @@ -672,7 +699,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const LayerIndex layer_nr = initial_raft_layer_nr + raft_interface_layer; z += interface_layer_height; - std::vector fan_speed_layer_time_settings_per_extruder_raft_interface = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_interface + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_interface) { const double regular_fan_speed = interface_fan_speed * 100.0; @@ -681,7 +709,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } const coord_t comb_offset = interface_line_spacing; - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, interface_layer_height, current_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_interface, comb_offset, interface_line_width, interface_avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + interface_layer_height, + current_extruder_nr, + fan_speed_layer_time_settings_per_extruder_raft_interface, + comb_offset, + interface_line_width, + interface_avoid_distance); gcode_layer.setIsInside(true); current_extruder_nr = interface_extruder_nr; @@ -689,7 +726,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, interface_layer_height); std::vector raft_outline_paths; - const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. + const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() + / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. if (storage.primeRaftOutline.area() > 0) { raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); @@ -715,29 +753,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - interface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - interface_max_resolution, - interface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + interface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + interface_max_resolution, + interface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); @@ -763,7 +802,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const LayerIndex layer_nr = initial_raft_layer_nr + 1 + num_interface_layers + raft_surface_layer - 1; // +1: 1 base layer z += surface_layer_height; - std::vector fan_speed_layer_time_settings_per_extruder_raft_surface = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_surface + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_surface) { const double regular_fan_speed = surface_fan_speed * 100.0; @@ -772,7 +812,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } const coord_t comb_offset = surface_line_spacing; - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, surface_layer_height, current_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_surface, comb_offset, surface_line_width, surface_avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + surface_layer_height, + current_extruder_nr, + fan_speed_layer_time_settings_per_extruder_raft_surface, + comb_offset, + surface_line_width, + surface_avoid_distance); gcode_layer.setIsInside(true); // make sure that we are using the correct extruder to print raft @@ -781,7 +830,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, surface_layer_height); std::vector raft_outline_paths; - const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. + const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() + / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. if (storage.primeRaftOutline.area() > 0) { raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); @@ -791,7 +841,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); Polygons raft_lines; - AngleDegrees fill_angle = (num_surface_layers - raft_surface_layer) % 2 ? 45 : 135; // Alternate between -45 and +45 degrees, ending up 90 degrees rotated from the default skin angle. + AngleDegrees fill_angle + = (num_surface_layers - raft_surface_layer) % 2 ? 45 : 135; // Alternate between -45 and +45 degrees, ending up 90 degrees rotated from the default skin angle. constexpr bool zig_zaggify_infill = true; constexpr size_t wall_line_count = 0; @@ -807,29 +858,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - surface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - surface_max_resolution, - surface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + surface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + surface_max_resolution, + surface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); @@ -865,8 +917,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn // find printZ of first actual printed mesh for (const SliceMeshStorage& mesh : storage.meshes) { - if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("cutting_mesh") - || mesh.settings.get("infill_mesh")) + if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") + || mesh.settings.get("cutting_mesh") || mesh.settings.get("infill_mesh")) { continue; } @@ -911,11 +963,22 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } const coord_t comb_offset_from_outlines = max_inner_wall_width * 2; - assert(static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); - const std::vector& extruder_order = (layer_nr < 0) ? extruder_order_per_layer_negative_layers[extruder_order_per_layer_negative_layers.size() + layer_nr] : extruder_order_per_layer[layer_nr]; + assert( + static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); + const std::vector& extruder_order + = (layer_nr < 0) ? extruder_order_per_layer_negative_layers[extruder_order_per_layer_negative_layers.size() + layer_nr] : extruder_order_per_layer[layer_nr]; const coord_t first_outer_wall_line_width = scene.extruders[extruder_order.front()].settings.get("wall_line_width_0"); - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, layer_thickness, extruder_order.front(), fan_speed_layer_time_settings_per_extruder, comb_offset_from_outlines, first_outer_wall_line_width, avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + layer_thickness, + extruder_order.front(), + fan_speed_layer_time_settings_per_extruder, + comb_offset_from_outlines, + first_outer_wall_line_width, + avoid_distance); if (include_helper_parts) { @@ -933,7 +996,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - const size_t support_infill_extruder_nr = (layer_nr <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + const size_t support_infill_extruder_nr = (layer_nr <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; for (const size_t& extruder_nr : extruder_order) { @@ -961,7 +1025,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; const PathConfigStorage::MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; if (mesh.settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE - && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! + && extruder_nr + == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! ) { addMeshLayerToGCode_meshSurfaceMode(storage, mesh, mesh_config, gcode_layer); @@ -1007,7 +1072,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const int skirt_height = train.settings.get("skirt_height"); const bool is_skirt = train.settings.get("adhesion_type") == EPlatformAdhesion::SKIRT; // only create a multilayer SkirtBrim for a skirt for the height of skirt_height - if (layer_nr != 0 && (layer_nr >= skirt_height || !is_skirt)) + if (layer_nr != 0 && (layer_nr >= skirt_height || ! is_skirt)) { return; } @@ -1016,7 +1081,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan return; } gcode_layer.setSkirtBrimIsPlanned(extruder_nr); - + const auto& original_skirt_brim = storage.skirt_brim[extruder_nr]; if (original_skirt_brim.size() == 0) { @@ -1042,7 +1107,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const size_t inset_idx; ConstPolygonPointer poly; }; - + size_t total_line_count = 0; for (const SkirtBrimLine& line : storage.skirt_brim[extruder_nr]) { @@ -1080,7 +1145,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan ConstPolygonPointer pp(all_brim_lines.back()); for (Point p : line) { - grid.insert(p, BrimLineReference{inset_idx, pp}); + grid.insert(p, BrimLineReference{ inset_idx, pp }); } } } @@ -1145,19 +1210,17 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan Polygons inner_brim_line; inner_brim_line.add(all_brim_lines[0]); - gcode_layer.addLinesByOptimizer - ( - layer_nr == 0 ? all_brim_lines : inner_brim_line, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements - ); + gcode_layer.addLinesByOptimizer( + layer_nr == 0 ? all_brim_lines : inner_brim_line, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements); // Add the support brim after the skirt_brim to gcode_layer // For support brim we don't care about the order, because support doesn't need to be accurate. @@ -1170,19 +1233,17 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan all_brim_lines = support_brim_lines; } - gcode_layer.addLinesByOptimizer - ( - layer_nr == 0 ? all_brim_lines : inner_brim_line, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements={} - ); + gcode_layer.addLinesByOptimizer( + layer_nr == 0 ? all_brim_lines : inner_brim_line, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); } void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const @@ -1341,7 +1402,11 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s return ret; } -void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { @@ -1364,7 +1429,11 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& polygons = Simplify(mesh.settings).polygon(polygons); - ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); const bool spiralize = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("magic_spiralize"); gcode_layer.addPolygonsByOptimizer(polygons, mesh_config.inset0_config, z_seam_config, mesh.settings.get("wall_0_wipe_dist"), spiralize); @@ -1378,7 +1447,12 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, c gcode_layer.addLinesByOptimizer(layer->openPolyLines, mesh_config.inset0_config, SpaceFillType::PolyLines); } -void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { @@ -1402,7 +1476,11 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const ZSeamConfig z_seam_config; if (mesh.isPrinted()) //"normal" meshes with walls, skin, infill, etc. get the traditional part ordering based on the z-seam settings. { - z_seam_config = ZSeamConfig(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + z_seam_config = ZSeamConfig( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); } PathOrderOptimizer part_order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config); for (const SliceLayerPart& part : layer.parts) @@ -1427,8 +1505,13 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const gcode_layer.setMesh(nullptr); } -void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) - const +void FffGcodeWriter::addMeshPartToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + LayerPlan& gcode_layer) const { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; @@ -1449,7 +1532,8 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S added_something = added_something | processSkin(storage, gcode_layer, mesh, extruder_nr, mesh_config, part); // After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. - if (added_something && (! mesh_group_settings.get("magic_spiralize") || gcode_layer.getLayerNr() < static_cast(mesh.settings.get("initial_bottom_layers")))) + if (added_something + && (! mesh_group_settings.get("magic_spiralize") || gcode_layer.getLayerNr() < static_cast(mesh.settings.get("initial_bottom_layers")))) { coord_t innermost_wall_line_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (gcode_layer.getLayerNr() == 0) @@ -1462,7 +1546,13 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S gcode_layer.setIsInside(false); } -bool FffGcodeWriter::processInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1473,8 +1563,13 @@ bool FffGcodeWriter::processInfill(const SliceDataStorage& storage, LayerPlan& g return added_something; } -bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) - const +bool FffGcodeWriter::processMultiLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1490,7 +1585,8 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La AngleDegrees infill_angle = 45; // Original default. This will get updated to an element from mesh->infill_angles. if (! mesh.infill_angles.empty()) { - const size_t combined_infill_layers = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); + const size_t combined_infill_layers + = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); infill_angle = mesh.infill_angles.at((gcode_layer.getLayerNr() / combined_infill_layers) % mesh.infill_angles.size()); } const Point3 mesh_middle = mesh.bounding_box.getMiddle(); @@ -1533,30 +1629,40 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La { lightning_layer = &mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr()); } - Infill infill_comp(infill_pattern, - zig_zaggify_infill, - connect_polygons, - part.infill_area_per_combine_per_density[density_idx][combine_idx], - infill_line_width, - infill_line_distance_here, - infill_overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - mesh.settings.get("cross_infill_pocket_size")); - infill_comp.generate(infill_paths, infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + infill_pattern, + zig_zaggify_infill, + connect_polygons, + part.infill_area_per_combine_per_density[density_idx][combine_idx], + infill_line_width, + infill_line_distance_here, + infill_overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + mesh.settings.get("cross_infill_pocket_size")); + infill_comp.generate( + infill_paths, + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); } if (! infill_lines.empty() || ! infill_polygons.empty()) { @@ -1580,25 +1686,27 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); - gcode_layer.addLinesByOptimizer(infill_lines, - mesh_config.infill_config[combine_idx], - zig_zaggify_infill ? SpaceFillType::PolyLines : SpaceFillType::Lines, - enable_travel_optimization, - /*wipe_dist = */ 0, - /* flow = */ 1.0, - near_start_location); + gcode_layer.addLinesByOptimizer( + infill_lines, + mesh_config.infill_config[combine_idx], + zig_zaggify_infill ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + /*wipe_dist = */ 0, + /* flow = */ 1.0, + near_start_location); } } } return added_something; } -bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SliceLayerPart& part) const +bool FffGcodeWriter::processSingleLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1629,7 +1737,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, AngleDegrees infill_angle = 45; // Original default. This will get updated to an element from mesh->infill_angles. if (! mesh.infill_angles.empty()) { - const size_t combined_infill_layers = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); + const size_t combined_infill_layers + = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); infill_angle = mesh.infill_angles.at((static_cast(gcode_layer.getLayerNr()) / combined_infill_layers) % mesh.infill_angles.size()); } const Point3 mesh_middle = mesh.bounding_box.getMiddle(); @@ -1726,30 +1835,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. wall_tool_paths.emplace_back(std::vector()); const coord_t overlap = infill_overlap - (density_idx == last_idx ? 0 : wall_line_count * infill_line_width); - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - infill_below_skin, - infill_line_width, - infill_line_distance_here, - overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - skin_below_wall_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_tool_paths.back(), infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_below_skin, + infill_line_width, + infill_line_distance_here, + overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + skin_below_wall_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_tool_paths.back(), + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, min_skin_below_wall_count); @@ -1781,30 +1900,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, constexpr coord_t overlap = 0; // overlap is already applied for the sparsest density in the generateGradualInfill wall_tool_paths.emplace_back(); - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - in_outline, - infill_line_width, - infill_line_distance_here, - overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - wall_line_count_here, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_tool_paths.back(), infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + in_outline, + infill_line_width, + infill_line_distance_here, + overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + wall_line_count_here, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_tool_paths.back(), + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, wall_line_count); @@ -1816,8 +1945,21 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, } wall_tool_paths.emplace_back(part.infill_wall_toolpaths); // The extra infill walls were generated separately. Add these too. - const bool walls_generated = - std::any_of(wall_tool_paths.cbegin(), wall_tool_paths.cend(), [](const std::vector& tp) { return ! (tp.empty() || std::all_of(tp.begin(), tp.end(), [](const VariableWidthLines& vwl) { return vwl.empty(); })); }); + const bool walls_generated = std::any_of( + wall_tool_paths.cbegin(), + wall_tool_paths.cend(), + [](const std::vector& tp) + { + return ! ( + tp.empty() + || std::all_of( + tp.begin(), + tp.end(), + [](const VariableWidthLines& vwl) + { + return vwl.empty(); + })); + }); if (! infill_lines.empty() || ! infill_polygons.empty() || walls_generated) { added_something = true; @@ -1838,7 +1980,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, else // So walls_generated must be true. { std::vector* start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; - while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. Should almost always be very quick. + while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. + // Should almost always be very quick. { start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; } @@ -1851,23 +1994,28 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh_config.infill_config[0].getLineWidth() * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.infill_config[0], - mesh_config.infill_config[0], - mesh_config.infill_config[0], - mesh_config.infill_config[0], - retract_before_outer_wall, - wipe_dist, - wipe_dist, - extruder_nr, - extruder_nr, - z_seam_config, - tool_paths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh_config.infill_config[0].getLineWidth() * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + tool_paths); added_something |= wall_orderer.addToLayer(); } } @@ -1879,21 +2027,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[0], ZSeamConfig(), 0, false, 1.0_r, false, false, near_start_location); } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesByOptimizer(infill_lines, mesh_config.infill_config[0], SpaceFillType::Lines, enable_travel_optimization, mesh.settings.get("infill_wipe_dist"), /*float_ratio = */ 1.0, near_start_location); + gcode_layer.addLinesByOptimizer( + infill_lines, + mesh_config.infill_config[0], + SpaceFillType::Lines, + enable_travel_optimization, + mesh.settings.get("infill_wipe_dist"), + /*float_ratio = */ 1.0, + near_start_location); } else { gcode_layer.addLinesByOptimizer( - infill_lines, mesh_config.infill_config[0], (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, enable_travel_optimization, /* wipe_dist = */ 0, /*float_ratio = */ 1.0, near_start_location); + infill_lines, + mesh_config.infill_config[0], + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + /* wipe_dist = */ 0, + /*float_ratio = */ 1.0, + near_start_location); } } return added_something; } -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) +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; const auto skin_edge_support_layers = mesh.settings.get("skin_edge_support_layers"); @@ -1978,7 +2145,8 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol } } - // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a bump + // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a + // bump infill_below_skin = skin_above_combined.intersection(part.infill_area_per_combine_per_density.back().front()).offset(-infill_line_width).offset(infill_line_width); constexpr bool remove_small_holes_from_infill_below_skin = true; @@ -1998,7 +2166,12 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol return ! infill_below_skin_overlap.empty() && ! infill_not_below_skin.empty(); } -void FffGcodeWriter::processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const +void FffGcodeWriter::processSpiralizedWall( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + const SliceMeshStorage& mesh) const { if (part.spiral_wall.empty()) { @@ -2024,11 +2197,18 @@ void FffGcodeWriter::processSpiralizedWall(const SliceDataStorage& storage, Laye // output a wall slice that is interpolated between the last and current walls for (const ConstPolygonRef& wall_outline : part.spiral_wall) { - gcode_layer.spiralizeWallSlice(mesh_config.inset0_config, wall_outline, ConstPolygonRef(*last_wall_outline), seam_vertex_idx, last_seam_vertex_idx, is_top_layer, is_bottom_layer); + gcode_layer + .spiralizeWallSlice(mesh_config.inset0_config, wall_outline, ConstPolygonRef(*last_wall_outline), seam_vertex_idx, last_seam_vertex_idx, is_top_layer, is_bottom_layer); } } -bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processInsets( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { bool added_something = false; if (extruder_nr != mesh.settings.get("wall_0_extruder_nr").extruder_nr && extruder_nr != mesh.settings.get("wall_x_extruder_nr").extruder_nr) @@ -2045,7 +2225,8 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { const size_t initial_bottom_layers = mesh.settings.get("initial_bottom_layers"); const int layer_nr = gcode_layer.getLayerNr(); - if ((layer_nr < static_cast(initial_bottom_layers) && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths + if ((layer_nr < static_cast(initial_bottom_layers) + && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths || (layer_nr >= static_cast(initial_bottom_layers) && part.spiral_wall.empty())) // The rest of the layers in spiralize mode are using the spiral wall { // nothing to do @@ -2055,7 +2236,8 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { spiralize = true; } - if (spiralize && gcode_layer.getLayerNr() == static_cast(initial_bottom_layers) && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr) + if (spiralize && gcode_layer.getLayerNr() == static_cast(initial_bottom_layers) + && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr) { // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward added_something = true; gcode_layer.setIsInside(true); // going to print stuff inside print object @@ -2205,23 +2387,28 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { // Main case: Optimize the insets with the InsetOrderOptimizer. const coord_t wall_x_wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.inset0_config, - mesh_config.insetX_config, - mesh_config.bridge_inset0_config, - mesh_config.bridge_insetX_config, - mesh.settings.get("travel_retract_before_outer_wall"), - mesh.settings.get("wall_0_wipe_dist"), - wall_x_wipe_dist, - mesh.settings.get("wall_0_extruder_nr").extruder_nr, - mesh.settings.get("wall_x_extruder_nr").extruder_nr, - z_seam_config, - part.wall_toolpaths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.inset0_config, + mesh_config.insetX_config, + mesh_config.bridge_inset0_config, + mesh_config.bridge_insetX_config, + mesh.settings.get("travel_retract_before_outer_wall"), + mesh.settings.get("wall_0_wipe_dist"), + wall_x_wipe_dist, + mesh.settings.get("wall_0_extruder_nr").extruder_nr, + mesh.settings.get("wall_x_extruder_nr").extruder_nr, + z_seam_config, + part.wall_toolpaths); added_something |= wall_orderer.addToLayer(); } return added_something; @@ -2260,7 +2447,13 @@ std::optional FffGcodeWriter::getSeamAvoidingLocation(const Polygons& fil } } -bool FffGcodeWriter::processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processSkin( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { const size_t top_bottom_extruder_nr = mesh.settings.get("top_bottom_extruder_nr").extruder_nr; const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr; @@ -2289,7 +2482,13 @@ bool FffGcodeWriter::processSkin(const SliceDataStorage& storage, LayerPlan& gco return added_something; } -bool FffGcodeWriter::processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part) const +bool FffGcodeWriter::processSkinPart( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part) const { bool added_something = false; @@ -2302,13 +2501,14 @@ bool FffGcodeWriter::processSkinPart(const SliceDataStorage& storage, LayerPlan& return added_something; } -void FffGcodeWriter::processRoofing(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SkinPart& skin_part, - bool& added_something) const +void FffGcodeWriter::processRoofing( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const { const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr; if (extruder_nr != roofing_extruder_nr) @@ -2326,16 +2526,30 @@ void FffGcodeWriter::processRoofing(const SliceDataStorage& storage, const Ratio skin_density = 1.0; const coord_t skin_overlap = 0; // skinfill already expanded over the roofing areas; don't overlap with perimeters const bool monotonic = mesh.settings.get("roofing_monotonic"); - processSkinPrintFeature(storage, gcode_layer, mesh, mesh_config, extruder_nr, skin_part.roofing_fill, mesh_config.roofing_config, pattern, roofing_angle, skin_overlap, skin_density, monotonic, added_something); + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + mesh_config, + extruder_nr, + skin_part.roofing_fill, + mesh_config.roofing_config, + pattern, + roofing_angle, + skin_overlap, + skin_density, + monotonic, + added_something); } -void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SkinPart& skin_part, - bool& added_something) const +void FffGcodeWriter::processTopBottom( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const { if (skin_part.skin_fill.empty()) { @@ -2361,7 +2575,7 @@ void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, // generate skin_polygons and skin_lines const GCodePathConfig* skin_config = &mesh_config.skin_config; Ratio skin_density = 1.0; - const coord_t skin_overlap = 0; // Skin overlap offset is applied in skin.cpp more overlap might be beneficial for curved bridges, but makes it worse in general. + const coord_t skin_overlap = 0; // Skin overlap offset is applied in skin.cpp more overlap might be beneficial for curved bridges, but makes it worse in general. const bool bridge_settings_enabled = mesh.settings.get("bridge_settings_enabled"); const bool bridge_enable_more_layers = bridge_settings_enabled && mesh.settings.get("bridge_enable_more_layers"); const Ratio support_threshold = bridge_settings_enabled ? mesh.settings.get("bridge_skin_support_threshold") : 0.0_r; @@ -2493,23 +2707,38 @@ void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, } } const bool monotonic = mesh.settings.get("skin_monotonic"); - processSkinPrintFeature(storage, gcode_layer, mesh, mesh_config, extruder_nr, skin_part.skin_fill, *skin_config, pattern, skin_angle, skin_overlap, skin_density, monotonic, added_something, fan_speed); + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + mesh_config, + extruder_nr, + skin_part.skin_fill, + *skin_config, + pattern, + skin_angle, + skin_overlap, + skin_density, + monotonic, + added_something, + fan_speed); } -void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const size_t extruder_nr, - const Polygons& area, - const GCodePathConfig& config, - EFillMethod pattern, - const AngleDegrees skin_angle, - const coord_t skin_overlap, - const Ratio skin_density, - const bool monotonic, - bool& added_something, - double fan_speed) const +void FffGcodeWriter::processSkinPrintFeature( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const size_t extruder_nr, + const Polygons& area, + const GCodePathConfig& config, + EFillMethod pattern, + const AngleDegrees skin_angle, + const coord_t skin_overlap, + const Ratio skin_density, + const bool monotonic, + bool& added_something, + double fan_speed) const { Polygons skin_polygons; Polygons skin_lines; @@ -2532,29 +2761,30 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - area, - config.getLineWidth(), - config.getLineWidth() / skin_density, - skin_overlap, - infill_multiplier, - skin_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_line_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + area, + config.getLineWidth(), + config.getLineWidth() / skin_density, + skin_overlap, + infill_multiplier, + skin_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_line_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN); // add paths @@ -2570,23 +2800,28 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), config.getLineWidth() * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - skin_extruder_nr, - skin_extruder_nr, - z_seam_config, - skin_paths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + config.getLineWidth() * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.skin_config, + mesh_config.skin_config, + mesh_config.skin_config, + mesh_config.skin_config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + skin_extruder_nr, + skin_extruder_nr, + z_seam_config, + skin_paths); added_something |= wall_orderer.addToLayer(); } } @@ -2603,11 +2838,23 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, const AngleRadians monotonic_direction = AngleRadians(skin_angle); constexpr Ratio flow = 1.0_r; - const coord_t max_adjacent_distance = config.getLineWidth() * 1.1; // Lines are considered adjacent if they are 1 line width apart, with 10% extra play. The monotonic order is enforced if they are adjacent. - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + const coord_t max_adjacent_distance + = config.getLineWidth() + * 1.1; // Lines are considered adjacent if they are 1 line width apart, with 10% extra play. The monotonic order is enforced if they are adjacent. + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesMonotonic(area, skin_lines, config, SpaceFillType::Lines, monotonic_direction, max_adjacent_distance, exclude_distance, mesh.settings.get("infill_wipe_dist"), flow, fan_speed); + gcode_layer.addLinesMonotonic( + area, + skin_lines, + config, + SpaceFillType::Lines, + monotonic_direction, + max_adjacent_distance, + exclude_distance, + mesh.settings.get("infill_wipe_dist"), + flow, + fan_speed); } else { @@ -2619,7 +2866,8 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, else { std::optional near_start_location; - const EFillMethod pattern = (gcode_layer.getLayerNr() == 0) ? mesh.settings.get("top_bottom_pattern_0") : mesh.settings.get("top_bottom_pattern"); + const EFillMethod pattern + = (gcode_layer.getLayerNr() == 0) ? mesh.settings.get("top_bottom_pattern_0") : mesh.settings.get("top_bottom_pattern"); if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG) { // update near_start_location to a location which tries to avoid seams in skin near_start_location = getSeamAvoidingLocation(area, skin_angle, gcode_layer.getLastPlannedPositionOrStartingPosition()); @@ -2627,10 +2875,18 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, constexpr bool enable_travel_optimization = false; constexpr float flow = 1.0; - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesByOptimizer(skin_lines, config, SpaceFillType::Lines, enable_travel_optimization, mesh.settings.get("infill_wipe_dist"), flow, near_start_location, fan_speed); + gcode_layer.addLinesByOptimizer( + skin_lines, + config, + SpaceFillType::Lines, + enable_travel_optimization, + mesh.settings.get("infill_wipe_dist"), + flow, + near_start_location, + fan_speed); } else { @@ -2642,7 +2898,12 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, } } -bool FffGcodeWriter::processIroning(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const SliceLayer& layer, const GCodePathConfig& line_config, LayerPlan& gcode_layer) const +bool FffGcodeWriter::processIroning( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const SliceLayer& layer, + const GCodePathConfig& line_config, + LayerPlan& gcode_layer) const { bool added_something = false; const bool ironing_enabled = mesh.settings.get("ironing_enabled"); @@ -2671,8 +2932,8 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - size_t support_infill_extruder_nr = - (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + size_t support_infill_extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; if (support_layer.support_bottom.empty() && support_layer.support_roof.empty() && support_layer.support_infill_parts.empty()) @@ -2707,7 +2968,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const size_t extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + const size_t extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; const ExtruderTrain& infill_extruder = Application::getInstance().current_slice->scene.extruders[extruder_nr]; coord_t default_support_line_distance = infill_extruder.settings.get("support_line_distance"); @@ -2795,7 +3057,22 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr coord_t wipe_dist = 0; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, infill_extruder.settings, extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, wall_toolpaths); + *this, + storage, + gcode_layer, + infill_extruder.settings, + extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + wall_toolpaths); added_something |= wall_orderer.addToLayer(); } @@ -2820,7 +3097,10 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } const unsigned int density_factor = 2 << density_idx; // == pow(2, density_idx + 1) - int support_line_distance_here = (part.custom_line_distance > 0 ? part.custom_line_distance : default_support_line_distance * density_factor); // the highest density infill combines with the next to create a grid with density_factor 1 + int support_line_distance_here + = (part.custom_line_distance > 0 + ? part.custom_line_distance + : default_support_line_distance * density_factor); // the highest density infill combines with the next to create a grid with density_factor 1 const int support_shift = support_line_distance_here / 2; if (part.custom_line_distance == 0 && (density_idx == max_density_idx || support_pattern == EFillMethod::CROSS || support_pattern == EFillMethod::CROSS_3D)) { @@ -2829,33 +3109,42 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const Polygons& area = part.infill_area_per_combine_per_density[density_idx][combine_idx]; constexpr size_t wall_count = 0; // Walls are generated somewhere else, so their layers aren't vertically combined. - const coord_t small_area_width = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width + = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. constexpr bool skip_stitching = false; const bool fill_gaps = density_idx == 0; // Only fill gaps for one of the densities. - Infill infill_comp(support_pattern, - zig_zaggify_infill, - connect_polygons, - area, - support_line_width, - support_line_distance_here, - current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), - infill_multiplier, - support_infill_angle, - gcode_layer.z, - support_shift, - max_resolution, - max_deviation, - wall_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - support_connect_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_toolpaths_here, support_polygons, support_lines, infill_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT, storage.support.cross_fill_provider); + Infill infill_comp( + support_pattern, + zig_zaggify_infill, + connect_polygons, + area, + support_line_width, + support_line_distance_here, + current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), + infill_multiplier, + support_infill_angle, + gcode_layer.z, + support_shift, + max_resolution, + max_deviation, + wall_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + support_connect_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_toolpaths_here, + support_polygons, + support_lines, + infill_extruder.settings, + gcode_layer.getLayerNr(), + SectionType::SUPPORT, + storage.support.cross_fill_provider); } if (need_travel_to_end_of_last_spiral && infill_extruder.settings.get("magic_spiralize")) @@ -2897,7 +3186,15 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const std::optional start_near_location = std::optional(); gcode_layer.addPolygonsByOptimizer( - support_polygons, gcode_layer.configs_storage.support_infill_config[combine_idx], z_seam_config, wall_0_wipe_dist, spiralize, flow_ratio, always_retract, alternate_layer_print_direction, start_near_location); + support_polygons, + gcode_layer.configs_storage.support_infill_config[combine_idx], + z_seam_config, + wall_0_wipe_dist, + spiralize, + flow_ratio, + always_retract, + alternate_layer_print_direction, + start_near_location); added_something = true; } @@ -2909,15 +3206,16 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const std::optional near_start_location = std::optional(); constexpr double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; - gcode_layer.addLinesByOptimizer(support_lines, - gcode_layer.configs_storage.support_infill_config[combine_idx], - (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - near_start_location, - fan_speed, - alternate_layer_print_direction); + gcode_layer.addLinesByOptimizer( + support_lines, + gcode_layer.configs_storage.support_infill_config[combine_idx], + (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + near_start_location, + fan_speed, + alternate_layer_print_direction); added_something = true; } @@ -2930,9 +3228,28 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; constexpr coord_t simplify_curvature = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, simplify_curvature); + const ZSeamConfig z_seam_config( + EZSeamType::SHORTEST, + gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + simplify_curvature); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, infill_extruder.settings, extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, wall_toolpaths_here); + *this, + storage, + gcode_layer, + infill_extruder.settings, + extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + wall_toolpaths_here); added_something |= wall_orderer.addToLayer(); } } @@ -2997,29 +3314,30 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay infill_outline = wall.offset(-support_roof_line_width / 2); } - Infill roof_computation(pattern, - zig_zaggify_infill, - connect_polygons, - infill_outline, - gcode_layer.configs_storage.support_roof_config.getLineWidth(), - support_roof_line_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_outline, + gcode_layer.configs_storage.support_roof_config.getLineWidth(), + support_roof_line_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons roof_polygons; std::vector roof_paths; Polygons roof_lines; @@ -3047,10 +3365,28 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, roof_extruder.settings, roof_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, roof_extruder_nr, roof_extruder_nr, z_seam_config, roof_paths); + *this, + storage, + gcode_layer, + roof_extruder.settings, + roof_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + roof_extruder_nr, + roof_extruder_nr, + z_seam_config, + roof_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(roof_lines, gcode_layer.configs_storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer( + roof_lines, + gcode_layer.configs_storage.support_roof_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); return true; } @@ -3094,30 +3430,32 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L const coord_t max_resolution = bottom_extruder.settings.get("meshfix_maximum_resolution"); const coord_t max_deviation = bottom_extruder.settings.get("meshfix_maximum_deviation"); - const coord_t support_bottom_line_distance = bottom_extruder.settings.get("support_bottom_line_distance"); // note: no need to apply initial line width factor; support bottoms cannot exist on the first layer - Infill bottom_computation(pattern, - zig_zaggify_infill, - connect_polygons, - support_layer.support_bottom, - gcode_layer.configs_storage.support_bottom_config.getLineWidth(), - support_bottom_line_distance, - support_bottom_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + const coord_t support_bottom_line_distance = bottom_extruder.settings.get( + "support_bottom_line_distance"); // note: no need to apply initial line width factor; support bottoms cannot exist on the first layer + Infill bottom_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + support_layer.support_bottom, + gcode_layer.configs_storage.support_bottom_config.getLineWidth(), + support_bottom_line_distance, + support_bottom_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons bottom_polygons; std::vector bottom_paths; Polygons bottom_lines; @@ -3141,10 +3479,28 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, bottom_extruder.settings, bottom_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, bottom_extruder_nr, bottom_extruder_nr, z_seam_config, bottom_paths); + *this, + storage, + gcode_layer, + bottom_extruder.settings, + bottom_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + bottom_extruder_nr, + bottom_extruder_nr, + z_seam_config, + bottom_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(bottom_lines, gcode_layer.configs_storage.support_bottom_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer( + bottom_lines, + gcode_layer.configs_storage.support_bottom_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); return true; } @@ -3251,7 +3607,7 @@ void FffGcodeWriter::finalize() // set extrusion mode back to "normal" gcode.resetExtrusionMode(); - for (size_t e = 0; e < Application::getInstance().current_slice->scene.extruders.size(); e ++) + for (size_t e = 0; e < Application::getInstance().current_slice->scene.extruders.size(); e++) { gcode.writeTemperatureCommand(e, 0, false); } From b1bcc703182e9ad8e9033373d281f2bd24432ba0 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 3 Aug 2023 15:46:46 +0200 Subject: [PATCH 286/656] Don't add skirt height if skirt doesn't exist CURA-10758 Co-authored-by: saumya.jain --- src/FffGcodeWriter.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1ae71f4c0f..a5a85986d3 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1152,23 +1152,24 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; const bool reverse_print_direction = false; - // For layer_nr != 0 add only the innermost brim line (which is only the case if skirt_height > 1) - Polygons inner_brim_line; - inner_brim_line.add(all_brim_lines[0]); - - gcode_layer.addLinesByOptimizer - ( - layer_nr == 0 ? all_brim_lines : inner_brim_line, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements - ); + if (! all_brim_lines.empty()) + { + // For layer_nr != 0 add only the innermost brim line (which is only the case if skirt_height > 1) + Polygons inner_brim_line; + inner_brim_line.add(all_brim_lines[0]); + + gcode_layer.addLinesByOptimizer( + layer_nr == 0 ? all_brim_lines : inner_brim_line, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements); + } } void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const From 87ada9cda03b7f02533915e27894d0131df9b50a Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 3 Aug 2023 13:49:28 +0000 Subject: [PATCH 287/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 1244 ++++++++++++++++++++++++++-------------- 1 file changed, 802 insertions(+), 442 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index a5a85986d3..59810a8c87 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1,20 +1,10 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include // numeric_limits -#include -#include -#include - -#include //For generating a UUID. -#include //For generating a UUID. -#include -#include +#include "FffGcodeWriter.h" #include "Application.h" #include "ExtruderTrain.h" -#include "FffGcodeWriter.h" #include "FffProcessor.h" #include "InsetOrderOptimizer.h" #include "LayerPlan.h" @@ -31,10 +21,24 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include +#include + +#include +#include //For generating a UUID. +#include //For generating a UUID. +#include // numeric_limits +#include +#include +#include + namespace cura { -FffGcodeWriter::FffGcodeWriter() : max_object_height(0), layer_plan_buffer(gcode), slice_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) +FffGcodeWriter::FffGcodeWriter() + : max_object_height(0) + , layer_plan_buffer(gcode) + , slice_uuid(boost::uuids::to_string(boost::uuids::random_generator()())) { for (unsigned int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++) { // initialize all as max layer_nr, so that they get updated to the lowest layer on which they are used. @@ -91,7 +95,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep { auto should_prime_extruder = gcode.initializeExtruderTrains(storage, start_extruder_nr); - if (!should_prime_extruder) + if (! should_prime_extruder) { // set to most negative number so that layer processing never primes this extruder anymore. extruder_prime_layer_nr[start_extruder_nr] = std::numeric_limits::min(); @@ -159,7 +163,10 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep run_multiple_producers_ordered_consumer( process_layer_starting_layer_nr, total_layers, - [&storage, total_layers, this](int layer_nr) { return &processLayer(storage, layer_nr, total_layers); }, + [&storage, total_layers, this](int layer_nr) + { + return &processLayer(storage, layer_nr, total_layers); + }, [this, total_layers](LayerPlan* gcode_layer) { Progress::messageProgress(Progress::Stage::EXPORT, std::max(0, gcode_layer->getLayerNr()) + 1, total_layers); @@ -328,7 +335,8 @@ static void retractionAndWipeConfigFromSettings(const Settings& settings, Retrac switch_retraction_config.primeSpeed = settings.get("switch_extruder_prime_speed"); switch_retraction_config.zHop = settings.get("retraction_hop_after_extruder_switch_height"); switch_retraction_config.retraction_min_travel_distance = 0; // No limitation on travel distance for an extruder switch retract. - switch_retraction_config.retraction_extrusion_window = 99999.9; // So that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions). + switch_retraction_config.retraction_extrusion_window + = 99999.9; // So that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions). switch_retraction_config.retraction_count_max = 9999999; // Extruder switch retraction is never limited. WipeScriptConfig& wipe_config = config->wipe_config; @@ -364,7 +372,7 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for(SliceMeshStorage& mesh: storage.meshes) + for (SliceMeshStorage& mesh : storage.meshes) { retractionAndWipeConfigFromSettings(mesh.settings, &mesh.retraction_wipe_config); } @@ -375,24 +383,21 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const EPlatformAdhesion adhesion_type = mesh_group_settings.get("adhesion_type"); const int skirt_brim_extruder_nr = mesh_group_settings.get("skirt_brim_extruder_nr"); - const ExtruderTrain* skirt_brim_extruder = (skirt_brim_extruder_nr < 0)? nullptr : &mesh_group_settings.get("skirt_brim_extruder_nr"); + const ExtruderTrain* skirt_brim_extruder = (skirt_brim_extruder_nr < 0) ? nullptr : &mesh_group_settings.get("skirt_brim_extruder_nr"); size_t start_extruder_nr; - if (adhesion_type == EPlatformAdhesion::SKIRT - && skirt_brim_extruder + if (adhesion_type == EPlatformAdhesion::SKIRT && skirt_brim_extruder && (skirt_brim_extruder->settings.get("skirt_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } - else if ((adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) - && skirt_brim_extruder + else if ( + (adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) && skirt_brim_extruder && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } - else if (adhesion_type == EPlatformAdhesion::RAFT - && skirt_brim_extruder - ) + else if (adhesion_type == EPlatformAdhesion::RAFT && skirt_brim_extruder) { start_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; } @@ -484,7 +489,8 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) storage.support.support_infill_angles_layer_0.push_back(0); } - auto getInterfaceAngles = [&storage](const ExtruderTrain& extruder, const std::string& interface_angles_setting, const EFillMethod pattern, const std::string& interface_height_setting) + auto getInterfaceAngles + = [&storage](const ExtruderTrain& extruder, const std::string& interface_angles_setting, const EFillMethod pattern, const std::string& interface_height_setting) { std::vector angles = extruder.settings.get>(interface_angles_setting); if (angles.empty()) @@ -501,7 +507,8 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) { for (const SliceMeshStorage& mesh : storage.meshes) { - if (mesh.settings.get(interface_height_setting) >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) + if (mesh.settings.get(interface_height_setting) + >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) { // Some roofs are quite thick. // Alternate between the two kinds of diagonal: / and \ . @@ -519,10 +526,12 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) }; const ExtruderTrain& roof_extruder = mesh_group_settings.get("support_roof_extruder_nr"); - storage.support.support_roof_angles = getInterfaceAngles(roof_extruder, "support_roof_angles", roof_extruder.settings.get("support_roof_pattern"), "support_roof_height"); + storage.support.support_roof_angles + = getInterfaceAngles(roof_extruder, "support_roof_angles", roof_extruder.settings.get("support_roof_pattern"), "support_roof_height"); const ExtruderTrain& bottom_extruder = mesh_group_settings.get("support_bottom_extruder_nr"); - storage.support.support_bottom_angles = getInterfaceAngles(bottom_extruder, "support_bottom_angles", bottom_extruder.settings.get("support_bottom_pattern"), "support_bottom_height"); + storage.support.support_bottom_angles + = getInterfaceAngles(bottom_extruder, "support_bottom_angles", bottom_extruder.settings.get("support_bottom_pattern"), "support_bottom_height"); } void FffGcodeWriter::processNextMeshGroupCode(const SliceDataStorage& storage) @@ -570,7 +579,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) z += layer_height; const coord_t comb_offset = base_settings.get("raft_base_line_spacing"); - std::vector fan_speed_layer_time_settings_per_extruder_raft_base = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_base + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_base) { double regular_fan_speed = base_settings.get("raft_base_fan_speed") * 100.0; @@ -580,7 +590,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const coord_t line_width = base_settings.get("raft_base_line_width"); const coord_t avoid_distance = base_settings.get("travel_avoid_distance"); - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); + LayerPlan& gcode_layer + = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); gcode_layer.setIsInside(true); gcode_layer.setExtruder(base_extruder_nr); @@ -616,29 +627,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::LINES, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - gcode_layer.configs_storage.raft_base_config.getLineWidth(), - line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::LINES, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + gcode_layer.configs_storage.raft_base_config.getLineWidth(), + line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); if (! raft_paths.empty()) @@ -646,7 +658,22 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, base_settings, base_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, base_extruder_nr, base_extruder_nr, z_seam_config, raft_paths); + *this, + storage, + gcode_layer, + base_settings, + base_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + base_extruder_nr, + base_extruder_nr, + z_seam_config, + raft_paths); wall_orderer.addToLayer(); } gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); @@ -672,7 +699,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const LayerIndex layer_nr = initial_raft_layer_nr + raft_interface_layer; z += interface_layer_height; - std::vector fan_speed_layer_time_settings_per_extruder_raft_interface = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_interface + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_interface) { const double regular_fan_speed = interface_fan_speed * 100.0; @@ -681,7 +709,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } const coord_t comb_offset = interface_line_spacing; - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, interface_layer_height, current_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_interface, comb_offset, interface_line_width, interface_avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + interface_layer_height, + current_extruder_nr, + fan_speed_layer_time_settings_per_extruder_raft_interface, + comb_offset, + interface_line_width, + interface_avoid_distance); gcode_layer.setIsInside(true); current_extruder_nr = interface_extruder_nr; @@ -689,7 +726,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, interface_layer_height); std::vector raft_outline_paths; - const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. + const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() + / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. if (storage.primeRaftOutline.area() > 0) { raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); @@ -715,29 +753,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - interface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - interface_max_resolution, - interface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + interface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + interface_max_resolution, + interface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); @@ -763,7 +802,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const LayerIndex layer_nr = initial_raft_layer_nr + 1 + num_interface_layers + raft_surface_layer - 1; // +1: 1 base layer z += surface_layer_height; - std::vector fan_speed_layer_time_settings_per_extruder_raft_surface = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy + std::vector fan_speed_layer_time_settings_per_extruder_raft_surface + = fan_speed_layer_time_settings_per_extruder; // copy so that we change only the local copy for (FanSpeedLayerTimeSettings& fan_speed_layer_time_settings : fan_speed_layer_time_settings_per_extruder_raft_surface) { const double regular_fan_speed = surface_fan_speed * 100.0; @@ -772,7 +812,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } const coord_t comb_offset = surface_line_spacing; - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, surface_layer_height, current_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_surface, comb_offset, surface_line_width, surface_avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + surface_layer_height, + current_extruder_nr, + fan_speed_layer_time_settings_per_extruder_raft_surface, + comb_offset, + surface_line_width, + surface_avoid_distance); gcode_layer.setIsInside(true); // make sure that we are using the correct extruder to print raft @@ -781,7 +830,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, surface_layer_height); std::vector raft_outline_paths; - const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. + const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() + / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. if (storage.primeRaftOutline.area() > 0) { raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); @@ -791,7 +841,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); Polygons raft_lines; - AngleDegrees fill_angle = (num_surface_layers - raft_surface_layer) % 2 ? 45 : 135; // Alternate between -45 and +45 degrees, ending up 90 degrees rotated from the default skin angle. + AngleDegrees fill_angle + = (num_surface_layers - raft_surface_layer) % 2 ? 45 : 135; // Alternate between -45 and +45 degrees, ending up 90 degrees rotated from the default skin angle. constexpr bool zig_zaggify_infill = true; constexpr size_t wall_line_count = 0; @@ -807,29 +858,30 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (const Polygons& raft_outline_path : raft_outline_paths) { - Infill infill_comp(EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - surface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - surface_max_resolution, - surface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + surface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + surface_max_resolution, + surface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); std::vector raft_paths; // Should remain empty, since we have no walls. infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); @@ -865,8 +917,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn // find printZ of first actual printed mesh for (const SliceMeshStorage& mesh : storage.meshes) { - if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("cutting_mesh") - || mesh.settings.get("infill_mesh")) + if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") + || mesh.settings.get("cutting_mesh") || mesh.settings.get("infill_mesh")) { continue; } @@ -911,11 +963,22 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } const coord_t comb_offset_from_outlines = max_inner_wall_width * 2; - assert(static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); - const std::vector& extruder_order = (layer_nr < 0) ? extruder_order_per_layer_negative_layers[extruder_order_per_layer_negative_layers.size() + layer_nr] : extruder_order_per_layer[layer_nr]; + assert( + static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); + const std::vector& extruder_order + = (layer_nr < 0) ? extruder_order_per_layer_negative_layers[extruder_order_per_layer_negative_layers.size() + layer_nr] : extruder_order_per_layer[layer_nr]; const coord_t first_outer_wall_line_width = scene.extruders[extruder_order.front()].settings.get("wall_line_width_0"); - LayerPlan& gcode_layer = *new LayerPlan(storage, layer_nr, z, layer_thickness, extruder_order.front(), fan_speed_layer_time_settings_per_extruder, comb_offset_from_outlines, first_outer_wall_line_width, avoid_distance); + LayerPlan& gcode_layer = *new LayerPlan( + storage, + layer_nr, + z, + layer_thickness, + extruder_order.front(), + fan_speed_layer_time_settings_per_extruder, + comb_offset_from_outlines, + first_outer_wall_line_width, + avoid_distance); if (include_helper_parts) { @@ -933,7 +996,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - const size_t support_infill_extruder_nr = (layer_nr <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + const size_t support_infill_extruder_nr = (layer_nr <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; for (const size_t& extruder_nr : extruder_order) { @@ -961,7 +1025,8 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; const PathConfigStorage::MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; if (mesh.settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE - && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! + && extruder_nr + == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! ) { addMeshLayerToGCode_meshSurfaceMode(storage, mesh, mesh_config, gcode_layer); @@ -1007,7 +1072,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const int skirt_height = train.settings.get("skirt_height"); const bool is_skirt = train.settings.get("adhesion_type") == EPlatformAdhesion::SKIRT; // only create a multilayer SkirtBrim for a skirt for the height of skirt_height - if (layer_nr != 0 && (layer_nr >= skirt_height || !is_skirt)) + if (layer_nr != 0 && (layer_nr >= skirt_height || ! is_skirt)) { return; } @@ -1016,7 +1081,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan return; } gcode_layer.setSkirtBrimIsPlanned(extruder_nr); - + const auto& original_skirt_brim = storage.skirt_brim[extruder_nr]; if (original_skirt_brim.size() == 0) { @@ -1042,7 +1107,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan const size_t inset_idx; ConstPolygonPointer poly; }; - + size_t total_line_count = 0; for (const SkirtBrimLine& line : storage.skirt_brim[extruder_nr]) { @@ -1050,7 +1115,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan total_line_count += line.open_polylines.size(); } Polygons all_brim_lines; - + // Add the support brim before the below algorithm which takes order requirements into account // For support brim we don't care about the order, because support doesn't need to be accurate. const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; @@ -1061,7 +1126,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan support_brim_lines.toPolylines(); all_brim_lines = support_brim_lines; } - + all_brim_lines.reserve(total_line_count); const coord_t line_w = train.settings.get("skirt_brim_line_width") * train.settings.get("initial_layer_line_width_factor"); @@ -1091,7 +1156,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan ConstPolygonPointer pp(all_brim_lines.back()); for (Point p : line) { - grid.insert(p, BrimLineReference{inset_idx, pp}); + grid.insert(p, BrimLineReference{ inset_idx, pp }); } } } @@ -1145,7 +1210,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan } } assert(all_brim_lines.size() == total_line_count); // Otherwise pointers would have gotten invalidated - + const bool enable_travel_optimization = true; // Use the combing outline while deciding in which order to print the lines. Can't hurt for only one layer. const coord_t wipe_dist = 0u; const Ratio flow_ratio = 1.0; @@ -1328,7 +1393,11 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s return ret; } -void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { @@ -1351,7 +1420,11 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& polygons = Simplify(mesh.settings).polygon(polygons); - ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); const bool spiralize = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("magic_spiralize"); gcode_layer.addPolygonsByOptimizer(polygons, mesh_config.inset0_config, z_seam_config, mesh.settings.get("wall_0_wipe_dist"), spiralize); @@ -1365,7 +1438,12 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, c gcode_layer.addLinesByOptimizer(layer->openPolyLines, mesh_config.inset0_config, SpaceFillType::PolyLines); } -void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { @@ -1389,7 +1467,11 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const ZSeamConfig z_seam_config; if (mesh.isPrinted()) //"normal" meshes with walls, skin, infill, etc. get the traditional part ordering based on the z-seam settings. { - z_seam_config = ZSeamConfig(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + z_seam_config = ZSeamConfig( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); } PathOrderOptimizer part_order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config); for (const SliceLayerPart& part : layer.parts) @@ -1414,8 +1496,13 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const gcode_layer.setMesh(nullptr); } -void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) - const +void FffGcodeWriter::addMeshPartToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + LayerPlan& gcode_layer) const { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; @@ -1436,7 +1523,8 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S added_something = added_something | processSkin(storage, gcode_layer, mesh, extruder_nr, mesh_config, part); // After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. - if (added_something && (! mesh_group_settings.get("magic_spiralize") || gcode_layer.getLayerNr() < static_cast(mesh.settings.get("initial_bottom_layers")))) + if (added_something + && (! mesh_group_settings.get("magic_spiralize") || gcode_layer.getLayerNr() < static_cast(mesh.settings.get("initial_bottom_layers")))) { coord_t innermost_wall_line_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (gcode_layer.getLayerNr() == 0) @@ -1449,7 +1537,13 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S gcode_layer.setIsInside(false); } -bool FffGcodeWriter::processInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1460,8 +1554,13 @@ bool FffGcodeWriter::processInfill(const SliceDataStorage& storage, LayerPlan& g return added_something; } -bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) - const +bool FffGcodeWriter::processMultiLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1477,7 +1576,8 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La AngleDegrees infill_angle = 45; // Original default. This will get updated to an element from mesh->infill_angles. if (! mesh.infill_angles.empty()) { - const size_t combined_infill_layers = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); + const size_t combined_infill_layers + = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); infill_angle = mesh.infill_angles.at((gcode_layer.getLayerNr() / combined_infill_layers) % mesh.infill_angles.size()); } const Point3 mesh_middle = mesh.bounding_box.getMiddle(); @@ -1520,30 +1620,40 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La { lightning_layer = &mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr()); } - Infill infill_comp(infill_pattern, - zig_zaggify_infill, - connect_polygons, - part.infill_area_per_combine_per_density[density_idx][combine_idx], - infill_line_width, - infill_line_distance_here, - infill_overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - mesh.settings.get("cross_infill_pocket_size")); - infill_comp.generate(infill_paths, infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + infill_pattern, + zig_zaggify_infill, + connect_polygons, + part.infill_area_per_combine_per_density[density_idx][combine_idx], + infill_line_width, + infill_line_distance_here, + infill_overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + mesh.settings.get("cross_infill_pocket_size")); + infill_comp.generate( + infill_paths, + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); } if (! infill_lines.empty() || ! infill_polygons.empty()) { @@ -1567,25 +1677,27 @@ bool FffGcodeWriter::processMultiLayerInfill(const SliceDataStorage& storage, La } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); - gcode_layer.addLinesByOptimizer(infill_lines, - mesh_config.infill_config[combine_idx], - zig_zaggify_infill ? SpaceFillType::PolyLines : SpaceFillType::Lines, - enable_travel_optimization, - /*wipe_dist = */ 0, - /* flow = */ 1.0, - near_start_location); + gcode_layer.addLinesByOptimizer( + infill_lines, + mesh_config.infill_config[combine_idx], + zig_zaggify_infill ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + /*wipe_dist = */ 0, + /* flow = */ 1.0, + near_start_location); } } } return added_something; } -bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SliceLayerPart& part) const +bool FffGcodeWriter::processSingleLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) { @@ -1616,7 +1728,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, AngleDegrees infill_angle = 45; // Original default. This will get updated to an element from mesh->infill_angles. if (! mesh.infill_angles.empty()) { - const size_t combined_infill_layers = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); + const size_t combined_infill_layers + = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(mesh.settings.get("layer_height"), coord_t(1)))); infill_angle = mesh.infill_angles.at((static_cast(gcode_layer.getLayerNr()) / combined_infill_layers) % mesh.infill_angles.size()); } const Point3 mesh_middle = mesh.bounding_box.getMiddle(); @@ -1713,30 +1826,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. wall_tool_paths.emplace_back(std::vector()); const coord_t overlap = infill_overlap - (density_idx == last_idx ? 0 : wall_line_count * infill_line_width); - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - infill_below_skin, - infill_line_width, - infill_line_distance_here, - overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - skin_below_wall_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_tool_paths.back(), infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_below_skin, + infill_line_width, + infill_line_distance_here, + overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + skin_below_wall_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_tool_paths.back(), + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, min_skin_below_wall_count); @@ -1768,30 +1891,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, constexpr coord_t overlap = 0; // overlap is already applied for the sparsest density in the generateGradualInfill wall_tool_paths.emplace_back(); - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - in_outline, - infill_line_width, - infill_line_distance_here, - overlap, - infill_multiplier, - infill_angle, - gcode_layer.z, - infill_shift, - max_resolution, - max_deviation, - wall_line_count_here, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_tool_paths.back(), infill_polygons, infill_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::INFILL, mesh.cross_fill_provider, lightning_layer, &mesh); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + in_outline, + infill_line_width, + infill_line_distance_here, + overlap, + infill_multiplier, + infill_angle, + gcode_layer.z, + infill_shift, + max_resolution, + max_deviation, + wall_line_count_here, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_tool_paths.back(), + infill_polygons, + infill_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::INFILL, + mesh.cross_fill_provider, + lightning_layer, + &mesh); if (density_idx < last_idx) { const coord_t cut_offset = get_cut_offset(zig_zaggify_infill, infill_line_width, wall_line_count); @@ -1803,8 +1936,21 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, } wall_tool_paths.emplace_back(part.infill_wall_toolpaths); // The extra infill walls were generated separately. Add these too. - const bool walls_generated = - std::any_of(wall_tool_paths.cbegin(), wall_tool_paths.cend(), [](const std::vector& tp) { return ! (tp.empty() || std::all_of(tp.begin(), tp.end(), [](const VariableWidthLines& vwl) { return vwl.empty(); })); }); + const bool walls_generated = std::any_of( + wall_tool_paths.cbegin(), + wall_tool_paths.cend(), + [](const std::vector& tp) + { + return ! ( + tp.empty() + || std::all_of( + tp.begin(), + tp.end(), + [](const VariableWidthLines& vwl) + { + return vwl.empty(); + })); + }); if (! infill_lines.empty() || ! infill_polygons.empty() || walls_generated) { added_something = true; @@ -1825,7 +1971,8 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, else // So walls_generated must be true. { std::vector* start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; - while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. Should almost always be very quick. + while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. + // Should almost always be very quick. { start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; } @@ -1838,23 +1985,28 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh_config.infill_config[0].getLineWidth() * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.infill_config[0], - mesh_config.infill_config[0], - mesh_config.infill_config[0], - mesh_config.infill_config[0], - retract_before_outer_wall, - wipe_dist, - wipe_dist, - extruder_nr, - extruder_nr, - z_seam_config, - tool_paths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh_config.infill_config[0].getLineWidth() * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + tool_paths); added_something |= wall_orderer.addToLayer(); } } @@ -1866,21 +2018,40 @@ bool FffGcodeWriter::processSingleLayerInfill(const SliceDataStorage& storage, gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[0], ZSeamConfig(), 0, false, 1.0_r, false, false, near_start_location); } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesByOptimizer(infill_lines, mesh_config.infill_config[0], SpaceFillType::Lines, enable_travel_optimization, mesh.settings.get("infill_wipe_dist"), /*float_ratio = */ 1.0, near_start_location); + gcode_layer.addLinesByOptimizer( + infill_lines, + mesh_config.infill_config[0], + SpaceFillType::Lines, + enable_travel_optimization, + mesh.settings.get("infill_wipe_dist"), + /*float_ratio = */ 1.0, + near_start_location); } else { gcode_layer.addLinesByOptimizer( - infill_lines, mesh_config.infill_config[0], (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, enable_travel_optimization, /* wipe_dist = */ 0, /*float_ratio = */ 1.0, near_start_location); + infill_lines, + mesh_config.infill_config[0], + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + /* wipe_dist = */ 0, + /*float_ratio = */ 1.0, + near_start_location); } } return added_something; } -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) +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; const auto skin_edge_support_layers = mesh.settings.get("skin_edge_support_layers"); @@ -1965,7 +2136,8 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol } } - // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a bump + // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a + // bump infill_below_skin = skin_above_combined.intersection(part.infill_area_per_combine_per_density.back().front()).offset(-infill_line_width).offset(infill_line_width); constexpr bool remove_small_holes_from_infill_below_skin = true; @@ -1985,7 +2157,12 @@ bool FffGcodeWriter::partitionInfillBySkinAbove(Polygons& infill_below_skin, Pol return ! infill_below_skin_overlap.empty() && ! infill_not_below_skin.empty(); } -void FffGcodeWriter::processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const +void FffGcodeWriter::processSpiralizedWall( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + const SliceMeshStorage& mesh) const { if (part.spiral_wall.empty()) { @@ -2011,11 +2188,18 @@ void FffGcodeWriter::processSpiralizedWall(const SliceDataStorage& storage, Laye // output a wall slice that is interpolated between the last and current walls for (const ConstPolygonRef& wall_outline : part.spiral_wall) { - gcode_layer.spiralizeWallSlice(mesh_config.inset0_config, wall_outline, ConstPolygonRef(*last_wall_outline), seam_vertex_idx, last_seam_vertex_idx, is_top_layer, is_bottom_layer); + gcode_layer + .spiralizeWallSlice(mesh_config.inset0_config, wall_outline, ConstPolygonRef(*last_wall_outline), seam_vertex_idx, last_seam_vertex_idx, is_top_layer, is_bottom_layer); } } -bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processInsets( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { bool added_something = false; if (extruder_nr != mesh.settings.get("wall_0_extruder_nr").extruder_nr && extruder_nr != mesh.settings.get("wall_x_extruder_nr").extruder_nr) @@ -2032,7 +2216,8 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { const size_t initial_bottom_layers = mesh.settings.get("initial_bottom_layers"); const int layer_nr = gcode_layer.getLayerNr(); - if ((layer_nr < static_cast(initial_bottom_layers) && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths + if ((layer_nr < static_cast(initial_bottom_layers) + && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths || (layer_nr >= static_cast(initial_bottom_layers) && part.spiral_wall.empty())) // The rest of the layers in spiralize mode are using the spiral wall { // nothing to do @@ -2042,7 +2227,8 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { spiralize = true; } - if (spiralize && gcode_layer.getLayerNr() == static_cast(initial_bottom_layers) && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr) + if (spiralize && gcode_layer.getLayerNr() == static_cast(initial_bottom_layers) + && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr) { // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward added_something = true; gcode_layer.setIsInside(true); // going to print stuff inside print object @@ -2192,23 +2378,28 @@ bool FffGcodeWriter::processInsets(const SliceDataStorage& storage, LayerPlan& g { // Main case: Optimize the insets with the InsetOrderOptimizer. const coord_t wall_x_wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.inset0_config, - mesh_config.insetX_config, - mesh_config.bridge_inset0_config, - mesh_config.bridge_insetX_config, - mesh.settings.get("travel_retract_before_outer_wall"), - mesh.settings.get("wall_0_wipe_dist"), - wall_x_wipe_dist, - mesh.settings.get("wall_0_extruder_nr").extruder_nr, - mesh.settings.get("wall_x_extruder_nr").extruder_nr, - z_seam_config, - part.wall_toolpaths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + mesh.settings.get("wall_line_width_0") * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.inset0_config, + mesh_config.insetX_config, + mesh_config.bridge_inset0_config, + mesh_config.bridge_insetX_config, + mesh.settings.get("travel_retract_before_outer_wall"), + mesh.settings.get("wall_0_wipe_dist"), + wall_x_wipe_dist, + mesh.settings.get("wall_0_extruder_nr").extruder_nr, + mesh.settings.get("wall_x_extruder_nr").extruder_nr, + z_seam_config, + part.wall_toolpaths); added_something |= wall_orderer.addToLayer(); } return added_something; @@ -2247,7 +2438,13 @@ std::optional FffGcodeWriter::getSeamAvoidingLocation(const Polygons& fil } } -bool FffGcodeWriter::processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const +bool FffGcodeWriter::processSkin( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const { const size_t top_bottom_extruder_nr = mesh.settings.get("top_bottom_extruder_nr").extruder_nr; const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr; @@ -2276,7 +2473,13 @@ bool FffGcodeWriter::processSkin(const SliceDataStorage& storage, LayerPlan& gco return added_something; } -bool FffGcodeWriter::processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part) const +bool FffGcodeWriter::processSkinPart( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part) const { bool added_something = false; @@ -2289,13 +2492,14 @@ bool FffGcodeWriter::processSkinPart(const SliceDataStorage& storage, LayerPlan& return added_something; } -void FffGcodeWriter::processRoofing(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SkinPart& skin_part, - bool& added_something) const +void FffGcodeWriter::processRoofing( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const { const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr; if (extruder_nr != roofing_extruder_nr) @@ -2313,16 +2517,30 @@ void FffGcodeWriter::processRoofing(const SliceDataStorage& storage, const Ratio skin_density = 1.0; const coord_t skin_overlap = 0; // skinfill already expanded over the roofing areas; don't overlap with perimeters const bool monotonic = mesh.settings.get("roofing_monotonic"); - processSkinPrintFeature(storage, gcode_layer, mesh, mesh_config, extruder_nr, skin_part.roofing_fill, mesh_config.roofing_config, pattern, roofing_angle, skin_overlap, skin_density, monotonic, added_something); + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + mesh_config, + extruder_nr, + skin_part.roofing_fill, + mesh_config.roofing_config, + pattern, + roofing_angle, + skin_overlap, + skin_density, + monotonic, + added_something); } -void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const SkinPart& skin_part, - bool& added_something) const +void FffGcodeWriter::processTopBottom( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const { if (skin_part.skin_fill.empty()) { @@ -2348,7 +2566,7 @@ void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, // generate skin_polygons and skin_lines const GCodePathConfig* skin_config = &mesh_config.skin_config; Ratio skin_density = 1.0; - const coord_t skin_overlap = 0; // Skin overlap offset is applied in skin.cpp more overlap might be beneficial for curved bridges, but makes it worse in general. + const coord_t skin_overlap = 0; // Skin overlap offset is applied in skin.cpp more overlap might be beneficial for curved bridges, but makes it worse in general. const bool bridge_settings_enabled = mesh.settings.get("bridge_settings_enabled"); const bool bridge_enable_more_layers = bridge_settings_enabled && mesh.settings.get("bridge_enable_more_layers"); const Ratio support_threshold = bridge_settings_enabled ? mesh.settings.get("bridge_skin_support_threshold") : 0.0_r; @@ -2480,23 +2698,38 @@ void FffGcodeWriter::processTopBottom(const SliceDataStorage& storage, } } const bool monotonic = mesh.settings.get("skin_monotonic"); - processSkinPrintFeature(storage, gcode_layer, mesh, mesh_config, extruder_nr, skin_part.skin_fill, *skin_config, pattern, skin_angle, skin_overlap, skin_density, monotonic, added_something, fan_speed); + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + mesh_config, + extruder_nr, + skin_part.skin_fill, + *skin_config, + pattern, + skin_angle, + skin_overlap, + skin_density, + monotonic, + added_something, + fan_speed); } -void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const SliceMeshStorage& mesh, - const PathConfigStorage::MeshPathConfigs& mesh_config, - const size_t extruder_nr, - const Polygons& area, - const GCodePathConfig& config, - EFillMethod pattern, - const AngleDegrees skin_angle, - const coord_t skin_overlap, - const Ratio skin_density, - const bool monotonic, - bool& added_something, - double fan_speed) const +void FffGcodeWriter::processSkinPrintFeature( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const size_t extruder_nr, + const Polygons& area, + const GCodePathConfig& config, + EFillMethod pattern, + const AngleDegrees skin_angle, + const coord_t skin_overlap, + const Ratio skin_density, + const bool monotonic, + bool& added_something, + double fan_speed) const { Polygons skin_polygons; Polygons skin_lines; @@ -2519,29 +2752,30 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; - Infill infill_comp(pattern, - zig_zaggify_infill, - connect_polygons, - area, - config.getLineWidth(), - config.getLineWidth() / skin_density, - skin_overlap, - infill_multiplier, - skin_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_line_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill infill_comp( + pattern, + zig_zaggify_infill, + connect_polygons, + area, + config.getLineWidth(), + config.getLineWidth() / skin_density, + skin_overlap, + infill_multiplier, + skin_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_line_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN); // add paths @@ -2557,23 +2791,28 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(mesh.settings.get("z_seam_type"), mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), config.getLineWidth() * 2); - InsetOrderOptimizer wall_orderer(*this, - storage, - gcode_layer, - mesh.settings, - extruder_nr, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - skin_extruder_nr, - skin_extruder_nr, - z_seam_config, - skin_paths); + const ZSeamConfig z_seam_config( + mesh.settings.get("z_seam_type"), + mesh.getZSeamHint(), + mesh.settings.get("z_seam_corner"), + config.getLineWidth() * 2); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + mesh.settings, + extruder_nr, + mesh_config.skin_config, + mesh_config.skin_config, + mesh_config.skin_config, + mesh_config.skin_config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + skin_extruder_nr, + skin_extruder_nr, + z_seam_config, + skin_paths); added_something |= wall_orderer.addToLayer(); } } @@ -2590,11 +2829,23 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, const AngleRadians monotonic_direction = AngleRadians(skin_angle); constexpr Ratio flow = 1.0_r; - const coord_t max_adjacent_distance = config.getLineWidth() * 1.1; // Lines are considered adjacent if they are 1 line width apart, with 10% extra play. The monotonic order is enforced if they are adjacent. - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + const coord_t max_adjacent_distance + = config.getLineWidth() + * 1.1; // Lines are considered adjacent if they are 1 line width apart, with 10% extra play. The monotonic order is enforced if they are adjacent. + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesMonotonic(area, skin_lines, config, SpaceFillType::Lines, monotonic_direction, max_adjacent_distance, exclude_distance, mesh.settings.get("infill_wipe_dist"), flow, fan_speed); + gcode_layer.addLinesMonotonic( + area, + skin_lines, + config, + SpaceFillType::Lines, + monotonic_direction, + max_adjacent_distance, + exclude_distance, + mesh.settings.get("infill_wipe_dist"), + flow, + fan_speed); } else { @@ -2606,7 +2857,8 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, else { std::optional near_start_location; - const EFillMethod pattern = (gcode_layer.getLayerNr() == 0) ? mesh.settings.get("top_bottom_pattern_0") : mesh.settings.get("top_bottom_pattern"); + const EFillMethod pattern + = (gcode_layer.getLayerNr() == 0) ? mesh.settings.get("top_bottom_pattern_0") : mesh.settings.get("top_bottom_pattern"); if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG) { // update near_start_location to a location which tries to avoid seams in skin near_start_location = getSeamAvoidingLocation(area, skin_angle, gcode_layer.getLastPlannedPositionOrStartingPosition()); @@ -2614,10 +2866,18 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, constexpr bool enable_travel_optimization = false; constexpr float flow = 1.0; - if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC - || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) + if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::LIGHTNING) { - gcode_layer.addLinesByOptimizer(skin_lines, config, SpaceFillType::Lines, enable_travel_optimization, mesh.settings.get("infill_wipe_dist"), flow, near_start_location, fan_speed); + gcode_layer.addLinesByOptimizer( + skin_lines, + config, + SpaceFillType::Lines, + enable_travel_optimization, + mesh.settings.get("infill_wipe_dist"), + flow, + near_start_location, + fan_speed); } else { @@ -2629,7 +2889,12 @@ void FffGcodeWriter::processSkinPrintFeature(const SliceDataStorage& storage, } } -bool FffGcodeWriter::processIroning(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const SliceLayer& layer, const GCodePathConfig& line_config, LayerPlan& gcode_layer) const +bool FffGcodeWriter::processIroning( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const SliceLayer& layer, + const GCodePathConfig& line_config, + LayerPlan& gcode_layer) const { bool added_something = false; const bool ironing_enabled = mesh.settings.get("ironing_enabled"); @@ -2658,8 +2923,8 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - size_t support_infill_extruder_nr = - (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + size_t support_infill_extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; if (support_layer.support_bottom.empty() && support_layer.support_roof.empty() && support_layer.support_infill_parts.empty()) @@ -2694,7 +2959,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const size_t extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; + const size_t extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr + : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; const ExtruderTrain& infill_extruder = Application::getInstance().current_slice->scene.extruders[extruder_nr]; coord_t default_support_line_distance = infill_extruder.settings.get("support_line_distance"); @@ -2782,7 +3048,22 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr coord_t wipe_dist = 0; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, infill_extruder.settings, extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, wall_toolpaths); + *this, + storage, + gcode_layer, + infill_extruder.settings, + extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + wall_toolpaths); added_something |= wall_orderer.addToLayer(); } @@ -2807,7 +3088,10 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } const unsigned int density_factor = 2 << density_idx; // == pow(2, density_idx + 1) - int support_line_distance_here = (part.custom_line_distance > 0 ? part.custom_line_distance : default_support_line_distance * density_factor); // the highest density infill combines with the next to create a grid with density_factor 1 + int support_line_distance_here + = (part.custom_line_distance > 0 + ? part.custom_line_distance + : default_support_line_distance * density_factor); // the highest density infill combines with the next to create a grid with density_factor 1 const int support_shift = support_line_distance_here / 2; if (part.custom_line_distance == 0 && (density_idx == max_density_idx || support_pattern == EFillMethod::CROSS || support_pattern == EFillMethod::CROSS_3D)) { @@ -2816,33 +3100,42 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const Polygons& area = part.infill_area_per_combine_per_density[density_idx][combine_idx]; constexpr size_t wall_count = 0; // Walls are generated somewhere else, so their layers aren't vertically combined. - const coord_t small_area_width = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width + = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. constexpr bool skip_stitching = false; const bool fill_gaps = density_idx == 0; // Only fill gaps for one of the densities. - Infill infill_comp(support_pattern, - zig_zaggify_infill, - connect_polygons, - area, - support_line_width, - support_line_distance_here, - current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), - infill_multiplier, - support_infill_angle, - gcode_layer.z, - support_shift, - max_resolution, - max_deviation, - wall_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - support_connect_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - infill_comp.generate(wall_toolpaths_here, support_polygons, support_lines, infill_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT, storage.support.cross_fill_provider); + Infill infill_comp( + support_pattern, + zig_zaggify_infill, + connect_polygons, + area, + support_line_width, + support_line_distance_here, + current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), + infill_multiplier, + support_infill_angle, + gcode_layer.z, + support_shift, + max_resolution, + max_deviation, + wall_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + support_connect_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + infill_comp.generate( + wall_toolpaths_here, + support_polygons, + support_lines, + infill_extruder.settings, + gcode_layer.getLayerNr(), + SectionType::SUPPORT, + storage.support.cross_fill_provider); } if (need_travel_to_end_of_last_spiral && infill_extruder.settings.get("magic_spiralize")) @@ -2884,7 +3177,15 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const std::optional start_near_location = std::optional(); gcode_layer.addPolygonsByOptimizer( - support_polygons, gcode_layer.configs_storage.support_infill_config[combine_idx], z_seam_config, wall_0_wipe_dist, spiralize, flow_ratio, always_retract, alternate_layer_print_direction, start_near_location); + support_polygons, + gcode_layer.configs_storage.support_infill_config[combine_idx], + z_seam_config, + wall_0_wipe_dist, + spiralize, + flow_ratio, + always_retract, + alternate_layer_print_direction, + start_near_location); added_something = true; } @@ -2896,15 +3197,16 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const std::optional near_start_location = std::optional(); constexpr double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; - gcode_layer.addLinesByOptimizer(support_lines, - gcode_layer.configs_storage.support_infill_config[combine_idx], - (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - near_start_location, - fan_speed, - alternate_layer_print_direction); + gcode_layer.addLinesByOptimizer( + support_lines, + gcode_layer.configs_storage.support_infill_config[combine_idx], + (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + near_start_location, + fan_speed, + alternate_layer_print_direction); added_something = true; } @@ -2917,9 +3219,28 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; constexpr coord_t simplify_curvature = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, simplify_curvature); + const ZSeamConfig z_seam_config( + EZSeamType::SHORTEST, + gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + simplify_curvature); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, infill_extruder.settings, extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, wall_toolpaths_here); + *this, + storage, + gcode_layer, + infill_extruder.settings, + extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + wall_toolpaths_here); added_something |= wall_orderer.addToLayer(); } } @@ -2984,29 +3305,30 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay infill_outline = wall.offset(-support_roof_line_width / 2); } - Infill roof_computation(pattern, - zig_zaggify_infill, - connect_polygons, - infill_outline, - gcode_layer.configs_storage.support_roof_config.getLineWidth(), - support_roof_line_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_outline, + gcode_layer.configs_storage.support_roof_config.getLineWidth(), + support_roof_line_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons roof_polygons; std::vector roof_paths; Polygons roof_lines; @@ -3034,10 +3356,28 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, roof_extruder.settings, roof_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, roof_extruder_nr, roof_extruder_nr, z_seam_config, roof_paths); + *this, + storage, + gcode_layer, + roof_extruder.settings, + roof_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + roof_extruder_nr, + roof_extruder_nr, + z_seam_config, + roof_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(roof_lines, gcode_layer.configs_storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer( + roof_lines, + gcode_layer.configs_storage.support_roof_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); return true; } @@ -3081,30 +3421,32 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L const coord_t max_resolution = bottom_extruder.settings.get("meshfix_maximum_resolution"); const coord_t max_deviation = bottom_extruder.settings.get("meshfix_maximum_deviation"); - const coord_t support_bottom_line_distance = bottom_extruder.settings.get("support_bottom_line_distance"); // note: no need to apply initial line width factor; support bottoms cannot exist on the first layer - Infill bottom_computation(pattern, - zig_zaggify_infill, - connect_polygons, - support_layer.support_bottom, - gcode_layer.configs_storage.support_bottom_config.getLineWidth(), - support_bottom_line_distance, - support_bottom_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); + const coord_t support_bottom_line_distance = bottom_extruder.settings.get( + "support_bottom_line_distance"); // note: no need to apply initial line width factor; support bottoms cannot exist on the first layer + Infill bottom_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + support_layer.support_bottom, + gcode_layer.configs_storage.support_bottom_config.getLineWidth(), + support_bottom_line_distance, + support_bottom_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons bottom_polygons; std::vector bottom_paths; Polygons bottom_lines; @@ -3128,10 +3470,28 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( - *this, storage, gcode_layer, bottom_extruder.settings, bottom_extruder_nr, config, config, config, config, retract_before_outer_wall, wipe_dist, wipe_dist, bottom_extruder_nr, bottom_extruder_nr, z_seam_config, bottom_paths); + *this, + storage, + gcode_layer, + bottom_extruder.settings, + bottom_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + bottom_extruder_nr, + bottom_extruder_nr, + z_seam_config, + bottom_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(bottom_lines, gcode_layer.configs_storage.support_bottom_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer( + bottom_lines, + gcode_layer.configs_storage.support_bottom_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); return true; } @@ -3238,7 +3598,7 @@ void FffGcodeWriter::finalize() // set extrusion mode back to "normal" gcode.resetExtrusionMode(); - for (size_t e = 0; e < Application::getInstance().current_slice->scene.extruders.size(); e ++) + for (size_t e = 0; e < Application::getInstance().current_slice->scene.extruders.size(); e++) { gcode.writeTemperatureCommand(e, 0, false); } From 5d77157376df6b94fe96c3f73e5c725a8bd98eeb Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 3 Aug 2023 12:43:30 +0200 Subject: [PATCH 288/656] skirt printed before support The two printings are scheduled one after the other CURA-10758 --- src/FffGcodeWriter.cpp | 53 +++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 59810a8c87..bb1b7aa21b 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -391,6 +391,7 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } +<<<<<<< HEAD else if ( (adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) && skirt_brim_extruder && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) @@ -398,6 +399,17 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) start_extruder_nr = skirt_brim_extruder->extruder_nr; } else if (adhesion_type == EPlatformAdhesion::RAFT && skirt_brim_extruder) +======= + else if ((adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) + && skirt_brim_extruder + && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) + { + start_extruder_nr = skirt_brim_extruder->extruder_nr; + } + else if (adhesion_type == EPlatformAdhesion::RAFT + && skirt_brim_extruder + ) +>>>>>>> 595ef2bf5 (skirt printed before support) { start_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; } @@ -1116,17 +1128,6 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan } Polygons all_brim_lines; - // Add the support brim before the below algorithm which takes order requirements into account - // For support brim we don't care about the order, because support doesn't need to be accurate. - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) - { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - all_brim_lines = support_brim_lines; - } - all_brim_lines.reserve(total_line_count); const coord_t line_w = train.settings.get("skirt_brim_line_width") * train.settings.get("initial_layer_line_width_factor"); @@ -1223,7 +1224,8 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan Polygons inner_brim_line; inner_brim_line.add(all_brim_lines[0]); - gcode_layer.addLinesByOptimizer( + gcode_layer.addLinesByOptimizer + ( layer_nr == 0 ? all_brim_lines : inner_brim_line, gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], SpaceFillType::PolyLines, @@ -1233,7 +1235,32 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan start_close_to, fan_speed, reverse_print_direction, - order_requirements); + order_requirements + ); + } + + + // Add the support brim after the skirt_brim to gcode_layer + // For support brim we don't care about the order, because support doesn't need to be accurate. + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + { + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer + ( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {} + ); } } From 41c49914b99fc0abd24e70685de7cc2603437041 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 3 Aug 2023 16:43:39 +0200 Subject: [PATCH 289/656] merge fix CURA-10758 --- src/FffGcodeWriter.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 22e69d43e8..5bd62fb8c9 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -391,7 +391,7 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) { start_extruder_nr = skirt_brim_extruder->extruder_nr; } -<<<<<<< HEAD + else if ( (adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) && skirt_brim_extruder && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) @@ -399,17 +399,6 @@ size_t FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage) start_extruder_nr = skirt_brim_extruder->extruder_nr; } else if (adhesion_type == EPlatformAdhesion::RAFT && skirt_brim_extruder) -======= - else if ((adhesion_type == EPlatformAdhesion::BRIM || mesh_group_settings.get("prime_tower_brim_enable")) - && skirt_brim_extruder - && (skirt_brim_extruder->settings.get("brim_line_count") > 0 || skirt_brim_extruder->settings.get("skirt_brim_minimal_length") > 0)) - { - start_extruder_nr = skirt_brim_extruder->extruder_nr; - } - else if (adhesion_type == EPlatformAdhesion::RAFT - && skirt_brim_extruder - ) ->>>>>>> 595ef2bf5 (skirt printed before support) { start_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; } From 828dd5574bacf00684166233c9fe064686d01b31 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Thu, 3 Aug 2023 14:44:45 +0000 Subject: [PATCH 290/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 48 +++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 5bd62fb8c9..fd836e57a8 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1213,19 +1213,17 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan Polygons inner_brim_line; inner_brim_line.add(all_brim_lines[0]); - gcode_layer.addLinesByOptimizer - ( - layer_nr == 0 ? all_brim_lines : inner_brim_line, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements - ); + gcode_layer.addLinesByOptimizer( + layer_nr == 0 ? all_brim_lines : inner_brim_line, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements); } @@ -1237,19 +1235,17 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan total_line_count += storage.support_brim.size(); Polygons support_brim_lines = storage.support_brim; support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer - ( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {} - ); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); } } From 59ff35f8076b654131d7907ea01a213a236c8ee5 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 4 Aug 2023 13:04:15 +0200 Subject: [PATCH 291/656] Revert "Disable Small Skin Area feature for now :-/" This reverts commit 0b15442458d98541b735406be782d0cff714609c. --- include/infill.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/infill.h b/include/infill.h index c6ce9ce4cd..2f3e4fb287 100644 --- a/include/infill.h +++ b/include/infill.h @@ -91,7 +91,7 @@ class Infill , max_resolution(max_resolution) , max_deviation(max_deviation) , wall_line_count(wall_line_count) - , small_area_width(0) // FIXME!: Disable small_area_width for the 5.4.x releases. Current plan is to figure out why this feature causes small line segments & fix that before 5.5.x + , small_area_width(small_area_width) , infill_origin(infill_origin) , skip_line_stitching(skip_line_stitching) , fill_gaps(fill_gaps) From 197cece728bd7762f9184f6ff4d3b6fe0a9d8b10 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 4 Aug 2023 14:19:05 +0200 Subject: [PATCH 292/656] Fix some edge cases with small top/bottom area setting. - When a thick circular curve is close to the small area width, the polygonal nature of the models would make it so that it would alternate between small skin areas and normal top-fill areas within that arc. Fix this by removing small holes before doing the second part of the morphological operation (either open or close, depending on wether you view it from the inner contour or the small infill areas point of view). - Both areas added together should completely cover the complete 'original' inner contour. Yet only one of them was simplified (at this juncture at least). In fact, there are paths through the code where the inner contour doesn't get simplified at all after this point. If it as in a simplified state before we start doing the small areas procedure, as it likely was in a lot of cases, then this could introduce new issues. done as part of CURA-10670 --- src/infill.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 26911124dc..553973f3e3 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -93,11 +93,15 @@ void Infill::generate(std::vector& toolpaths, // infill pattern is concentric or if the small_area_width is zero. if (pattern != EFillMethod::CONCENTRIC && small_area_width > 0) { + const auto to_small_length = INT2MM(static_cast(infill_line_width) / 2.0); + // Split the infill region in a narrow region and the normal region. Polygons small_infill = inner_contour; - inner_contour = inner_contour.offset(-small_area_width / 2).offset(small_area_width / 2); + inner_contour = inner_contour.offset(-small_area_width / 2); + inner_contour.removeSmallAreas(to_small_length * to_small_length, true); + inner_contour = inner_contour.offset(small_area_width / 2); + inner_contour = Simplify(max_resolution, max_deviation, 0).polygon(inner_contour); small_infill = small_infill.difference(inner_contour); - small_infill = Simplify(max_resolution, max_deviation, 0).polygon(small_infill); // Small corners of a bigger area should not be considered narrow and are therefore added to the bigger area again. auto small_infill_parts = small_infill.splitIntoParts(); From 8ba4968113651390a9c13a89596617148878a685 Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 4 Aug 2023 12:26:11 +0000 Subject: [PATCH 293/656] Applied clang-format. --- include/infill.h | 228 +++++++++++++++++++++++++++-------------------- src/infill.cpp | 216 +++++++++++++++++++++++++++++--------------- 2 files changed, 273 insertions(+), 171 deletions(-) diff --git a/include/infill.h b/include/infill.h index 2f3e4fb287..98276f4741 100644 --- a/include/infill.h +++ b/include/infill.h @@ -7,8 +7,8 @@ #include "infill/LightningGenerator.h" #include "infill/ZigzagConnectorProcessor.h" #include "settings/EnumSettings.h" //For infill types. -#include "settings/types/Angle.h" #include "settings/Settings.h" +#include "settings/types/Angle.h" #include "utils/ExtrusionLine.h" #include "utils/IntPoint.h" #include "utils/section_type.h" @@ -20,7 +20,7 @@ class AABB; class SierpinskiFillProvider; class SliceMeshStorage; -class Infill +class Infill { friend class InfillTest; @@ -46,71 +46,73 @@ class Infill bool fill_gaps; //!< Whether to fill gaps in strips of infill that would be too thin to fit the infill lines. If disabled, those areas are left empty. bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself - bool skip_some_zags; //!< (ZigZag) Whether to skip some zags - size_t zag_skip_count; //!< (ZigZag) To skip one zag in every N if skip some zags is enabled + bool skip_some_zags; //!< (ZigZag) Whether to skip some zags + size_t zag_skip_count; //!< (ZigZag) To skip one zag in every N if skip some zags is enabled coord_t pocket_size; //!< The size of the pockets at the intersections of the fractal in the cross 3d pattern bool mirror_offset; //!< Indication in which offset direction the extra infill lines are made static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0) public: - Infill(EFillMethod pattern - , bool zig_zaggify - , bool connect_polygons - , const Polygons& in_outline - , coord_t infill_line_width - , coord_t line_distance - , coord_t infill_overlap - , size_t infill_multiplier - , AngleDegrees fill_angle - , coord_t z - , coord_t shift - , coord_t max_resolution - , coord_t max_deviation - , size_t wall_line_count = 0 - , coord_t small_area_width = 0 - , const Point& infill_origin = Point() - , bool skip_line_stitching = false - , bool fill_gaps = true - , bool connected_zigzags = false - , bool use_endpieces = false - , bool skip_some_zags = false - , size_t zag_skip_count = 0 - , coord_t pocket_size = 0 - ) - : pattern(pattern) - , zig_zaggify(zig_zaggify) - , connect_polygons(connect_polygons) - , outer_contour(in_outline) - , infill_line_width(infill_line_width) - , line_distance(line_distance) - , infill_overlap(infill_overlap) - , infill_multiplier(infill_multiplier) - , fill_angle(fill_angle) - , z(z) - , shift(shift) - , max_resolution(max_resolution) - , max_deviation(max_deviation) - , wall_line_count(wall_line_count) - , small_area_width(small_area_width) - , infill_origin(infill_origin) - , skip_line_stitching(skip_line_stitching) - , fill_gaps(fill_gaps) - , connected_zigzags(connected_zigzags) - , use_endpieces(use_endpieces) - , skip_some_zags(skip_some_zags) - , zag_skip_count(zag_skip_count) - , pocket_size(pocket_size) - , mirror_offset(zig_zaggify) + Infill( + EFillMethod pattern, + bool zig_zaggify, + bool connect_polygons, + const Polygons& in_outline, + coord_t infill_line_width, + coord_t line_distance, + coord_t infill_overlap, + size_t infill_multiplier, + AngleDegrees fill_angle, + coord_t z, + coord_t shift, + coord_t max_resolution, + coord_t max_deviation, + size_t wall_line_count = 0, + coord_t small_area_width = 0, + const Point& infill_origin = Point(), + bool skip_line_stitching = false, + bool fill_gaps = true, + bool connected_zigzags = false, + bool use_endpieces = false, + bool skip_some_zags = false, + size_t zag_skip_count = 0, + coord_t pocket_size = 0) + : pattern(pattern) + , zig_zaggify(zig_zaggify) + , connect_polygons(connect_polygons) + , outer_contour(in_outline) + , infill_line_width(infill_line_width) + , line_distance(line_distance) + , infill_overlap(infill_overlap) + , infill_multiplier(infill_multiplier) + , fill_angle(fill_angle) + , z(z) + , shift(shift) + , max_resolution(max_resolution) + , max_deviation(max_deviation) + , wall_line_count(wall_line_count) + , small_area_width(small_area_width) + , infill_origin(infill_origin) + , skip_line_stitching(skip_line_stitching) + , fill_gaps(fill_gaps) + , connected_zigzags(connected_zigzags) + , use_endpieces(use_endpieces) + , skip_some_zags(skip_some_zags) + , zag_skip_count(zag_skip_count) + , pocket_size(pocket_size) + , mirror_offset(zig_zaggify) { - //TODO: The connected lines algorithm is only available for linear-based infill, for now. - //We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. - //Cubic Subdivision ends lines in the center of the infill so it won't be effective. - connect_lines = zig_zaggify && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); + // TODO: The connected lines algorithm is only available for linear-based infill, for now. + // We skip ZigZag, Cross and Cross3D because they have their own algorithms. Eventually we want to replace all that with the new algorithm. + // Cubic Subdivision ends lines in the center of the infill so it won't be effective. + connect_lines = zig_zaggify + && (pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC + || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON); } /*! * Generate the infill. - * + * * \param toolpaths (output) The resulting variable-width paths (from the extra walls around the pattern). Binned by inset_idx. * \param result_polygons (output) The resulting polygons (from concentric infill) * \param result_lines (output) The resulting line segments (from linear infill types) @@ -119,7 +121,16 @@ class Infill * \param mesh A mesh for which to generate infill (should only be used for non-helper-mesh objects). * \param[in] cross_fill_provider The cross fractal subdivision decision functor */ - void generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, int layer_idx, SectionType section_type, const SierpinskiFillProvider* cross_fill_provider = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); + void generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + int layer_idx, + SectionType section_type, + const SierpinskiFillProvider* cross_fill_provider = nullptr, + const LightningLayer* lightning_layer = nullptr, + const SliceMeshStorage* mesh = nullptr); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -133,21 +144,37 @@ class Infill * \param settings [in] A settings storage to use for generating variable-width walls. * \return The inner contour of the wall toolpaths */ - static Polygons generateWallToolPaths(std::vector& toolpaths, Polygons& outer_contour, const size_t wall_line_count, const coord_t line_width, const coord_t infill_overlap, const Settings& settings, int layer_idx, SectionType section_type); + static Polygons generateWallToolPaths( + std::vector& toolpaths, + Polygons& outer_contour, + const size_t wall_line_count, + const coord_t line_width, + const coord_t infill_overlap, + const Settings& settings, + int layer_idx, + SectionType section_type); + private: /*! * Generate the infill pattern without the infill_multiplier functionality */ - void _generate(std::vector& toolpaths, Polygons& result_polygons, Polygons& result_lines, const Settings& settings, const SierpinskiFillProvider* cross_fill_pattern = nullptr, const LightningLayer * lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); + void _generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_pattern = nullptr, + const LightningLayer* lightning_layer = nullptr, + const SliceMeshStorage* mesh = nullptr); /*! * Multiply the infill lines, so that any single line becomes [infill_multiplier] lines next to each other. - * + * * This is done in a way such that there is not overlap between the lines * except the middle original one if the multiplier is odd. - * + * * This introduces a lot of line segments. - * + * * \param[in,out] result_polygons The polygons to be multiplied (input and output) * \param[in,out] result_lines The lines to be multiplied (input and output) */ @@ -171,9 +198,7 @@ class Infill , end_segment(end_segment) , end_polygon(end_polygon) , previous(nullptr) - , next(nullptr) - { - }; + , next(nullptr){}; /*! * Where the line segment starts. @@ -236,7 +261,7 @@ class Infill * This is necessary for putting line segments in a hash set. * \param other The line segment to compare this line segment with. */ - bool operator ==(const InfillLineSegment& other) const; + bool operator==(const InfillLineSegment& other) const; }; /*! @@ -252,7 +277,7 @@ class Infill * \param result_polygons (output) The resulting polygons, if zigzagging accidentally happened to connect gyroid lines in a circle. */ void generateGyroidInfill(Polygons& result_polylines, Polygons& result_polygons); - + /*! * Generate lightning fill aka minfill aka 'Ribbed Support Vault Infill', see Tricard,Claux,Lefebvre/'Ribbed Support Vaults for 3D Printing of Hollowed Objects' * see https://hal.archives-ouvertes.fr/hal-02155929/document @@ -262,7 +287,7 @@ class Infill /*! * Generate sparse concentric infill - * + * * \param toolpaths (output) The resulting toolpaths. Binned by inset_idx. * \param inset_value The offset between each consecutive two polygons */ @@ -295,7 +320,7 @@ class Infill /*! * Generate a single shifting square grid of infill lines. * This is used in tetrahedral infill (Octet infill) and in Quarter Cubic infill. - * + * * \param pattern_z_shift The amount by which to shift the whole pattern down * \param angle_shift The angle to add to the infill_angle * \param[out] result (output) The resulting lines @@ -332,94 +357,101 @@ class Infill /*! * Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule * \param[out] result (output) The resulting lines - * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill + * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill * \param scanline_min_idx The lowest index of all scanlines crossing the polygon * \param line_distance The distance between two lines which are in the same direction * \param boundary The axis aligned boundary box within which the polygon is * \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline * \param total_shift total shift of the scanlines in the direction perpendicular to the fill_angle. */ - void addLineInfill( Polygons& result, - const PointMatrix& rotation_matrix, - const int scanline_min_idx, - const int line_distance, - const AABB boundary, - std::vector>& cut_list, - coord_t total_shift); + void addLineInfill( + Polygons& result, + const PointMatrix& rotation_matrix, + const int scanline_min_idx, + const int line_distance, + const AABB boundary, + std::vector>& cut_list, + coord_t total_shift); /*! * generate lines within the area of \p in_outline, at regular intervals of \p line_distance - * + * * idea: * intersect a regular grid of 'scanlines' with the area inside \p in_outline - * + * * \param[out] result (output) The resulting lines * \param line_distance The distance between two lines which are in the same direction * \param infill_rotation The angle of the generated lines * \param extra_shift extra shift of the scanlines in the direction perpendicular to the infill_rotation */ void generateLineInfill(Polygons& result, int line_distance, const double& infill_rotation, coord_t extra_shift); - + /*! * Function for creating linear based infill types (Lines, ZigZag). - * + * * This function implements the basic functionality of Infill::generateLineInfill (see doc of that function), * but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection. - * + * * It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill. * * \param[out] result (output) The resulting lines * \param line_distance The distance between two lines which are in the same direction - * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill + * \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill * \param zigzag_connector_processor The processor used to generate zigzag connectors * \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line * \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle */ - void generateLinearBasedInfill(Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, coord_t extra_shift); + void generateLinearBasedInfill( + Polygons& result, + const int line_distance, + const PointMatrix& rotation_matrix, + ZigzagConnectorProcessor& zigzag_connector_processor, + const bool connected_zigzags, + coord_t extra_shift); /*! - * + * * generate lines within the area of [in_outline], at regular intervals of [line_distance] * idea: * intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill) * zigzag: * include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_| - * + * * Note that ZigZag consists of 3 types: * - without endpieces * - with disconnected endpieces * - with connected endpieces - * + * * <-- * ___ * | | | * | | | * | |___| * --> - * + * * ^ = even scanline * ^ ^ no endpieces - * + * * start boundary from even scanline! :D - * - * + * + * * v disconnected end piece: leave out last line segment * _____ * | | | \ . * | | | | * |_____| |__/ - * + * * ^ ^ ^ scanlines - * - * + * + * * v connected end piece * ________ * | | | \ . * | | | | * |_____| |__/ . - * + * * ^ ^ ^ scanlines - * + * * \param[out] result (output) The resulting lines * \param line_distance The distance between two lines which are in the same direction * \param infill_rotation The angle of the generated lines @@ -446,6 +478,6 @@ class Infill void connectLines(Polygons& result_lines); }; -}//namespace cura +} // namespace cura #endif // INFILL_H diff --git a/src/infill.cpp b/src/infill.cpp index 553973f3e3..812d2e45ba 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -1,15 +1,9 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include //For std::sort. -#include -#include - -#include -#include +#include "infill.h" #include "WallToolPaths.h" -#include "infill.h" #include "infill/GyroidInfill.h" #include "infill/ImageBasedDensityProvider.h" #include "infill/LightningGenerator.h" @@ -25,6 +19,13 @@ #include "utils/UnionFind.h" #include "utils/polygonUtils.h" +#include +#include + +#include //For std::sort. +#include +#include + /*! * Function which returns the scanline_idx for a given x coordinate * @@ -49,7 +50,15 @@ static inline int computeScanSegmentIdx(int x, int line_width) namespace cura { -Polygons Infill::generateWallToolPaths(std::vector& toolpaths, Polygons& outer_contour, const size_t wall_line_count, const coord_t line_width, const coord_t infill_overlap, const Settings& settings, int layer_idx, SectionType section_type) +Polygons Infill::generateWallToolPaths( + std::vector& toolpaths, + Polygons& outer_contour, + const size_t wall_line_count, + const coord_t line_width, + const coord_t infill_overlap, + const Settings& settings, + int layer_idx, + SectionType section_type) { outer_contour = outer_contour.offset(infill_overlap); scripta::log("infill_outer_contour", outer_contour, section_type, layer_idx, scripta::CellVDI{ "infill_overlap", infill_overlap }); @@ -69,15 +78,16 @@ Polygons Infill::generateWallToolPaths(std::vector& toolpath return inner_contour; } -void Infill::generate(std::vector& toolpaths, - Polygons& result_polygons, - Polygons& result_lines, - const Settings& settings, - int layer_idx, - SectionType section_type, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) +void Infill::generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + int layer_idx, + SectionType section_type, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh) { if (outer_contour.empty()) { @@ -108,10 +118,8 @@ void Infill::generate(std::vector& toolpaths, small_infill.clear(); for (const auto& small_infill_part : small_infill_parts) { - if ( - small_infill_part.offset(-infill_line_width / 2).offset(infill_line_width / 2).area() < infill_line_width * infill_line_width * 10 - && ! inner_contour.intersection(small_infill_part.offset(infill_line_width / 4)).empty() - ) + if (small_infill_part.offset(-infill_line_width / 2).offset(infill_line_width / 2).area() < infill_line_width * infill_line_width * 10 + && ! inner_contour.intersection(small_infill_part.offset(infill_line_width / 4)).empty()) { inner_contour.add(small_infill_part); } @@ -127,12 +135,16 @@ void Infill::generate(std::vector& toolpaths, const size_t narrow_wall_count = small_area_width / infill_line_width + 1; WallToolPaths wall_toolpaths(small_infill, infill_line_width, narrow_wall_count, 0, settings, layer_idx, section_type); std::vector small_infill_paths = wall_toolpaths.getToolPaths(); - scripta::log("infill_small_infill_paths_0", small_infill_paths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "infill_small_infill_paths_0", + small_infill_paths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); for (const auto& small_infill_path : small_infill_paths) { toolpaths.emplace_back(small_infill_path); @@ -144,9 +156,11 @@ void Infill::generate(std::vector& toolpaths, if (pattern == EFillMethod::ZIG_ZAG // Zig-zag prints the zags along the walls. || (zig_zaggify && (pattern == EFillMethod::LINES // Zig-zaggified infill patterns print their zags along the walls. - || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON - || pattern == EFillMethod::GYROID || pattern == EFillMethod::CROSS || pattern == EFillMethod::CROSS_3D)) - || infill_multiplier % 2 == 0) // Multiplied infill prints loops of infill, partly along the walls, if even. For odd multipliers >1 it gets offset by the multiply algorithm itself. + || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::GRID || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL + || pattern == EFillMethod::QUARTER_CUBIC || pattern == EFillMethod::TRIHEXAGON || pattern == EFillMethod::GYROID || pattern == EFillMethod::CROSS + || pattern == EFillMethod::CROSS_3D)) + || infill_multiplier % 2 + == 0) // Multiplied infill prints loops of infill, partly along the walls, if even. For odd multipliers >1 it gets offset by the multiply algorithm itself. { inner_contour = inner_contour.offset(-infill_line_width / 2); inner_contour = Simplify(max_resolution, max_deviation, 0).polygon(inner_contour); @@ -181,17 +195,27 @@ void Infill::generate(std::vector& toolpaths, } scripta::log("infill_result_polygons_0", result_polygons, section_type, layer_idx); scripta::log("infill_result_lines_0", result_lines, section_type, layer_idx); - scripta::log("infill_toolpaths_0", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "infill_toolpaths_0", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); if (connect_polygons) { // remove too small polygons coord_t snap_distance = infill_line_width * 2; // polygons with a span of max 1 * nozzle_size are too small - auto it = std::remove_if(result_polygons.begin(), result_polygons.end(), [snap_distance](PolygonRef poly) { return poly.shorterThan(snap_distance); }); + auto it = std::remove_if( + result_polygons.begin(), + result_polygons.end(), + [snap_distance](PolygonRef poly) + { + return poly.shorterThan(snap_distance); + }); result_polygons.erase(it, result_polygons.end()); PolygonConnector connector(infill_line_width); @@ -204,22 +228,27 @@ void Infill::generate(std::vector& toolpaths, toolpaths = connected_paths; scripta::log("infill_result_polygons_1", result_polygons, section_type, layer_idx); scripta::log("infill_result_lines_1", result_lines, section_type, layer_idx); - scripta::log("infill_toolpaths_1", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "infill_toolpaths_1", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); } } -void Infill::_generate(std::vector& toolpaths, - Polygons& result_polygons, - Polygons& result_lines, - const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) +void Infill::_generate( + std::vector& toolpaths, + Polygons& result_polygons, + Polygons& result_lines, + const Settings& settings, + const SierpinskiFillProvider* cross_fill_provider, + const LightningLayer* lightning_trees, + const SliceMeshStorage* mesh) { if (inner_contour.empty()) return; @@ -295,7 +324,9 @@ void Infill::_generate(std::vector& toolpaths, Simplify simplifier(max_resolution, max_deviation, 0); result_polygons = simplifier.polygon(result_polygons); - if (! skip_line_stitching && (zig_zaggify || pattern == EFillMethod::CROSS || pattern == EFillMethod::CROSS_3D || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::GYROID || pattern == EFillMethod::ZIG_ZAG)) + if (! skip_line_stitching + && (zig_zaggify || pattern == EFillMethod::CROSS || pattern == EFillMethod::CROSS_3D || pattern == EFillMethod::CUBICSUBDIV || pattern == EFillMethod::GYROID + || pattern == EFillMethod::ZIG_ZAG)) { // don't stich for non-zig-zagged line infill types Polygons stitched_lines; PolylineStitcher::stitch(result_lines, stitched_lines, result_polygons, infill_line_width); @@ -322,7 +353,8 @@ void Infill::multiplyInfill(Polygons& result_polygons, Polygons& result_lines) const Polygons first_offset_polygons_inward = result_polygons.offset(-offset); // make lines on the inside of the input polygons const Polygons first_offset_polygons_outward = result_polygons.offset(offset); // make lines on the other side of the input polygons const Polygons first_offset_polygons = first_offset_polygons_outward.difference(first_offset_polygons_inward); - first_offset = first_offset_lines.unionPolygons(first_offset_polygons); // usually we only have either lines or polygons, but this code also handles an infill pattern which generates both + first_offset = first_offset_lines.unionPolygons( + first_offset_polygons); // usually we only have either lines or polygons, but this code also handles an infill pattern which generates both if (zig_zaggify) { first_offset = inner_contour.difference(first_offset); @@ -409,7 +441,8 @@ void Infill::generateConcentricInfill(std::vector& toolpaths constexpr size_t inset_wall_count = 1; // 1 wall at a time. constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. - WallToolPaths wall_toolpaths(current_inset, infill_line_width, inset_wall_count, wall_0_inset, settings, 0, SectionType::CONCENTRIC_INFILL); // FIXME: @jellespijker pass the correct layer + WallToolPaths wall_toolpaths(current_inset, infill_line_width, inset_wall_count, wall_0_inset, settings, 0, SectionType::CONCENTRIC_INFILL); // FIXME: @jellespijker pass + // the correct layer const std::vector inset_paths = wall_toolpaths.getToolPaths(); toolpaths.insert(toolpaths.end(), inset_paths.begin(), inset_paths.end()); @@ -503,7 +536,14 @@ void Infill::generateCrossInfill(const SierpinskiFillProvider& cross_fill_provid } } -void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector>& cut_list, coord_t shift) +void Infill::addLineInfill( + Polygons& result, + const PointMatrix& rotation_matrix, + const int scanline_min_idx, + const int line_distance, + const AABB boundary, + std::vector>& cut_list, + coord_t shift) { assert(! connect_lines && "connectLines() should add the infill lines, not addLineInfill"); @@ -580,7 +620,13 @@ void Infill::generateZigZagInfill(Polygons& result, const coord_t line_distance, * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment, * while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment. */ -void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, coord_t extra_shift) +void Infill::generateLinearBasedInfill( + Polygons& result, + const int line_distance, + const PointMatrix& rotation_matrix, + ZigzagConnectorProcessor& zigzag_connector_processor, + const bool connected_zigzags, + coord_t extra_shift) { if (line_distance == 0 || inner_contour.empty()) // No infill to generate (0% density) or no area to generate it in. { @@ -611,7 +657,10 @@ void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance // Then we can later join two crossings together to form lines and still know what polygon line segments that infill line connected to. struct Crossing { - Crossing(Point coordinate, size_t polygon_index, size_t vertex_index) : coordinate(coordinate), polygon_index(polygon_index), vertex_index(vertex_index){}; + Crossing(Point coordinate, size_t polygon_index, size_t vertex_index) + : coordinate(coordinate) + , polygon_index(polygon_index) + , vertex_index(vertex_index){}; Point coordinate; size_t polygon_index; size_t vertex_index; @@ -660,12 +709,14 @@ void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance if (p0.X < p1.X) { scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment - scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >) + scanline_idx1 + = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >) } else { direction = -1; - scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >) + scanline_idx0 + = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >) scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment } @@ -705,7 +756,8 @@ void Infill::generateLinearBasedInfill(Polygons& result, const int line_distance { continue; } - InfillLineSegment* new_segment = new InfillLineSegment(unrotated_first, first.vertex_index, first.polygon_index, unrotated_second, second.vertex_index, second.polygon_index); + InfillLineSegment* new_segment + = new InfillLineSegment(unrotated_first, first.vertex_index, first.polygon_index, unrotated_second, second.vertex_index, second.polygon_index); // Put the same line segment in the data structure twice: Once for each of the polygon line segment that it crosses. crossings_on_line[first.polygon_index][first.vertex_index].push_back(new_segment); crossings_on_line[second.polygon_index][second.vertex_index].push_back(new_segment); @@ -764,15 +816,18 @@ void Infill::connectLines(Polygons& result_lines) Point vertex_after = inner_contour_polygon[vertex_index]; // Sort crossings on every line by how far they are from their initial point. - std::sort(crossings_on_polygon_segment.begin(), - crossings_on_polygon_segment.end(), - [&vertex_before, polygon_index, vertex_index](InfillLineSegment* left_hand_side, InfillLineSegment* right_hand_side) - { - // Find the two endpoints that are relevant. - const Point left_hand_point = (left_hand_side->start_segment == vertex_index && left_hand_side->start_polygon == polygon_index) ? left_hand_side->start : left_hand_side->end; - const Point right_hand_point = (right_hand_side->start_segment == vertex_index && right_hand_side->start_polygon == polygon_index) ? right_hand_side->start : right_hand_side->end; - return vSize(left_hand_point - vertex_before) < vSize(right_hand_point - vertex_before); - }); + std::sort( + crossings_on_polygon_segment.begin(), + crossings_on_polygon_segment.end(), + [&vertex_before, polygon_index, vertex_index](InfillLineSegment* left_hand_side, InfillLineSegment* right_hand_side) + { + // Find the two endpoints that are relevant. + const Point left_hand_point + = (left_hand_side->start_segment == vertex_index && left_hand_side->start_polygon == polygon_index) ? left_hand_side->start : left_hand_side->end; + const Point right_hand_point + = (right_hand_side->start_segment == vertex_index && right_hand_side->start_polygon == polygon_index) ? right_hand_side->start : right_hand_side->end; + return vSize(left_hand_point - vertex_before) < vSize(right_hand_point - vertex_before); + }); for (InfillLineSegment* crossing : crossings_on_polygon_segment) { @@ -787,14 +842,16 @@ void Infill::connectLines(Polygons& result_lines) assert(crossing_handle != (size_t)-1); const size_t previous_crossing_handle = connected_lines.find(previous_crossing); assert(previous_crossing_handle != (size_t)-1); - if (crossing_handle == previous_crossing_handle) // These two infill lines are already connected. Don't create a loop now. Continue connecting with the next crossing. + if (crossing_handle + == previous_crossing_handle) // These two infill lines are already connected. Don't create a loop now. Continue connecting with the next crossing. { continue; } // Join two infill lines together with a connecting line. // Here the InfillLineSegments function as a linked list, so that they can easily be joined. - const Point previous_point = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) ? previous_segment->start : previous_segment->end; + const Point previous_point + = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) ? previous_segment->start : previous_segment->end; const Point next_point = (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index) ? crossing->start : crossing->end; InfillLineSegment* new_segment; // If the segment is zero length, we avoid creating it but still want to connect the crossing with the previous segment @@ -812,7 +869,8 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); // A connecting line between them. + new_segment + = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); // A connecting line between them. new_segment->previous = previous_segment; if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) { @@ -853,7 +911,13 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment(previous_segment->start, vertex_index, polygon_index, vertex_after, (vertex_index + 1) % inner_contour[polygon_index].size(), polygon_index); + new_segment = new InfillLineSegment( + previous_segment->start, + vertex_index, + polygon_index, + vertex_after, + (vertex_index + 1) % inner_contour[polygon_index].size(), + polygon_index); previous_segment->previous = new_segment; new_segment->previous = previous_segment; previous_segment = new_segment; @@ -869,7 +933,13 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment(previous_segment->end, vertex_index, polygon_index, vertex_after, (vertex_index + 1) % inner_contour[polygon_index].size(), polygon_index); + new_segment = new InfillLineSegment( + previous_segment->end, + vertex_index, + polygon_index, + vertex_after, + (vertex_index + 1) % inner_contour[polygon_index].size(), + polygon_index); previous_segment->next = new_segment; new_segment->previous = previous_segment; previous_segment = new_segment; From bba52adb4f81c072a6d67696a6d9ae1d40a157ab Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 6 Aug 2023 12:17:26 +0200 Subject: [PATCH 294/656] Send allong flattend mesh settings In this commit, the functionality to transfer flattened mesh settings to plugins was added. This was necessary to ensure additional mesh settings could be understood and implemented by the plugins in the system. A new utility function "getFlattendSettings" that returns flattened mesh settings was introduced in the settings module. This functions gathers settings from the parent containers as well as child containers for completeness. Finally, changes were made to the infill operation to adapt it to the new functionality. The flattened mesh settings are now sent to plugin function calls. Contributes to CURA-10619 --- include/plugins/converters.h | 8 +++++++- include/settings/Settings.h | 6 ++++-- src/infill.cpp | 2 +- src/settings/Settings.cpp | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 01109c9733..77d266d9b6 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -271,10 +272,15 @@ struct infill_generate_request using value_type = slots::infill::v0::generate::CallRequest; using native_value_type = Polygons; - value_type operator()(const native_value_type& inner_contour, const std::string& pattern) const + value_type operator()(const native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const { value_type message{}; message.set_pattern(pattern); + auto* msg_settings = message.mutable_settings()->mutable_settings(); + for (const auto& [key, value] : settings.getFlattendSettings()) + { + msg_settings->insert({ key, value }); + } if (inner_contour.empty()) { diff --git a/include/settings/Settings.h b/include/settings/Settings.h index e0af78044c..9ac578ffaa 100644 --- a/include/settings/Settings.h +++ b/include/settings/Settings.h @@ -1,5 +1,5 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef SETTINGS_SETTINGS_H #define SETTINGS_SETTINGS_H @@ -95,6 +95,8 @@ class Settings */ void setParent(Settings* new_parent); + std::unordered_map getFlattendSettings() const; + private: /*! * Optionally, a parent setting container to ask for the value of a setting diff --git a/src/infill.cpp b/src/infill.cpp index f09e09f7bc..9e54cfc074 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -311,7 +311,7 @@ void Infill::_generate( case EFillMethod::PLUGIN: { auto [toolpaths_, generated_result_polygons_, generated_result_lines_] - = slots::instance().generate(inner_contour, mesh->settings.get("infill_pattern")); + = slots::instance().generate(inner_contour, mesh->settings.get("infill_pattern"), mesh->settings); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); result_polygons.add(generated_result_polygons_); result_lines.add(generated_result_lines_); diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 594aa2fccb..e8b2ebfed4 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -20,6 +20,7 @@ #include "utils/string.h" //For Escaped. #include "utils/types/string_switch.h" //For string switch. +#include #include #include @@ -771,4 +772,18 @@ std::string Settings::getWithoutLimiting(const std::string& key) const } } +std::unordered_map Settings::getFlattendSettings() const +{ + if (parent) + { + auto parent_settings = parent->getFlattendSettings(); + parent_settings.insert(settings.begin(), settings.end()); + return parent_settings; + } + else + { + return settings; + } +} + } // namespace cura From b1ea45b337c2fed0241e2768d128e7dd0cb24222 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 6 Aug 2023 10:18:09 +0000 Subject: [PATCH 295/656] Applied clang-format. --- include/settings/Settings.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/settings/Settings.h b/include/settings/Settings.h index 9ac578ffaa..f810e0774a 100644 --- a/include/settings/Settings.h +++ b/include/settings/Settings.h @@ -64,7 +64,8 @@ class Settings * \param key The key of the setting to get. * \return The setting's value, cast to the desired type. */ - template A get(const std::string& key) const; + template + A get(const std::string& key) const; /*! * \brief Get a string containing all settings in this container. @@ -121,7 +122,6 @@ class Settings std::string getWithoutLimiting(const std::string& key) const; }; -} //namespace cura - -#endif //SETTINGS_SETTINGS_H +} // namespace cura +#endif // SETTINGS_SETTINGS_H From a85186f7b5db726f2d0ab0f7dbbdfb2d8935a90f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 6 Aug 2023 13:39:13 +0200 Subject: [PATCH 296/656] Fix mesh setting retrieval Separated logic in getFlattendSettings() method to a new method getKeys(), for fetching keys in a way that better respects parent-child relationships. This method returns setting keys in a vector, improving retrieval of mesh settings. Changes made in line with CURA-10619 to avoid erroneous values due to overlooking parent settings. Contributes to CURA-10619 --- include/settings/Settings.h | 2 ++ src/settings/Settings.cpp | 24 ++++++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/settings/Settings.h b/include/settings/Settings.h index f810e0774a..2327e41141 100644 --- a/include/settings/Settings.h +++ b/include/settings/Settings.h @@ -98,6 +98,8 @@ class Settings std::unordered_map getFlattendSettings() const; + std::vector getKeys() const; + private: /*! * Optionally, a parent setting container to ask for the value of a setting diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index e8b2ebfed4..6b88a3b363 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -20,7 +20,8 @@ #include "utils/string.h" //For Escaped. #include "utils/types/string_switch.h" //For string switch. -#include +#include +#include #include #include @@ -773,17 +774,24 @@ std::string Settings::getWithoutLimiting(const std::string& key) const } std::unordered_map Settings::getFlattendSettings() const +{ + auto keys = getKeys(); + return keys + | ranges::views::transform( + [&](const auto& key) + { + return std::pair(key, get(key)); + }) + | ranges::to>(); +} + +std::vector Settings::getKeys() const { if (parent) { - auto parent_settings = parent->getFlattendSettings(); - parent_settings.insert(settings.begin(), settings.end()); - return parent_settings; - } - else - { - return settings; + return parent->getKeys(); } + return ranges::views::keys(settings) | ranges::to_vector; } } // namespace cura From 38813365f4205fed6785dec66cd3d12788d07433 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 08:30:11 +0200 Subject: [PATCH 297/656] Update Conan version The multi config generation bug has been fixed since 1.58 --- .github/workflows/benchmark.yml | 3 +-- .github/workflows/lint-tidier.yml | 2 +- .github/workflows/requirements-conan-package.txt | 2 +- .github/workflows/requirements-linter.txt | 2 +- .github/workflows/unit-test.yml | 5 ++--- conanfile.py | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 7f9b8f126b..b2d9806de4 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -59,8 +59,7 @@ jobs: - name: Install Python requirements and Create default Conan profile run: | - pip install -r https://raw.githubusercontent.com/Ultimaker/Cura/main/.github/workflows/requirements-conan-package.txt - # Note the runner requirements are always installed from the main branch in the Ultimaker/Cura repo + pip install -r .github/workflows/requirements-conan-package.txt - name: Use Conan download cache (Bash) if: ${{ runner.os != 'Windows' }} diff --git a/.github/workflows/lint-tidier.yml b/.github/workflows/lint-tidier.yml index 843f0d5a19..4c13db4c35 100644 --- a/.github/workflows/lint-tidier.yml +++ b/.github/workflows/lint-tidier.yml @@ -68,7 +68,7 @@ jobs: run: | . ./activate_github_actions_runenv.sh . ./activate_github_actions_buildenv.sh - working-directory: build/generators + working-directory: build/Release/generators - name: Build CuraEngine and tests run: | diff --git a/.github/workflows/requirements-conan-package.txt b/.github/workflows/requirements-conan-package.txt index 0471cc9eac..6f81c91ad8 100644 --- a/.github/workflows/requirements-conan-package.txt +++ b/.github/workflows/requirements-conan-package.txt @@ -1 +1 @@ -conan==1.56.0 +conan>=1.60.2 diff --git a/.github/workflows/requirements-linter.txt b/.github/workflows/requirements-linter.txt index b15fb5ea14..6a54602ceb 100644 --- a/.github/workflows/requirements-linter.txt +++ b/.github/workflows/requirements-linter.txt @@ -1,2 +1,2 @@ pyyaml -conan==1.56.0 \ No newline at end of file +conan>=1.60.2 \ No newline at end of file diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 40f69c3384..0b41348882 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -74,8 +74,7 @@ jobs: - name: Install Python requirements and Create default Conan profile run: | - pip install -r https://raw.githubusercontent.com/Ultimaker/Cura/main/.github/workflows/requirements-conan-package.txt - # Note the runner requirements are always installed from the main branch in the Ultimaker/Cura repo + pip install -r .github/workflows/requirements-conan-package.txt - name: Use Conan download cache (Bash) if: ${{ runner.os != 'Windows' }} @@ -124,7 +123,7 @@ jobs: run: | . ./activate_github_actions_runenv.sh . ./activate_github_actions_buildenv.sh - working-directory: build/generators + working-directory: build/Release/generators - name: Build CuraEngine and tests run: | diff --git a/conanfile.py b/conanfile.py index 21b7a4b843..aca65ece35 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ from conan.tools.build import check_min_cppstd from conan.tools.scm import Version -required_conan_version = ">=1.56.0" +required_conan_version = ">=1.54 <=1.56.0 || >=1.58.0 <2.0.0" class CuraEngineConan(ConanFile): From 47aede8d839ddda0ebd453e86217dca9c77f83fe Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 08:44:25 +0200 Subject: [PATCH 298/656] Switch the order of conan steps setting config options after dl-ing config --- .github/workflows/benchmark.yml | 254 ++++++++++++++--------------- .github/workflows/unit-test.yml | 274 ++++++++++++++++---------------- 2 files changed, 264 insertions(+), 264 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b2d9806de4..09a68825af 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,134 +1,134 @@ name: Benchmark on: - schedule: - - cron: '15 6,12,17 * * *' - - # FIXME: remove the path trigger after merge - push: - paths: - - 'include/**' - - 'src/**' - - 'cmake/**' - - 'benchmark/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - - '.github/workflows/benchmark.yml' - branches: - - main - - CURA-9906_benchmark - tags: - - '[0-9].[0-9].[0-9]*' + schedule: + - cron: '15 6,12,17 * * *' + + # FIXME: remove the path trigger after merge + push: + paths: + - 'include/**' + - 'src/**' + - 'cmake/**' + - 'benchmark/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - '.github/workflows/benchmark.yml' + branches: + - main + - CURA-9906_benchmark + tags: + - '[0-9].[0-9].[0-9]*' env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: info - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} + CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: info + CONAN_NON_INTERACTIVE: 1 jobs: - conan-recipe-version: - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + conan-recipe-version: + uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + with: + project_name: curaengine + + benchmark: + needs: [ conan-recipe-version ] + name: Run C++ benchmark + runs-on: ubuntu-22.04 + steps: + - name: Checkout CuraEngine + uses: actions/checkout@v3 + + - name: Setup Python and pip + uses: actions/setup-python@v4 with: - project_name: curaengine - - benchmark: - needs: [ conan-recipe-version ] - name: Run C++ benchmark - runs-on: ubuntu-22.04 - steps: - - name: Checkout CuraEngine - uses: actions/checkout@v3 - - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: '3.11.x' - architecture: 'x64' - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt - - - name: Cache Benchmark library - uses: actions/cache@v1 - with: - path: ./cache - key: ${{ runner.os }}-googlebenchmark-v1.5.0 - - - name: Install Python requirements and Create default Conan profile - run: | - pip install -r .github/workflows/requirements-conan-package.txt - - - name: Use Conan download cache (Bash) - if: ${{ runner.os != 'Windows' }} - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - if: ${{ runner.os != 'Windows' }} - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ runner.os }}-${{ runner.arch }} - - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - - name: Install GCC-12 on ubuntu-22.04 - run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 - - - name: Get Conan configuration - run: | - conan profile new default --detect - conan config install https://github.com/Ultimaker/conan-config.git - - - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv - - - name: Upload the Dependency package(s) - run: conan upload "*" -r cura --all -c - - - name: Set Environment variables from Conan install (bash) - if: ${{ runner.os != 'Windows' }} - run: | - . ./activate_github_actions_runenv.sh - . ./activate_github_actions_buildenv.sh - working-directory: build/generators - - - name: Build CuraEngine and tests - run: | - cmake --preset release - cmake --build --preset release - - - name: Run benchmark CuraEngine - id: run-test - run: ./benchmarks --benchmark_format=json --benchmark_context=version=`${{ needs.conan-recipe-version.outputs.recipe_semver_full }}` | tee benchmark_result.json - working-directory: build/Release/benchmark - - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - name: C++ Benchmark - tool: 'googlecpp' - output-file-path: build/Release/benchmark/benchmark_result.json - github-token: ${{ secrets.GITHUB_TOKEN }} - auto-push: true - # Show alert with commit comment on detecting possible performance regression - alert-threshold: '200%' - comment-on-alert: true - fail-on-alert: true + python-version: '3.11.x' + architecture: 'x64' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt + + - name: Cache Benchmark library + uses: actions/cache@v1 + with: + path: ./cache + key: ${{ runner.os }}-googlebenchmark-v1.5.0 + + - name: Install Python requirements and Create default Conan profile + run: | + pip install -r .github/workflows/requirements-conan-package.txt + + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + + - name: Install GCC-12 on ubuntu-22.04 + run: | + sudo apt install g++-12 gcc-12 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + + - name: Get Conan configuration + run: | + conan profile new default --detect + conan config install https://github.com/Ultimaker/conan-config.git + + - name: Use Conan download cache (Bash) + if: ${{ runner.os != 'Windows' }} + run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + + - name: Cache Conan local repository packages (Bash) + uses: actions/cache@v3 + if: ${{ runner.os != 'Windows' }} + with: + path: | + $HOME/.conan/data + $HOME/.conan/conan_download_cache + key: conan-${{ runner.os }}-${{ runner.arch }} + + - name: Install dependencies + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv + + - name: Upload the Dependency package(s) + run: conan upload "*" -r cura --all -c + + - name: Set Environment variables from Conan install (bash) + if: ${{ runner.os != 'Windows' }} + run: | + . ./activate_github_actions_runenv.sh + . ./activate_github_actions_buildenv.sh + working-directory: build/generators + + - name: Build CuraEngine and tests + run: | + cmake --preset release + cmake --build --preset release + + - name: Run benchmark CuraEngine + id: run-test + run: ./benchmarks --benchmark_format=json --benchmark_context=version=`${{ needs.conan-recipe-version.outputs.recipe_semver_full }}` | tee benchmark_result.json + working-directory: build/Release/benchmark + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + name: C++ Benchmark + tool: 'googlecpp' + output-file-path: build/Release/benchmark/benchmark_result.json + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '200%' + comment-on-alert: true + fail-on-alert: true diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 0b41348882..f3d91bfe9d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -3,145 +3,145 @@ name: unit-test # FIXME: This should be a reusable workflow on: - push: - paths: - - 'include/**' - - 'src/**' - - 'cmake/**' - - 'tests/**' - - 'test_package/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - - '.github/workflows/unit-test.yml' - - '.github/worflows/requirements-conan-package.txt' - branches: - - main - - 'CURA-*' - - '[0-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' - pull_request: - types: [opened, reopened, synchronize] - paths: - - 'include/**' - - 'src/**' - - 'cmake/**' - - 'tests/**' - - 'test_package/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - - '.github/workflows/unit-test.yml' - - '.github/worflows/requirements-conan-package.txt' - branches: - - main - - 'CURA-*' - - '[0-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' + push: + paths: + - 'include/**' + - 'src/**' + - 'cmake/**' + - 'tests/**' + - 'test_package/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - '.github/workflows/unit-test.yml' + - '.github/worflows/requirements-conan-package.txt' + branches: + - main + - 'CURA-*' + - '[0-9]+.[0-9]+' + tags: + - '[0-9]+.[0-9]+.[0-9]+' + pull_request: + types: [ opened, reopened, synchronize ] + paths: + - 'include/**' + - 'src/**' + - 'cmake/**' + - 'tests/**' + - 'test_package/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - '.github/workflows/unit-test.yml' + - '.github/worflows/requirements-conan-package.txt' + branches: + - main + - 'CURA-*' + - '[0-9]+.[0-9]+' + tags: + - '[0-9]+.[0-9]+.[0-9]+' env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: info - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} + CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: info + CONAN_NON_INTERACTIVE: 1 jobs: - conan-recipe-version: - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + conan-recipe-version: + uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + with: + project_name: curaengine + + testing: + runs-on: ubuntu-22.04 + needs: [ conan-recipe-version ] + + steps: + - name: Checkout CuraEngine + uses: actions/checkout@v3 + + - name: Setup Python and pip + uses: actions/setup-python@v4 with: - project_name: curaengine - - testing: - runs-on: ubuntu-22.04 - needs: [ conan-recipe-version ] - - steps: - - name: Checkout CuraEngine - uses: actions/checkout@v3 - - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: '3.11.x' - architecture: 'x64' - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt - - - name: Install Python requirements and Create default Conan profile - run: | - pip install -r .github/workflows/requirements-conan-package.txt - - - name: Use Conan download cache (Bash) - if: ${{ runner.os != 'Windows' }} - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - if: ${{ runner.os != 'Windows' }} - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ runner.os }}-${{ runner.arch }} - - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - - name: Install GCC-12 on ubuntu-22.04 - run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 - - - name: Get Conan configuration - run: | - conan profile new default --detect - conan config install https://github.com/Ultimaker/conan-config.git - - - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_testing=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv - - - name: Upload the Dependency package(s) - run: conan upload "*" -r cura --all -c - - - name: Set Environment variables from Conan install (bash) - if: ${{ runner.os != 'Windows' }} - run: | - . ./activate_github_actions_runenv.sh - . ./activate_github_actions_buildenv.sh - working-directory: build/Release/generators - - - name: Build CuraEngine and tests - run: | - cmake --preset release - cmake --build --preset release - - - name: Run Unit Test CuraEngine - id: run-test - run: ctest --output-junit engine_test.xml - working-directory: build/Release - - - name: Publish Unit Test Results - id: test-results - uses: EnricoMi/publish-unit-test-result-action@v1 - if: ${{ always() }} - with: - files: | - **/*.xml - - - name: Conclusion - run: echo "Conclusion is ${{ fromJSON( steps.test-results.outputs.json ).conclusion }}" + python-version: '3.11.x' + architecture: 'x64' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt + + - name: Install Python requirements and Create default Conan profile + run: | + pip install -r .github/workflows/requirements-conan-package.txt + + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + + - name: Install GCC-12 on ubuntu-22.04 + run: | + sudo apt install g++-12 gcc-12 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + + - name: Get Conan configuration + run: | + conan profile new default --detect + conan config install https://github.com/Ultimaker/conan-config.git + + - name: Use Conan download cache (Bash) + if: ${{ runner.os != 'Windows' }} + run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + + - name: Cache Conan local repository packages (Bash) + uses: actions/cache@v3 + if: ${{ runner.os != 'Windows' }} + with: + path: | + $HOME/.conan/data + $HOME/.conan/conan_download_cache + key: conan-${{ runner.os }}-${{ runner.arch }} + + - name: Install dependencies + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_testing=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv + + - name: Upload the Dependency package(s) + run: conan upload "*" -r cura --all -c + + - name: Set Environment variables from Conan install (bash) + if: ${{ runner.os != 'Windows' }} + run: | + . ./activate_github_actions_runenv.sh + . ./activate_github_actions_buildenv.sh + working-directory: build/Release/generators + + - name: Build CuraEngine and tests + run: | + cmake --preset release + cmake --build --preset release + + - name: Run Unit Test CuraEngine + id: run-test + run: ctest --output-junit engine_test.xml + working-directory: build/Release + + - name: Publish Unit Test Results + id: test-results + uses: EnricoMi/publish-unit-test-result-action@v1 + if: ${{ always() }} + with: + files: | + **/*.xml + + - name: Conclusion + run: echo "Conclusion is ${{ fromJSON( steps.test-results.outputs.json ).conclusion }}" From 68c6281be5982a08939df70ef49b1e2dbb2dcfe1 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 09:01:47 +0200 Subject: [PATCH 299/656] switch order of profile detect --- .github/workflows/benchmark.yml | 2 +- .github/workflows/unit-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 09a68825af..c312dcb5d7 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -81,8 +81,8 @@ jobs: - name: Get Conan configuration run: | - conan profile new default --detect conan config install https://github.com/Ultimaker/conan-config.git + conan profile new default --detect - name: Use Conan download cache (Bash) if: ${{ runner.os != 'Windows' }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index f3d91bfe9d..8728e95ab0 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -96,8 +96,8 @@ jobs: - name: Get Conan configuration run: | - conan profile new default --detect conan config install https://github.com/Ultimaker/conan-config.git + conan profile new default --detect - name: Use Conan download cache (Bash) if: ${{ runner.os != 'Windows' }} From 4e6c20a09d03fff4c0d02c2dcf03c2a488430c37 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 09:12:44 +0200 Subject: [PATCH 300/656] Downgrade Python version --- .github/workflows/benchmark.yml | 2 +- .github/workflows/unit-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index c312dcb5d7..83b543bc5f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Python and pip uses: actions/setup-python@v4 with: - python-version: '3.11.x' + python-version: '3.10.x' architecture: 'x64' cache: 'pip' cache-dependency-path: .github/workflows/requirements-conan-package.txt diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 8728e95ab0..119ca405b1 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -67,7 +67,7 @@ jobs: - name: Setup Python and pip uses: actions/setup-python@v4 with: - python-version: '3.11.x' + python-version: '3.10.x' architecture: 'x64' cache: 'pip' cache-dependency-path: .github/workflows/requirements-conan-package.txt From e49b8c7bdc301622e760acbe14eb230d5336efd5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 09:19:04 +0200 Subject: [PATCH 301/656] Don't install conan 2.0 --- .github/workflows/requirements-conan-package.txt | 2 +- .github/workflows/requirements-linter.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/requirements-conan-package.txt b/.github/workflows/requirements-conan-package.txt index 6f81c91ad8..42669ee348 100644 --- a/.github/workflows/requirements-conan-package.txt +++ b/.github/workflows/requirements-conan-package.txt @@ -1 +1 @@ -conan>=1.60.2 +conan>=1.60.2,<2.0.0 diff --git a/.github/workflows/requirements-linter.txt b/.github/workflows/requirements-linter.txt index 6a54602ceb..a2fa4b7dd2 100644 --- a/.github/workflows/requirements-linter.txt +++ b/.github/workflows/requirements-linter.txt @@ -1,2 +1,2 @@ pyyaml -conan>=1.60.2 \ No newline at end of file +conan>=1.60.2,<2.0.0 \ No newline at end of file From 07612ee21573ff02acac959731e173c2c39c0f1d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 09:22:05 +0200 Subject: [PATCH 302/656] also rerun when runner req are changed --- .github/workflows/benchmark.yml | 1 + .github/workflows/unit-test.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 83b543bc5f..7a97bb5310 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -14,6 +14,7 @@ on: - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/benchmark.yml' + - '.github/workflows/requirements-conan-package.txt' branches: - main - CURA-9906_benchmark diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 119ca405b1..866e651210 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -14,7 +14,7 @@ on: - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/unit-test.yml' - - '.github/worflows/requirements-conan-package.txt' + - '.github/workflows/requirements-conan-package.txt' branches: - main - 'CURA-*' @@ -33,7 +33,7 @@ on: - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/unit-test.yml' - - '.github/worflows/requirements-conan-package.txt' + - '.github/workflows/requirements-conan-package.txt' branches: - main - 'CURA-*' From 5a3331c22901a5c548af103b011b33b1e7d86f0b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 11:48:40 +0200 Subject: [PATCH 303/656] Made prep_client_context function static in common.h This change was to ensure that the prep_client_context function isn't exposed to the linker. This is a part of code clean-up as well as to avoid any potential issues of function collision during linking. It keeps this function limited to its translation unit, thereby enhancing modularity and reducing possible side-effects. CURA-10851 relates to the task of improving code quality and maintainability. Contributes to CURA-10851 --- include/plugins/components/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h index 45b04d80ea..099c84a12f 100644 --- a/include/plugins/components/common.h +++ b/include/plugins/components/common.h @@ -28,7 +28,7 @@ using plugin_info_ptr = std::shared_ptr>; * @param client_context - Client context to prepare * @param timeout - Call timeout duration (optional, default = 500ms) */ -inline void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) +inline static void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) { // Set time-out client_context.set_deadline(std::chrono::system_clock::now() + timeout); From fd5f1772c8da023d6c459444c5ebc2676a0acaf8 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 7 Aug 2023 10:41:49 +0000 Subject: [PATCH 304/656] Applied clang-format. --- include/plugins/components/common.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h index 099c84a12f..661e1dc23d 100644 --- a/include/plugins/components/common.h +++ b/include/plugins/components/common.h @@ -28,7 +28,8 @@ using plugin_info_ptr = std::shared_ptr>; * @param client_context - Client context to prepare * @param timeout - Call timeout duration (optional, default = 500ms) */ -inline static void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) +inline static void + prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) { // Set time-out client_context.set_deadline(std::chrono::system_clock::now() + timeout); From f806338b90911b9ddb5bf9f039607bc92d98ec73 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 12:47:31 +0200 Subject: [PATCH 305/656] Fixed Benchmarks for plugin CURA-10851 --- benchmark/simplify_benchmark.h | 49 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 1dfe5d3716..6320bb7b36 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -4,31 +4,32 @@ #ifndef CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H #define CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H -#include - -#include -#include -#include - #include "../tests/ReadTestPolygons.h" - -#include "utils/channel.h" +#include "plugins/slots.h" #include "utils/Simplify.h" +#include "utils/channel.h" -#include "plugins/slots.h" +#include + +#include +#include +#include namespace cura { class SimplifyTestFixture : public benchmark::Fixture { public: - const std::vector POLYGON_FILENAMES = { - std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave_hole.txt").string(), - std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square_hole.txt").string(), - std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_triangle.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_two_squares.txt").string(), - std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_1.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_2.txt").string(), - std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_3.txt").string(), std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_4.txt").string() - }; + const std::vector POLYGON_FILENAMES = { std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_concave_hole.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_square_hole.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_triangle.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/polygon_two_squares.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_1.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_2.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_3.txt").string(), + std::filesystem::path(__FILE__).parent_path().append("tests/resources/slice_polygon_4.txt").string() }; std::vector shapes; @@ -59,14 +60,14 @@ BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_local); BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State& st) { - for (auto _ : st) - { + for (auto _ : st) + { Polygons simplified; - for (const auto& polys : shapes) - { - benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); - } - } + for (const auto& polys : shapes) + { + benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); + } + } } BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_noplugin); @@ -78,7 +79,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St try { - slots::instance().connect(utils::createChannel({host, port})); + slots::instance().connect(plugins::v0::SlotID::SIMPLIFY_MODIFY, utils::createChannel({ host, port })); } catch (std::runtime_error e) { From 3c588006d4000a7aa00966baba108c5eeb4d3640 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 13:00:47 +0200 Subject: [PATCH 306/656] Use main gRPC defs CURA-10475 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 22e5dbf6b7..db6341e9d0 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10619") + self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") def generate(self): deps = CMakeDeps(self) From 82f71d09ccd9f622f15d48e26693b106206ad1ec Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 13:26:28 +0200 Subject: [PATCH 307/656] Update benchmark.yml --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 7a97bb5310..e650eaf0fb 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -109,7 +109,7 @@ jobs: run: | . ./activate_github_actions_runenv.sh . ./activate_github_actions_buildenv.sh - working-directory: build/generators + working-directory: build/Release/generators - name: Build CuraEngine and tests run: | From 3c8ad145b74ba651bc1dc6180c8d6d50f0e74d13 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 7 Aug 2023 11:28:37 +0000 Subject: [PATCH 308/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9546d81e00..2f8f50050a 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1115,7 +1115,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan } Polygons all_brim_lines; - + all_brim_lines.reserve(total_line_count); const coord_t line_w = train.settings.get("skirt_brim_line_width") * train.settings.get("initial_layer_line_width_factor"); @@ -1214,15 +1214,15 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan gcode_layer.addLinesByOptimizer( layer_nr == 0 ? all_brim_lines : inner_brim_line, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements); + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements); } From 20932c9e70fb2118f4b78510f579a7fcb0c8f85e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 15:25:23 +0200 Subject: [PATCH 309/656] Use gRPC definitions from working branch Contributes to CURA-10446 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index b65c67a704..4aa676d960 100644 --- a/conanfile.py +++ b/conanfile.py @@ -106,7 +106,7 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") self.requires("asio-grpc/2.4.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") def generate(self): deps = CMakeDeps(self) From 0f3bca785dc0078afc183720202ee9cbb69a18b1 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 16:06:08 +0200 Subject: [PATCH 310/656] Add GCodePaths modify to Cura.proto SlotID CURA-10446 --- Cura.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/Cura.proto b/Cura.proto index 4942f9c5ce..ceb294f619 100644 --- a/Cura.proto +++ b/Cura.proto @@ -17,6 +17,7 @@ enum SlotID { SIMPLIFY_MODIFY = 100; POSTPROCESS_MODIFY = 101; INFILL_MODIFY = 102; + GCODE_PATHS_MODIFY = 103; INFILL_GENERATE = 200; } From 6de08b94ac2a3b599c8c0acead2105afb976a164 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 16:06:43 +0200 Subject: [PATCH 311/656] Add GcodePathsModify Slot Converters are empty for now CURA-10446 --- include/plugins/slots.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 49235da4a3..48f6804185 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -5,6 +5,7 @@ #define PLUGINS_SLOTS_H #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" +#include "cura/plugins/slots/gcode_paths/v0/modify.grpc.pb.h" #include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" @@ -98,6 +99,10 @@ template using slot_settings_broadcast_ = SlotProxy; +template +using slot_gcode_paths_modify_ + = SlotProxy; + template struct Typelist { @@ -211,12 +216,13 @@ struct Holder } // namespace details -using slot_simplify = details::slot_simplify_; +using slot_gcode_paths_modify = details::slot_gcode_paths_modify_<>; +using slot_infill_generate = details::slot_infill_generate_; using slot_postprocess = details::slot_postprocess_<>; using slot_settings_broadcast = details::slot_settings_broadcast_<>; -using slot_infill_generate = details::slot_infill_generate_; +using slot_simplify = details::slot_simplify_; -using SlotTypes = details::Typelist; +using SlotTypes = details::Typelist; } // namespace plugins using slots = plugins::details::SingletonRegistry; From d4376fd030243d93c8466c8b122feba93f9bc60d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 16:07:45 +0200 Subject: [PATCH 312/656] Format Layerplan with ClangFormat CURA-10446 --- src/LayerPlan.cpp | 527 ++++++++++++++++++++++++++++------------------ 1 file changed, 326 insertions(+), 201 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 5c60180631..e05ec309e0 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1,17 +1,10 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include -#include -#include - -#include -#include +#include "LayerPlan.h" #include "Application.h" //To communicate layer view data. #include "ExtruderTrain.h" -#include "LayerPlan.h" #include "PathOrderMonotonic.h" //Monotonic ordering of skin lines. #include "Slice.h" #include "WipeScriptConfig.h" @@ -25,34 +18,40 @@ #include "utils/linearAlg2D.h" #include "utils/polygonUtils.h" +#include +#include + +#include +#include +#include +#include + 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; -ExtruderPlan::ExtruderPlan -( +ExtruderPlan::ExtruderPlan( const size_t extruder, const LayerIndex layer_nr, const bool is_initial_layer, const bool is_raft_layer, const coord_t layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, - const RetractionConfig& retraction_config -) : - heated_pre_travel_time(0), - required_start_temperature(-1), - extruder_nr(extruder), - layer_nr(layer_nr), - is_initial_layer(is_initial_layer), - is_raft_layer(is_raft_layer), - layer_thickness(layer_thickness), - fan_speed_layer_time_settings(fan_speed_layer_time_settings), - retraction_config(retraction_config), - extraTime(0.0), - temperatureFactor(0.0), - slowest_path_speed(0.0) + const RetractionConfig& retraction_config) + : heated_pre_travel_time(0) + , required_start_temperature(-1) + , extruder_nr(extruder) + , layer_nr(layer_nr) + , is_initial_layer(is_initial_layer) + , is_raft_layer(is_raft_layer) + , layer_thickness(layer_thickness) + , fan_speed_layer_time_settings(fan_speed_layer_time_settings) + , retraction_config(retraction_config) + , extraTime(0.0) + , temperatureFactor(0.0) + , slowest_path_speed(0.0) { } @@ -99,19 +98,17 @@ void ExtruderPlan::applyBackPressureCompensation(const Ratio back_pressure_compe } } -GCodePath* LayerPlan::getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, bool spiralize, const Ratio speed_factor) +GCodePath* LayerPlan::getLatestPathWithConfig( + const GCodePathConfig& config, + SpaceFillType space_fill_type, + const Ratio flow, + const Ratio width_factor, + bool spiralize, + const Ratio speed_factor) { std::vector& paths = extruder_plans.back().paths; - if - ( - paths.size() > 0 && - paths.back().config == &config && - ! paths.back().done && - paths.back().flow == flow && - paths.back().width_factor == width_factor && - paths.back().speed_factor == speed_factor && - paths.back().mesh == current_mesh - ) // spiralize can only change when a travel path is in between + if (paths.size() > 0 && paths.back().config == &config && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor + && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } @@ -133,8 +130,7 @@ void LayerPlan::forceNewPathStart() paths[paths.size() - 1].done = true; } -LayerPlan::LayerPlan -( +LayerPlan::LayerPlan( const SliceDataStorage& storage, LayerIndex layer_nr, coord_t z, @@ -143,26 +139,26 @@ LayerPlan::LayerPlan const std::vector& fan_speed_layer_time_settings_per_extruder, coord_t comb_boundary_offset, coord_t comb_move_inside_distance, - coord_t travel_avoid_distance -) : - configs_storage(storage, layer_nr, layer_thickness), - z(z), - final_travel_z(z), - mode_skip_agressive_merge(false), - storage(storage), - layer_nr(layer_nr), - is_initial_layer(layer_nr == 0 - static_cast(Raft::getTotalExtraLayers())), - is_raft_layer(layer_nr < 0 - static_cast(Raft::getFillerLayerCount())), - layer_thickness(layer_thickness), - has_prime_tower_planned_per_extruder(Application::getInstance().current_slice->scene.extruders.size(), false), - current_mesh(nullptr), - last_extruder_previous_layer(start_extruder), - last_planned_extruder(&Application::getInstance().current_slice->scene.extruders[start_extruder]), - first_travel_destination_is_inside(false), // set properly when addTravel is called for the first time (otherwise not set properly) - comb_boundary_minimum(computeCombBoundary(CombBoundary::MINIMUM)), - comb_boundary_preferred(computeCombBoundary(CombBoundary::PREFERRED)), - comb_move_inside_distance(comb_move_inside_distance), - fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder) + coord_t travel_avoid_distance) + : configs_storage(storage, layer_nr, layer_thickness) + , z(z) + , final_travel_z(z) + , mode_skip_agressive_merge(false) + , storage(storage) + , layer_nr(layer_nr) + , is_initial_layer(layer_nr == 0 - static_cast(Raft::getTotalExtraLayers())) + , is_raft_layer(layer_nr < 0 - static_cast(Raft::getFillerLayerCount())) + , layer_thickness(layer_thickness) + , has_prime_tower_planned_per_extruder(Application::getInstance().current_slice->scene.extruders.size(), false) + , current_mesh(nullptr) + , last_extruder_previous_layer(start_extruder) + , last_planned_extruder(&Application::getInstance().current_slice->scene.extruders[start_extruder]) + , first_travel_destination_is_inside(false) + , // set properly when addTravel is called for the first time (otherwise not set properly) + comb_boundary_minimum(computeCombBoundary(CombBoundary::MINIMUM)) + , comb_boundary_preferred(computeCombBoundary(CombBoundary::PREFERRED)) + , comb_move_inside_distance(comb_move_inside_distance) + , fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder) { size_t current_extruder = start_extruder; was_inside = true; // not used, because the first travel move is bogus @@ -180,7 +176,14 @@ LayerPlan::LayerPlan layer_start_pos_per_extruder.emplace_back(extruder.settings.get("layer_start_x"), extruder.settings.get("layer_start_y")); } extruder_plans.reserve(Application::getInstance().current_slice->scene.extruders.size()); - extruder_plans.emplace_back(current_extruder, layer_nr, is_initial_layer, is_raft_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_wipe_config_per_extruder[current_extruder].retraction_config); + extruder_plans.emplace_back( + current_extruder, + layer_nr, + is_initial_layer, + is_raft_layer, + layer_thickness, + fan_speed_layer_time_settings_per_extruder[current_extruder], + storage.retraction_wipe_config_per_extruder[current_extruder].retraction_config); for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { // Skirt and brim. @@ -303,7 +306,14 @@ bool LayerPlan::setExtruder(const size_t extruder_nr) { // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer extruder_plans.back().extruder_nr = extruder_nr; } - extruder_plans.emplace_back(extruder_nr, layer_nr, is_initial_layer, is_raft_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder_nr], storage.retraction_wipe_config_per_extruder[extruder_nr].retraction_config); + extruder_plans.emplace_back( + extruder_nr, + layer_nr, + is_initial_layer, + is_raft_layer, + layer_thickness, + fan_speed_layer_time_settings_per_extruder[extruder_nr], + storage.retraction_wipe_config_per_extruder[extruder_nr].retraction_config); assert(extruder_plans.size() <= Application::getInstance().current_slice->scene.extruders.size() && "Never use the same extruder twice on one layer!"); last_planned_extruder = &Application::getInstance().current_slice->scene.extruders[extruder_nr]; @@ -374,7 +384,8 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) { const GCodePathConfig& travel_config = configs_storage.travel_config_per_extruder[getExtruder()]; - const RetractionConfig& retraction_config = current_mesh ? current_mesh->retraction_wipe_config.retraction_config : storage.retraction_wipe_config_per_extruder[getExtruder()].retraction_config; + const RetractionConfig& retraction_config + = current_mesh ? current_mesh->retraction_wipe_config.retraction_config : storage.retraction_wipe_config_per_extruder[getExtruder()].retraction_config; GCodePath* path = getLatestPathWithConfig(travel_config, SpaceFillType::None); @@ -422,20 +433,17 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) bool unretract_before_last_travel_move = false; // Decided when calculating the combing const bool perform_z_hops = mesh_or_extruder_settings.get("retraction_hop_enabled"); const bool perform_z_hops_only_when_collides = mesh_or_extruder_settings.get("retraction_hop_only_when_collides"); - combed = - comb->calc - ( - perform_z_hops, - perform_z_hops_only_when_collides, - *extruder, - *last_planned_position, - p, - combPaths, - was_inside, - is_inside, - max_distance_ignored, - unretract_before_last_travel_move - ); + combed = comb->calc( + perform_z_hops, + perform_z_hops_only_when_collides, + *extruder, + *last_planned_position, + p, + combPaths, + was_inside, + is_inside, + max_distance_ignored, + unretract_before_last_travel_move); if (combed) { bool retract = path->retract || (combPaths.size() > 1 && retraction_enable); @@ -503,8 +511,9 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) { if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc) { // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print. - assert (extruder != nullptr); - coord_t innermost_wall_line_width = mesh_or_extruder_settings.get((mesh_or_extruder_settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); + assert(extruder != nullptr); + coord_t innermost_wall_line_width + = mesh_or_extruder_settings.get((mesh_or_extruder_settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (layer_nr == 0) { innermost_wall_line_width *= mesh_or_extruder_settings.get("initial_layer_line_width_factor"); @@ -550,7 +559,15 @@ void LayerPlan::planPrime(const float& prime_blob_wipe_length) forceNewPathStart(); } -void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow, const Ratio width_factor, bool spiralize, Ratio speed_factor, double fan_speed) +void LayerPlan::addExtrusionMove( + Point p, + const GCodePathConfig& config, + SpaceFillType space_fill_type, + const Ratio& flow, + const Ratio width_factor, + bool spiralize, + Ratio speed_factor, + double fan_speed) { GCodePath* path = getLatestPathWithConfig(config, space_fill_type, flow, width_factor, spiralize, speed_factor); path->points.push_back(p); @@ -562,7 +579,15 @@ void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFi last_planned_position = p; } -void LayerPlan::addPolygon(ConstPolygonRef polygon, int start_idx, const bool backwards, const GCodePathConfig& config, coord_t wall_0_wipe_dist, bool spiralize, const Ratio& flow_ratio, bool always_retract) +void LayerPlan::addPolygon( + ConstPolygonRef polygon, + int start_idx, + const bool backwards, + const GCodePathConfig& config, + coord_t wall_0_wipe_dist, + bool spiralize, + const Ratio& flow_ratio, + bool always_retract) { constexpr Ratio width_ratio = 1.0_r; // Not printed with variable line width. Point p0 = polygon[start_idx]; @@ -610,15 +635,16 @@ void LayerPlan::addPolygon(ConstPolygonRef polygon, int start_idx, const bool ba } } -void LayerPlan::addPolygonsByOptimizer(const Polygons& polygons, - const GCodePathConfig& config, - const ZSeamConfig& z_seam_config, - coord_t wall_0_wipe_dist, - bool spiralize, - const Ratio flow_ratio, - bool always_retract, - bool reverse_order, - const std::optional start_near_location) +void LayerPlan::addPolygonsByOptimizer( + const Polygons& polygons, + const GCodePathConfig& config, + const ZSeamConfig& z_seam_config, + coord_t wall_0_wipe_dist, + bool spiralize, + const Ratio flow_ratio, + bool always_retract, + bool reverse_order, + const std::optional start_near_location) { if (polygons.empty()) { @@ -650,16 +676,17 @@ 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 -void LayerPlan::addWallLine(const Point& p0, - const Point& p1, - const Settings& settings, - const GCodePathConfig& non_bridge_config, - const GCodePathConfig& bridge_config, - float flow, - const Ratio width_factor, - float& non_bridge_line_volume, - Ratio speed_factor, - double distance_to_bridge_start) +void LayerPlan::addWallLine( + const Point& p0, + const Point& p1, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + float flow, + const Ratio width_factor, + float& non_bridge_line_volume, + Ratio speed_factor, + double distance_to_bridge_start) { const coord_t min_line_len = settings.get("meshfix_maximum_resolution") / 2; // Shouldn't cut up stuff (too much) below the required simplify resolution. const double acceleration_segment_len = MM2INT(1); // accelerate using segments of this length @@ -685,7 +712,9 @@ void LayerPlan::addWallLine(const Point& p0, while (distance_to_line_end > min_line_len) { // if we are accelerating after a bridge line, the segment length is less than the whole line length - Point segment_end = (speed_factor == 1 || distance_to_line_end < acceleration_segment_len) ? line_end : cur_point + (line_end - cur_point) * acceleration_segment_len / distance_to_line_end; + Point segment_end = (speed_factor == 1 || distance_to_line_end < acceleration_segment_len) + ? line_end + : cur_point + (line_end - cur_point) * acceleration_segment_len / distance_to_line_end; // flow required for the next line segment - when accelerating after a bridge segment, the flow is increased in inverse proportion to the speed_factor // so the slower the feedrate, the greater the flow - the idea is to get the extruder back to normal pressure as quickly as possible @@ -714,7 +743,14 @@ void LayerPlan::addWallLine(const Point& p0, if ((len - coast_dist) > min_line_len) { // segment is longer than coast distance so extrude using non-bridge config to start of coast - addExtrusionMove(segment_end + coast_dist * (cur_point - segment_end) / len, non_bridge_config, SpaceFillType::Polygons, segment_flow, width_factor, spiralize, speed_factor); + addExtrusionMove( + segment_end + coast_dist * (cur_point - segment_end) / len, + non_bridge_config, + SpaceFillType::Polygons, + segment_flow, + width_factor, + spiralize, + speed_factor); } // then coast to start of bridge segment constexpr Ratio flow = 0.0_r; // Coasting has no flow rate. @@ -723,13 +759,14 @@ void LayerPlan::addWallLine(const Point& p0, else { // no coasting required, just normal segment using non-bridge config - addExtrusionMove(segment_end, - non_bridge_config, - SpaceFillType::Polygons, - segment_flow, - width_factor, - spiralize, - (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); + addExtrusionMove( + segment_end, + non_bridge_config, + SpaceFillType::Polygons, + segment_flow, + width_factor, + spiralize, + (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); } distance_to_bridge_start -= len; @@ -737,13 +774,14 @@ void LayerPlan::addWallLine(const Point& p0, else { // no coasting required, just normal segment using non-bridge config - addExtrusionMove(segment_end, - non_bridge_config, - SpaceFillType::Polygons, - segment_flow, - width_factor, - spiralize, - (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); + addExtrusionMove( + segment_end, + non_bridge_config, + SpaceFillType::Polygons, + segment_flow, + width_factor, + spiralize, + (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); } non_bridge_line_volume += vSize(cur_point - segment_end) * segment_flow * width_factor * speed_factor * non_bridge_config.getSpeed(); cur_point = segment_end; @@ -759,7 +797,14 @@ void LayerPlan::addWallLine(const Point& p0, if (bridge_wall_mask.empty()) { // no bridges required - addExtrusionMove(p1, non_bridge_config, SpaceFillType::Polygons, flow, width_factor, spiralize, (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? 1.0_r : overhang_speed_factor); + addExtrusionMove( + p1, + non_bridge_config, + SpaceFillType::Polygons, + flow, + width_factor, + spiralize, + (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? 1.0_r : overhang_speed_factor); } else { @@ -851,7 +896,15 @@ void LayerPlan::addWallLine(const Point& p0, } } -void LayerPlan::addWall(ConstPolygonRef wall, int start_idx, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, float flow_ratio, bool always_retract) +void LayerPlan::addWall( + ConstPolygonRef wall, + int start_idx, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract) { // TODO: Deprecated in favor of ExtrusionJunction version below. if (wall.size() < 3) @@ -860,10 +913,18 @@ void LayerPlan::addWall(ConstPolygonRef wall, int start_idx, const Settings& set } constexpr size_t dummy_perimeter_id = 0; // <-- Here, don't care about which perimeter any more. - const coord_t nominal_line_width = non_bridge_config.getLineWidth(); // <-- The line width which it's 'supposed to' be will be used to adjust the flow ratio each time, this'll give a flow-ratio-multiplier of 1. + const coord_t nominal_line_width + = non_bridge_config + .getLineWidth(); // <-- The line width which it's 'supposed to' be will be used to adjust the flow ratio each time, this'll give a flow-ratio-multiplier of 1. ExtrusionLine ewall; - std::for_each(wall.begin(), wall.end(), [&dummy_perimeter_id, &nominal_line_width, &ewall](const Point& p) { ewall.emplace_back(p, nominal_line_width, dummy_perimeter_id); }); + std::for_each( + wall.begin(), + wall.end(), + [&dummy_perimeter_id, &nominal_line_width, &ewall](const Point& p) + { + ewall.emplace_back(p, nominal_line_width, dummy_perimeter_id); + }); ewall.emplace_back(*wall.begin(), nominal_line_width, dummy_perimeter_id); constexpr bool is_closed = true; constexpr bool is_reversed = false; @@ -871,17 +932,18 @@ void LayerPlan::addWall(ConstPolygonRef wall, int start_idx, const Settings& set addWall(ewall, start_idx, settings, non_bridge_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract, is_closed, is_reversed, is_linked_path); } -void LayerPlan::addWall(const ExtrusionLine& wall, - int start_idx, - const Settings& settings, - const GCodePathConfig& non_bridge_config, - const GCodePathConfig& bridge_config, - coord_t wall_0_wipe_dist, - float flow_ratio, - bool always_retract, - const bool is_closed, - const bool is_reversed, - const bool is_linked_path) +void LayerPlan::addWall( + const ExtrusionLine& wall, + int start_idx, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract, + const bool is_closed, + const bool is_reversed, + const bool is_linked_path) { if (wall.empty()) { @@ -899,7 +961,8 @@ void LayerPlan::addWall(const ExtrusionLine& wall, const coord_t min_bridge_line_len = settings.get("bridge_wall_min_length"); - const Ratio nominal_line_width_multiplier = 1.0 / Ratio(non_bridge_config.getLineWidth()); // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this + const Ratio nominal_line_width_multiplier + = 1.0 / Ratio(non_bridge_config.getLineWidth()); // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this // helper function to calculate the distance from the start of the current wall line to the first bridge segment @@ -1047,12 +1110,29 @@ void LayerPlan::addWall(const ExtrusionLine& wall, if (is_small_feature) { constexpr bool spiralize = false; - addExtrusionMove(destination, non_bridge_config, SpaceFillType::Polygons, flow_ratio, line_width * nominal_line_width_multiplier, spiralize, small_feature_speed_factor); + addExtrusionMove( + destination, + non_bridge_config, + SpaceFillType::Polygons, + flow_ratio, + line_width * nominal_line_width_multiplier, + spiralize, + small_feature_speed_factor); } else { const Point origin = p0.p + normal(line_vector, piece_length * piece); - addWallLine(origin, destination, settings, non_bridge_config, bridge_config, flow_ratio, line_width * nominal_line_width_multiplier, non_bridge_line_volume, speed_factor, distance_to_bridge_start); + addWallLine( + origin, + destination, + settings, + non_bridge_config, + bridge_config, + flow_ratio, + line_width * nominal_line_width_multiplier, + non_bridge_line_volume, + speed_factor, + distance_to_bridge_start); } } @@ -1117,14 +1197,15 @@ void LayerPlan::addInfillWall(const ExtrusionLine& wall, const GCodePathConfig& } } -void LayerPlan::addWalls(const Polygons& walls, - const Settings& settings, - const GCodePathConfig& non_bridge_config, - const GCodePathConfig& bridge_config, - const ZSeamConfig& z_seam_config, - coord_t wall_0_wipe_dist, - float flow_ratio, - bool always_retract) +void LayerPlan::addWalls( + const Polygons& walls, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + const ZSeamConfig& z_seam_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract) { // TODO: Deprecated in favor of ExtrusionJunction version below. PathOrderOptimizer orderOptimizer(getLastPlannedPositionOrStartingPosition(), z_seam_config); @@ -1140,16 +1221,17 @@ void LayerPlan::addWalls(const Polygons& walls, } -void LayerPlan::addLinesByOptimizer(const Polygons& polygons, - const GCodePathConfig& config, - const SpaceFillType space_fill_type, - const bool enable_travel_optimization, - const coord_t wipe_dist, - const Ratio flow_ratio, - const std::optional near_start_location, - const double fan_speed, - const bool reverse_print_direction, - const std::unordered_multimap& order_requirements) +void LayerPlan::addLinesByOptimizer( + const Polygons& polygons, + const GCodePathConfig& config, + const SpaceFillType space_fill_type, + const bool enable_travel_optimization, + const coord_t wipe_dist, + const Ratio flow_ratio, + const std::optional near_start_location, + const double fan_speed, + const bool reverse_print_direction, + const std::unordered_multimap& order_requirements) { Polygons boundary; if (enable_travel_optimization && ! comb_boundary_minimum.empty()) @@ -1174,7 +1256,13 @@ void LayerPlan::addLinesByOptimizer(const Polygons& polygons, boundary = Simplify(MM2INT(0.1), MM2INT(0.1), 0).polygon(boundary); } constexpr bool detect_loops = true; - PathOrderOptimizer order_optimizer(near_start_location.value_or(getLastPlannedPositionOrStartingPosition()), ZSeamConfig(), detect_loops, &boundary, reverse_print_direction, order_requirements); + PathOrderOptimizer order_optimizer( + near_start_location.value_or(getLastPlannedPositionOrStartingPosition()), + ZSeamConfig(), + detect_loops, + &boundary, + reverse_print_direction, + order_requirements); for (size_t line_idx = 0; line_idx < polygons.size(); line_idx++) { order_optimizer.addPolyline(polygons[line_idx]); @@ -1185,7 +1273,13 @@ void LayerPlan::addLinesByOptimizer(const Polygons& polygons, } -void LayerPlan::addLinesInGivenOrder(const std::vector>& paths, const GCodePathConfig& config, const SpaceFillType space_fill_type, const coord_t wipe_dist, const Ratio flow_ratio, const double fan_speed) +void LayerPlan::addLinesInGivenOrder( + const std::vector>& paths, + const GCodePathConfig& config, + const SpaceFillType space_fill_type, + const coord_t wipe_dist, + const Ratio flow_ratio, + const double fan_speed) { coord_t half_line_width = config.getLineWidth() / 2; coord_t line_width_2 = half_line_width * half_line_width; @@ -1282,16 +1376,17 @@ void LayerPlan::addLinesInGivenOrder(const std::vector order(monotonic_direction, max_adjacent_distance, last_position); @@ -1335,7 +1433,14 @@ void LayerPlan::addLinesMonotonic(const Polygons& area, addLinesByOptimizer(left_over, config, space_fill_type, true, wipe_dist, flow_ratio, getLastPlannedPositionOrStartingPosition(), fan_speed); } -void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRef wall, ConstPolygonRef last_wall, const int seam_vertex_idx, const int last_seam_vertex_idx, const bool is_top_layer, const bool is_bottom_layer) +void LayerPlan::spiralizeWallSlice( + const GCodePathConfig& config, + ConstPolygonRef wall, + ConstPolygonRef last_wall, + const int seam_vertex_idx, + const int last_seam_vertex_idx, + const bool is_top_layer, + const bool is_bottom_layer) { const bool smooth_contours = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("smooth_spiralized_contours"); constexpr bool spiralize = true; // In addExtrusionMove calls, enable spiralize and use nominal line width. @@ -1503,10 +1608,10 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ double factor = 0.0; double target_speed = 0.0; - std::function slow_down_func - { - [&target_speed](const GCodePath& path) { return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); } - }; + std::function slow_down_func{ [&target_speed](const GCodePath& path) + { + return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); + } }; if (minExtrudeTime >= total_extrude_time_at_minimum_speed) { @@ -1525,8 +1630,9 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ { // Slowing down to the slowest path speed is not sufficient, need to slow down further to the minimum speed. // Linear interpolate between total_extrude_time_at_slowest_speed and total_extrude_time_at_minimum_speed - const double factor = (1/total_extrude_time_at_minimum_speed - 1/minExtrudeTime) / (1/total_extrude_time_at_minimum_speed - 1/total_extrude_time_at_slowest_speed); - target_speed = minimalSpeed * (1.0-factor) + slowest_path_speed * factor; + const double factor + = (1 / total_extrude_time_at_minimum_speed - 1 / minExtrudeTime) / (1 / total_extrude_time_at_minimum_speed - 1 / total_extrude_time_at_slowest_speed); + target_speed = minimalSpeed * (1.0 - factor) + slowest_path_speed * factor; temperatureFactor = 1.0 - factor; // Update stored naive time estimates @@ -1536,13 +1642,12 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ { // Slowing down to the slowest_speed is sufficient to respect the minimum layer time. // Linear interpolate between extrudeTime and total_extrude_time_at_slowest_speed - factor = (1/total_extrude_time_at_slowest_speed - 1/minExtrudeTime) / (1/total_extrude_time_at_slowest_speed - 1/extrudeTime); - slow_down_func = - [&slowest_path_speed = slowest_path_speed, &factor](const GCodePath& path) - { - const double target_speed = slowest_path_speed * (1.0 - factor) + (path.config->getSpeed() * path.speed_factor) * factor; - return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); - }; + factor = (1 / total_extrude_time_at_slowest_speed - 1 / minExtrudeTime) / (1 / total_extrude_time_at_slowest_speed - 1 / extrudeTime); + slow_down_func = [&slowest_path_speed = slowest_path_speed, &factor](const GCodePath& path) + { + const double target_speed = slowest_path_speed * (1.0 - factor) + (path.config->getSpeed() * path.speed_factor) * factor; + return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); + }; // Update stored naive time estimates estimates.extrude_time = minExtrudeTime; @@ -1577,17 +1682,14 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point starting_pos Point p0 = starting_position; const double min_path_speed = fan_speed_layer_time_settings.cool_min_speed; - slowest_path_speed = - std::accumulate - ( - paths.begin(), - paths.end(), - std::numeric_limits::max(), - [](double value, const GCodePath& path) - { - return path.isTravelPath() ? value : std::min(value, path.config->getSpeed().value * path.speed_factor); - } - ); + slowest_path_speed = std::accumulate( + paths.begin(), + paths.end(), + std::numeric_limits::max(), + [](double value, const GCodePath& path) + { + return path.isTravelPath() ? value : std::min(value, path.config->getSpeed().value * path.speed_factor); + }); bool was_retracted = false; // wrong assumption; won't matter that much. (TODO) for (GCodePath& path : paths) @@ -1706,19 +1808,27 @@ void ExtruderPlan::processFanSpeedForFirstLayers() */ fan_speed = fan_speed_layer_time_settings.cool_fan_speed_min; - if (layer_nr < fan_speed_layer_time_settings.cool_fan_full_layer && fan_speed_layer_time_settings.cool_fan_full_layer > 0 // don't apply initial layer fan speed speedup if disabled. + if (layer_nr < fan_speed_layer_time_settings.cool_fan_full_layer + && fan_speed_layer_time_settings.cool_fan_full_layer > 0 // don't apply initial layer fan speed speedup if disabled. && ! is_raft_layer // don't apply initial layer fan speed speedup to raft, but to model layers ) { // Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0. - fan_speed = fan_speed_layer_time_settings.cool_fan_speed_0 + (fan_speed - fan_speed_layer_time_settings.cool_fan_speed_0) * std::max(LayerIndex(0), layer_nr) / fan_speed_layer_time_settings.cool_fan_full_layer; + fan_speed = fan_speed_layer_time_settings.cool_fan_speed_0 + + (fan_speed - fan_speed_layer_time_settings.cool_fan_speed_0) * std::max(LayerIndex(0), layer_nr) / fan_speed_layer_time_settings.cool_fan_full_layer; } } void LayerPlan::processFanSpeedAndMinimalLayerTime(Point starting_position) { // the minimum layer time behaviour is only applied to the last extruder. - const size_t last_extruder_nr = ranges::max_element(extruder_plans, [](const ExtruderPlan& a, const ExtruderPlan& b) { return a.extruder_nr < b.extruder_nr; })->extruder_nr; + const size_t last_extruder_nr = ranges::max_element( + extruder_plans, + [](const ExtruderPlan& a, const ExtruderPlan& b) + { + return a.extruder_nr < b.extruder_nr; + }) + ->extruder_nr; Point starting_position_last_extruder; unsigned int last_extruder_idx; double other_extr_plan_time = 0.0; @@ -1770,10 +1880,13 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // flow-rate compensation const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - gcode.setFlowRateExtrusionSettings(mesh_group_settings.get("flow_rate_max_extrusion_offset"), mesh_group_settings.get("flow_rate_extrusion_offset_factor")); // Offset is in mm. + gcode.setFlowRateExtrusionSettings( + mesh_group_settings.get("flow_rate_max_extrusion_offset"), + mesh_group_settings.get("flow_rate_extrusion_offset_factor")); // Offset is in mm. static LayerIndex layer_1{ 1 - static_cast(Raft::getTotalExtraLayers()) }; - if (layer_nr == layer_1 && mesh_group_settings.get("machine_heated_bed") && mesh_group_settings.get("material_bed_temperature") != mesh_group_settings.get("material_bed_temperature_layer_0")) + if (layer_nr == layer_1 && mesh_group_settings.get("machine_heated_bed") + && mesh_group_settings.get("material_bed_temperature") != mesh_group_settings.get("material_bed_temperature_layer_0")) { constexpr bool wait = false; gcode.writeBedTemperatureCommand(mesh_group_settings.get("material_bed_temperature"), wait); @@ -1793,7 +1906,11 @@ void LayerPlan::writeGCode(GCodeExport& gcode) for (size_t extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; - const RetractionAndWipeConfig* retraction_config = current_mesh ? ¤t_mesh->retraction_wipe_config: &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; + + // TODO: Insert modify slot for the gcodepaths CURA-10446 + + const RetractionAndWipeConfig* retraction_config + = current_mesh ? ¤t_mesh->retraction_wipe_config : &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; coord_t z_hop_height = retraction_config->retraction_config.zHop; if (extruder_nr != extruder_plan.extruder_nr) @@ -1866,8 +1983,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) bool update_extrusion_offset = true; double cumulative_path_time = 0.; // Time in seconds. - const std::function insertTempOnTime = - [&](const double to_add, const int64_t path_idx) + const std::function insertTempOnTime = [&](const double to_add, const int64_t path_idx) { cumulative_path_time += to_add; extruder_plan.handleInserts(path_idx, gcode, cumulative_path_time); @@ -1990,7 +2106,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // for some movements such as prime tower purge, the speed may get changed by this factor speed *= path.speed_factor; - //This seems to be the best location to place this, but still not ideal. + // This seems to be the best location to place this, but still not ideal. if (path.mesh != current_mesh) { current_mesh = path.mesh; @@ -2102,7 +2218,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) if (extruder.settings.get("cool_lift_head") && extruder_plan.extraTime > 0.0) { gcode.writeComment("Small layer, adding delay"); - const RetractionAndWipeConfig& retraction_config = current_mesh ? current_mesh->retraction_wipe_config: storage.retraction_wipe_config_per_extruder[gcode.getExtruderNr()]; + const RetractionAndWipeConfig& retraction_config + = current_mesh ? current_mesh->retraction_wipe_config : storage.retraction_wipe_config_per_extruder[gcode.getExtruderNr()]; gcode.writeRetraction(retraction_config.retraction_config); if (extruder_plan_idx == extruder_plans.size() - 1 || ! extruder.settings.get("machine_extruder_end_pos_abs")) { // only do the z-hop if it's the last extruder plan; otherwise it's already at the switching bay area @@ -2154,7 +2271,12 @@ bool LayerPlan::makeRetractSwitchRetract(unsigned int extruder_plan_idx, unsigne } } -bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_plan_idx, const size_t path_idx, const coord_t layer_thickness, const std::function insertTempOnTime) +bool LayerPlan::writePathWithCoasting( + GCodeExport& gcode, + const size_t extruder_plan_idx, + const size_t path_idx, + const coord_t layer_thickness, + const std::function insertTempOnTime) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; const ExtruderTrain& extruder = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr]; @@ -2174,9 +2296,11 @@ bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_ const double extrude_speed = path.config->getSpeed() * 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 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 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 // /\ 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...) @@ -2281,7 +2405,8 @@ void LayerPlan::applyBackPressureCompensation() { for (auto& extruder_plan : extruder_plans) { - const Ratio back_pressure_compensation = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr].settings.get("speed_equalize_flow_width_factor"); + const Ratio back_pressure_compensation + = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr].settings.get("speed_equalize_flow_width_factor"); if (back_pressure_compensation != 0.0) { extruder_plan.applyBackPressureCompensation(back_pressure_compensation); From cf45455cc0fae3223fb932978fb19099bdf984fe Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 7 Aug 2023 18:06:07 +0200 Subject: [PATCH 313/656] Added forward declaration of RemoteException in broadcast.h and included "plugins/slots.h" in LayerPlan.cpp. This change was necessary due to obfuscation of some classes with forward declarations. Including the "plugins/slots.h" in "LayerPlan.cpp" ensures the program has knowledge of the corresponding definitions and declarations. This adjustment ensures the correct functioning and stability of the system in handling remote exceptions. Contributes to CURA-10446 --- include/plugins/components/broadcast.h | 4 ++++ src/LayerPlan.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index 57720a61cc..fc9e4f6c11 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -26,6 +26,10 @@ namespace cura::plugins { +namespace exceptions +{ +class RemoteException; // forward declaration probably needed due to us obfuscating some other classes with forward declarations +} // namespace exceptions template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. class PluginProxyBroadcastComponent diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index e05ec309e0..5f87dc2e17 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -11,6 +11,7 @@ #include "communication/Communication.h" #include "pathPlanning/Comb.h" #include "pathPlanning/CombPaths.h" +#include "plugins/slots.h" #include "raft.h" // getTotalExtraLayers #include "settings/types/Ratio.h" #include "sliceDataStorage.h" From 45832a17b514710d3030bc1f4ce2387230f8338f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 07:28:28 +0200 Subject: [PATCH 314/656] Full include paths CURA-10446 --- include/plugins/components/broadcast.h | 2 +- include/plugins/components/invoke.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index fc9e4f6c11..37a91eb9d7 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -4,9 +4,9 @@ #ifndef PLUGINS_BROADCASTCOMPONENT_H #define PLUGINS_BROADCASTCOMPONENT_H -#include "common.h" #include "cura/plugins/v0/slot_id.pb.h" #include "plugins/broadcasts.h" +#include "plugins/components/common.h" #include "plugins/exception.h" #include "plugins/metadata.h" #include "utils/format/thread_id.h" diff --git a/include/plugins/components/invoke.h b/include/plugins/components/invoke.h index 616f33c087..7fffb6daec 100644 --- a/include/plugins/components/invoke.h +++ b/include/plugins/components/invoke.h @@ -4,9 +4,9 @@ #ifndef PLUGINS_INVOKECOMPONENT_H #define PLUGINS_INVOKECOMPONENT_H -#include "common.h" #include "cura/plugins/v0/slot_id.pb.h" #include "plugins/broadcasts.h" +#include "plugins/components/common.h" #include "plugins/exception.h" #include "plugins/metadata.h" #include "utils/format/thread_id.h" From f88649a00ff217a73cf9c30570ca5065c5732e3b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 07:42:28 +0200 Subject: [PATCH 315/656] Include what you use CURA-10446 --- include/plugins/converters.h | 6 +++++- include/plugins/slots.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 77d266d9b6..5dd2b526ac 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -5,8 +5,11 @@ #define PLUGINS_CONVERTERS_H #include "Cura.pb.h" +#include "WallToolPaths.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" +#include "cura/plugins/slots/gcode_paths/v0/modify.grpc.pb.h" +#include "cura/plugins/slots/gcode_paths/v0/modify.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.pb.h" #include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" @@ -17,6 +20,7 @@ #include "cura/plugins/slots/simplify/v0/modify.pb.h" #include "plugins/metadata.h" #include "plugins/types.h" +#include "utils/polygon.h" #include #include @@ -376,7 +380,7 @@ struct infill_generate_response result_lines.emplace_back(poly_line); } - return std::make_tuple(toolpaths_, result_polygons, result_lines); + return { toolpaths_, result_polygons, result_lines }; } }; diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 48f6804185..33731d03ea 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -4,19 +4,20 @@ #ifndef PLUGINS_SLOTS_H #define PLUGINS_SLOTS_H +#include "WallToolPaths.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/gcode_paths/v0/modify.grpc.pb.h" #include "cura/plugins/slots/infill/v0/generate.grpc.pb.h" #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" -#include "infill.h" #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" #include "plugins/validator.h" #include "utils/IntPoint.h" #include "utils/Simplify.h" // TODO: Remove once the simplify slot has been removed +#include "utils/polygon.h" #include "utils/types/char_range_literal.h" #include From 76e20fb1f6849c56deadbb8c2428df30f757fe71 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 07:42:51 +0200 Subject: [PATCH 316/656] Use specific converters CURA-10446 --- include/plugins/converters.h | 23 +++++++++++++++++++++++ include/plugins/slots.h | 10 ++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 5dd2b526ac..b806385991 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -384,6 +384,29 @@ struct infill_generate_response } }; +struct gcode_paths_modify_request +{ + using value_type = slots::gcode_paths::v0::modify::CallRequest; + using native_value_type = std::vector; + + value_type operator()(const native_value_type& paths, const std::integral auto extruder_nr, const std::integral auto layer_nr) const + { + value_type message{}; + return message; + } +}; + +struct gcode_paths_modify_response +{ + using value_type = slots::gcode_paths::v0::modify::CallResponse; + using native_value_type = std::vector; + + native_value_type operator()(const value_type& message) const + { + return {}; + } +}; + } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 33731d03ea..bcd9040f71 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -101,8 +101,14 @@ using slot_settings_broadcast_ = SlotProxy; template -using slot_gcode_paths_modify_ - = SlotProxy; +using slot_gcode_paths_modify_ = SlotProxy< + v0::SlotID::GCODE_PATHS_MODIFY, + "<=1.0.0", + slots::gcode_paths::v0::modify::GCodePathsModifyService::Stub, + Validator, + gcode_paths_modify_request, + gcode_paths_modify_response, + Default>; template struct Typelist From 28791f64342d6398d32f785f3a5b09a0e09bc229 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 09:06:54 +0200 Subject: [PATCH 317/656] Refactor TimeMaterialEstimates I do think that we need to have a propper look int the future, how this struct is "misused" when calculating the minimum layer time CURA-10446 --- CMakeLists.txt | 2 +- include/pathPlanning/TimeMaterialEstimates.h | 192 ++++++++----------- src/LayerPlan.cpp | 2 +- src/LayerPlanBuffer.cpp | 6 +- src/pathPlanning/TimeMaterialEstimates.cpp | 86 --------- 5 files changed, 81 insertions(+), 207 deletions(-) delete mode 100644 src/pathPlanning/TimeMaterialEstimates.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c35efd46a..286820b6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,7 @@ set(engine_SRCS # Except main.cpp. src/pathPlanning/GCodePath.cpp src/pathPlanning/LinePolygonsCrossings.cpp src/pathPlanning/NozzleTempInsert.cpp - src/pathPlanning/TimeMaterialEstimates.cpp + src/progress/Progress.cpp src/progress/ProgressStageEstimator.cpp diff --git a/include/pathPlanning/TimeMaterialEstimates.h b/include/pathPlanning/TimeMaterialEstimates.h index 4a00526553..68a33e3cd8 100644 --- a/include/pathPlanning/TimeMaterialEstimates.h +++ b/include/pathPlanning/TimeMaterialEstimates.h @@ -1,126 +1,86 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H #define PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H -namespace cura +namespace cura { -class ExtruderPlan; // forward declaration so that TimeMaterialEstimates can be a friend - -/*! - * Time and material estimates for a portion of paths, e.g. layer, extruder plan, path. - */ -class TimeMaterialEstimates +struct TimeMaterialEstimates { - friend class ExtruderPlan; // cause there the naive estimates are calculated -private: - double extrude_time; //!< Time in seconds occupied by extrusion - double extrude_time_at_slowest_path_speed; //!< Time in seconds occupied by extrusion assuming paths are printed at slowest path speed, usually the outer wall speed - double extrude_time_at_minimum_speed; //!< Time in seconds occupied by extrusion assuming paths are printed at the user specified Minimum Speed - double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion) - double retracted_travel_time; //!< Time in seconds occupied by retracted travel (non-extrusion) - double material; //!< Material used (in mm^3) -public: - /*! - * Basic contructor - * - * \param extrude_time Time in seconds occupied by extrusion - * \param unretracted_travel_time Time in seconds occupied by non-retracted travel (non-extrusion) - * \param retracted_travel_time Time in seconds occupied by retracted travel (non-extrusion) - * \param material Material used (in mm^3) - */ - TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material); - - /*! - * Basic constructor initializing all estimates to zero. - */ - TimeMaterialEstimates(); - - /*! - * Set all estimates to zero. - */ - void reset(); - - /*! - * Pointwise addition of estimate stats - * - * \param other The estimates to add to these estimates. - * \return The resulting estimates - */ - TimeMaterialEstimates operator+(const TimeMaterialEstimates& other); - - /*! - * In place pointwise addition of estimate stats - * - * \param other The estimates to add to these estimates. - * \return These estimates - */ - TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other); - - /*! - * \brief Subtracts the specified estimates from these estimates and returns - * the result. - * - * Each of the estimates in this class are individually subtracted. - * - * \param other The estimates to subtract from these estimates. - * \return These estimates with the specified estimates subtracted. - */ - TimeMaterialEstimates operator-(const TimeMaterialEstimates& other); - - /*! - * \brief Subtracts the specified elements from these estimates. - * - * This causes the estimates in this instance to change. Each of the - * estimates in this class are individually subtracted. - * - * \param other The estimates to subtract from these estimates. - * \return A reference to this instance. - */ - TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other); - - /*! - * Get total time estimate. The different time estimate member values added together. - * - * \return the total of all different time estimate values - */ - double getTotalTime() const; - - /*! - * Get the total time during which the head is not retracted. - * - * This includes extrusion time and non-retracted travel time - * - * \return the total time during which the head is not retracted. - */ - double getTotalUnretractedTime() const; - - /*! - * Get the total travel time. - * - * This includes the retracted travel time as well as the unretracted travel time. - * - * \return the total travel time. - */ - double getTravelTime() const; - - /*! - * Get the extrusion time. - * - * \return extrusion time. - */ - double getExtrudeTime() const; - - /*! - * Get the amount of material used in mm^3. - * - * \return amount of material - */ - double getMaterial() const; + double extrude_time{ 0.0 }; //!< Time in seconds occupied by extrusion + double extrude_time_at_slowest_path_speed{ 0.0 }; //!< Time in seconds occupied by extrusion assuming paths are printed at slowest path speed, usually the outer wall speed + double extrude_time_at_minimum_speed{ 0.0 }; //!< Time in seconds occupied by extrusion assuming paths are printed at the user specified Minimum Speed + double unretracted_travel_time{ 0.0 }; //!< Time in seconds occupied by non-retracted travel (non-extrusion) + double retracted_travel_time{ 0.0 }; //!< Time in seconds occupied by retracted travel (non-extrusion) + double material{ 0.0 }; //!< Material used (in mm^3) + + constexpr TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other) noexcept + { + extrude_time += other.extrude_time; + extrude_time_at_slowest_path_speed += other.extrude_time_at_slowest_path_speed; + extrude_time_at_minimum_speed += other.extrude_time_at_minimum_speed; + unretracted_travel_time += other.unretracted_travel_time; + retracted_travel_time += other.retracted_travel_time; + material += other.material; + return *this; + } + + constexpr TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other) noexcept + { + extrude_time -= other.extrude_time; + extrude_time_at_slowest_path_speed -= other.extrude_time_at_slowest_path_speed; + extrude_time_at_minimum_speed -= other.extrude_time_at_minimum_speed; + unretracted_travel_time -= other.unretracted_travel_time; + retracted_travel_time -= other.retracted_travel_time; + material -= other.material; + return *this; + } + + constexpr TimeMaterialEstimates operator+(const TimeMaterialEstimates& other) const noexcept + { + return TimeMaterialEstimates{ extrude_time + other.extrude_time, + extrude_time_at_slowest_path_speed + other.extrude_time_at_slowest_path_speed, + extrude_time_at_minimum_speed + other.extrude_time_at_minimum_speed, + unretracted_travel_time + other.unretracted_travel_time, + retracted_travel_time + other.retracted_travel_time, + material + other.material }; + } + + constexpr TimeMaterialEstimates operator-(const TimeMaterialEstimates& other) const noexcept + { + return TimeMaterialEstimates{ extrude_time - other.extrude_time, + extrude_time_at_slowest_path_speed - other.extrude_time_at_slowest_path_speed, + extrude_time_at_minimum_speed - other.extrude_time_at_minimum_speed, + unretracted_travel_time - other.unretracted_travel_time, + retracted_travel_time - other.retracted_travel_time, + material - other.material }; + } + + constexpr bool operator<=>(const TimeMaterialEstimates& other) const noexcept = default; + + constexpr void reset() noexcept + { + *this = TimeMaterialEstimates{}; + } + + [[nodiscard]] constexpr auto getTotalTime() const noexcept + { + return extrude_time + unretracted_travel_time + retracted_travel_time; + } + + [[nodiscard]] constexpr auto getTotalUnretractedTime() const noexcept + { + return extrude_time + unretracted_travel_time; + } + + [[nodiscard]] constexpr auto getTravelTime() const noexcept + { + return retracted_travel_time + unretracted_travel_time; + } }; -}//namespace cura +} // namespace cura -#endif//PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H +#endif // PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 5f87dc2e17..1bccfcc077 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1590,7 +1590,7 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ { const double minimalSpeed = fan_speed_layer_time_settings.cool_min_speed; const double travelTime = estimates.getTravelTime(); - const double extrudeTime = estimates.getExtrudeTime(); + const double extrudeTime = estimates.extrude_time; const double totalTime = travelTime + extrudeTime + time_other_extr_plans; constexpr double epsilon = 0.01; diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index 1ef34d1e1b..a985c8306b 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -427,7 +427,7 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex return; } - assert((time_window >= 0 || last_extruder_plan.estimates.getMaterial() == 0) && "Time window should always be positive if we actually extrude"); + assert((time_window >= 0 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); // ,layer change . // : ,precool command ,layer change . @@ -520,11 +520,11 @@ void LayerPlanBuffer::insertTempCommands() Ratio avg_flow; if (time > 0.0) { - avg_flow = extruder_plan.estimates.getMaterial() / time; + avg_flow = extruder_plan.estimates.material / time; } else { - assert(extruder_plan.estimates.getMaterial() == 0.0 && "No extrusion time should mean no material usage!"); + assert(extruder_plan.estimates.material == 0.0 && "No extrusion time should mean no material usage!"); avg_flow = 0.0; } diff --git a/src/pathPlanning/TimeMaterialEstimates.cpp b/src/pathPlanning/TimeMaterialEstimates.cpp deleted file mode 100644 index 1ecbd04d87..0000000000 --- a/src/pathPlanning/TimeMaterialEstimates.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. - -#include "pathPlanning/TimeMaterialEstimates.h" - -namespace cura -{ - -TimeMaterialEstimates::TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material) -: extrude_time(extrude_time) -, unretracted_travel_time(unretracted_travel_time) -, retracted_travel_time(retracted_travel_time) -, material(material) -{ -} - -TimeMaterialEstimates::TimeMaterialEstimates() -: extrude_time(0.0) -, unretracted_travel_time(0.0) -, retracted_travel_time(0.0) -, material(0.0) -{ -} - -TimeMaterialEstimates TimeMaterialEstimates::operator-(const TimeMaterialEstimates& other) -{ - return TimeMaterialEstimates(extrude_time - other.extrude_time,unretracted_travel_time - other.unretracted_travel_time,retracted_travel_time - other.retracted_travel_time,material - other.material); -} - -TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstimates& other) -{ - extrude_time -= other.extrude_time; - unretracted_travel_time -= other.unretracted_travel_time; - retracted_travel_time -= other.retracted_travel_time; - material -= other.material; - return *this; -} - -TimeMaterialEstimates TimeMaterialEstimates::operator+(const TimeMaterialEstimates& other) -{ - return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material); -} - -TimeMaterialEstimates& TimeMaterialEstimates::operator+=(const TimeMaterialEstimates& other) -{ - extrude_time += other.extrude_time; - unretracted_travel_time += other.unretracted_travel_time; - retracted_travel_time += other.retracted_travel_time; - material += other.material; - return *this; -} - -double TimeMaterialEstimates::getExtrudeTime() const -{ - return extrude_time; -} - -double TimeMaterialEstimates::getMaterial() const -{ - return material; -} - -double TimeMaterialEstimates::getTotalTime() const -{ - return extrude_time + unretracted_travel_time + retracted_travel_time; -} - -double TimeMaterialEstimates::getTotalUnretractedTime() const -{ - return extrude_time + unretracted_travel_time; -} - -double TimeMaterialEstimates::getTravelTime() const -{ - return retracted_travel_time + unretracted_travel_time; -} - -void TimeMaterialEstimates::reset() -{ - extrude_time = 0.0; - unretracted_travel_time = 0.0; - retracted_travel_time = 0.0; - material = 0.0; -} - -}//namespace cura From 7352914dde1aa97967432a1669bae06bbf1c9fa3 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 10:57:44 +0200 Subject: [PATCH 318/656] Move conversion logic to dedicated .cpp file Moved the conversion logic of broadcast_settings_request, handshake_request, simplify_request, postprocess_request, infill_generate_request and gcode_paths_modify_request to a dedicated cpp file. This refactoring was necessary to resolve multiple 'exceptions not declared' messages, likely due to includes and auto return types declared in multiple translation units. This change will improve the architecture and maintainability of the code. Contributes to CURA-10466 --- include/plugins/converters.h | 270 ++++---------------------------- src/plugins/converters.cpp | 294 +++++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+), 243 deletions(-) create mode 100644 src/plugins/converters.cpp diff --git a/include/plugins/converters.h b/include/plugins/converters.h index b806385991..dac97eb8b7 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -5,7 +5,6 @@ #define PLUGINS_CONVERTERS_H #include "Cura.pb.h" -#include "WallToolPaths.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/gcode_paths/v0/modify.grpc.pb.h" @@ -20,7 +19,6 @@ #include "cura/plugins/slots/simplify/v0/modify.pb.h" #include "plugins/metadata.h" #include "plugins/types.h" -#include "utils/polygon.h" #include #include @@ -30,6 +28,16 @@ #include #include + +namespace cura +{ +class GCodePath; +class LayerIndex; +class ExtrusionLine; +class Polygons; +class Settings; +} // namespace cura + namespace cura::plugins { @@ -39,15 +47,9 @@ struct empty using value_type = google::protobuf::Empty; ///< The protobuf message type. using native_value_type = std::nullptr_t; ///< The native value type. - value_type operator()() const - { - return {}; - } + value_type operator()() const; - constexpr native_value_type operator()(const value_type&) const - { - return nullptr; - } + constexpr native_value_type operator()(const value_type&) const; }; struct broadcast_settings_request @@ -62,42 +64,7 @@ struct broadcast_settings_request * @param value The value of the setting to be broadcasted. * @return The converted `proto::BroadcastServiceSettingsRequest` message. */ - value_type operator()(const native_value_type& slice_message) const - { - value_type message{}; - auto* global_settings = message.mutable_global_settings()->mutable_settings(); - for (const auto& setting : slice_message.global_settings().settings()) - { - global_settings->emplace(setting.name(), setting.value()); - } - - auto* extruders_settings = message.mutable_extruder_settings(); - for (const auto& extruder : slice_message.extruders()) - { - auto* settings = extruders_settings->Add()->mutable_settings(); - for (const auto& setting : extruder.settings().settings()) - { - settings->emplace(setting.name(), setting.value()); - } - } - - auto* object_settings = message.mutable_object_settings(); - for (const auto& object : slice_message.object_lists()) - { - auto* settings = object_settings->Add()->mutable_settings(); - for (const auto& setting : object.settings()) - { - settings->emplace(setting.name(), setting.value()); - } - } - - auto* limit_to_extruder = message.mutable_limit_to_extruder(); - for (const auto& setting_extruder : slice_message.limit_to_extruder()) - { - limit_to_extruder->emplace(setting_extruder.name(), setting_extruder.extruder()); - } - return message; - } + value_type operator()(const native_value_type& slice_message) const; }; @@ -114,13 +81,7 @@ struct handshake_request * @return The converted `proto::HandshakeRequest` message. */ - value_type operator()(const native_value_type& slot_info) const - { - value_type message{}; - message.set_slot_id(slot_info.slot_id); - message.set_version_range(slot_info.version_range.data()); - return message; - } + value_type operator()(const native_value_type& slot_info) const; }; struct handshake_response @@ -134,14 +95,7 @@ struct handshake_response * @param message The `proto::HandshakeResponse` message. * @return The native data. */ - native_value_type operator()(const value_type& message, std::string_view peer) const - { - return { .slot_version = message.slot_version(), - .plugin_name = message.plugin_name(), - .plugin_version = message.plugin_version(), - .peer = std::string{ peer }, - .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; - } + native_value_type operator()(const value_type& message, std::string_view peer) const; }; @@ -159,42 +113,7 @@ struct simplify_request * @param max_area_deviation The maximum area deviation for the simplified polygons. * @return The converted `proto::SimplifyRequest` message. */ - value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const - { - value_type message{}; - if (polygons.empty()) - { - return message; - } - - auto* msg_polygons = message.mutable_polygons(); - auto* msg_polygon = msg_polygons->add_polygons(); - auto* msg_outline = msg_polygon->mutable_outline(); - - for (const auto& point : ranges::front(polygons.paths)) - { - auto* msg_outline_path = msg_outline->add_path(); - msg_outline_path->set_x(point.X); - msg_outline_path->set_y(point.Y); - } - - auto* msg_holes = msg_polygon->mutable_holes(); - for (const auto& polygon : polygons.paths | ranges::views::drop(1)) - { - auto* msg_hole = msg_holes->Add(); - for (const auto& point : polygon) - { - auto* msg_path = msg_hole->add_path(); - msg_path->set_x(point.X); - msg_path->set_y(point.Y); - } - } - - message.set_max_resolution(max_resolution); - message.set_max_deviation(max_resolution); - message.set_max_area_deviation(max_resolution); - return message; - } + value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const; }; /** @@ -214,30 +133,7 @@ struct simplify_response * @param message The `proto::SimplifyResponse` message. * @return The converted native value. */ - native_value_type operator()(const value_type& message) const - { - native_value_type poly{}; - for (const auto& paths : message.polygons().polygons()) - { - Polygon o{}; - for (const auto& point : paths.outline().path()) - { - o.add(Point{ point.x(), point.y() }); - } - poly.add(o); - - for (const auto& hole : paths.holes()) - { - Polygon h{}; - for (const auto& point : hole.path()) - { - h.add(Point{ point.x(), point.y() }); - } - poly.add(h); - } - } - return poly; - } + native_value_type operator()(const value_type& message) const; }; @@ -252,12 +148,7 @@ struct postprocess_request * @param gcode The native G-code string. * @return The converted `proto::PostprocessRequest` message. */ - value_type operator()(const native_value_type& gcode) const - { - value_type message{}; - message.set_gcode_word(gcode); - return message; - } + value_type operator()(const native_value_type& gcode) const; }; struct postprocess_response @@ -265,10 +156,7 @@ struct postprocess_response using value_type = slots::postprocess::v0::modify::CallResponse; using native_value_type = std::string; - native_value_type operator()(const value_type& message) const - { - return message.gcode_word(); - } + native_value_type operator()(const value_type& message) const; }; struct infill_generate_request @@ -276,135 +164,31 @@ struct infill_generate_request using value_type = slots::infill::v0::generate::CallRequest; using native_value_type = Polygons; - value_type operator()(const native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const - { - value_type message{}; - message.set_pattern(pattern); - auto* msg_settings = message.mutable_settings()->mutable_settings(); - for (const auto& [key, value] : settings.getFlattendSettings()) - { - msg_settings->insert({ key, value }); - } - - if (inner_contour.empty()) - { - return message; - } - - auto* msg_polygons = message.mutable_infill_areas(); - auto* msg_polygon = msg_polygons->add_polygons(); - auto* msg_outline = msg_polygon->mutable_outline(); - - for (const auto& point : ranges::front(inner_contour.paths)) - { - auto* msg_outline_path = msg_outline->add_path(); - msg_outline_path->set_x(point.X); - msg_outline_path->set_y(point.Y); - } - - auto* msg_holes = msg_polygon->mutable_holes(); - for (const auto& polygon : inner_contour.paths | ranges::views::drop(1)) - { - auto* msg_hole = msg_holes->Add(); - for (const auto& point : polygon) - { - auto* msg_path = msg_hole->add_path(); - msg_path->set_x(point.X); - msg_path->set_y(point.Y); - } - } - - return message; - } + value_type operator()(const native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const; }; struct infill_generate_response { using value_type = slots::infill::v0::generate::CallResponse; - using native_value_type = std::tuple, Polygons, Polygons>; - - native_value_type operator()(const value_type& message) const - { - VariableWidthLines toolpaths; - Polygons result_polygons; - Polygons result_lines; - - for (auto& tool_path : message.tool_paths().tool_paths()) - { - ExtrusionLine lines; - for (auto& msg_junction : tool_path.junctions()) - { - auto& p = msg_junction.point(); - auto junction = ExtrusionJunction{ p.x(), p.y(), msg_junction.width() }; - lines.emplace_back(junction); - } - - toolpaths.push_back(lines); - } - - std::vector toolpaths_; - toolpaths_.push_back(toolpaths); - - for (auto& polygon_msg : message.polygons().polygons()) - { - Polygons polygon{}; - - Polygon outline{}; - for (auto& path_msg : polygon_msg.outline().path()) - { - outline.add(Point{ path_msg.x(), path_msg.y() }); - } - polygon.add(outline); - - - for (auto& hole_msg : polygon_msg.holes()) - { - Polygon hole{}; - for (auto& path_msg : hole_msg.path()) - { - hole.add(Point{ path_msg.x(), path_msg.y() }); - } - polygon.add(hole); - } - - result_polygons.add(polygon); - } - - for (auto& polygon : message.poly_lines().paths()) - { - Polygon poly_line; - for (auto& p : polygon.path()) - { - poly_line.emplace_back(Point{ p.x(), p.y() }); - } - result_lines.emplace_back(poly_line); - } - - return { toolpaths_, result_polygons, result_lines }; - } + using native_value_type = std::tuple>, Polygons, Polygons>; + + native_value_type operator()(const value_type& message) const; }; struct gcode_paths_modify_request { using value_type = slots::gcode_paths::v0::modify::CallRequest; - using native_value_type = std::vector; + using native_value_type = std::vector; - value_type operator()(const native_value_type& paths, const std::integral auto extruder_nr, const std::integral auto layer_nr) const - { - value_type message{}; - return message; - } + value_type operator()(const native_value_type& paths, const size_t extruder_nr, const LayerIndex layer_nr) const; }; struct gcode_paths_modify_response { using value_type = slots::gcode_paths::v0::modify::CallResponse; - using native_value_type = std::vector; + using native_value_type = std::vector; - native_value_type operator()(const value_type& message) const - { - return {}; - } + native_value_type operator()(const value_type& message) const; }; } // namespace cura::plugins diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp new file mode 100644 index 0000000000..e022497894 --- /dev/null +++ b/src/plugins/converters.cpp @@ -0,0 +1,294 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + + +#include "plugins/converters.h" + +#include "WallToolPaths.h" +#include "pathPlanning/GCodePath.h" +#include "settings/Settings.h" +#include "settings/types/LayerIndex.h" +#include "utils/polygon.h" + +namespace cura::plugins +{ + +empty::value_type empty::operator()() const +{ + return {}; +} + +constexpr empty::native_value_type empty::operator()(const value_type&) const +{ + return nullptr; +} + +broadcast_settings_request::value_type broadcast_settings_request::operator()(const broadcast_settings_request::native_value_type& slice_message) const +{ + value_type message{}; + auto* global_settings = message.mutable_global_settings()->mutable_settings(); + for (const auto& setting : slice_message.global_settings().settings()) + { + global_settings->emplace(setting.name(), setting.value()); + } + + auto* extruders_settings = message.mutable_extruder_settings(); + for (const auto& extruder : slice_message.extruders()) + { + auto* settings = extruders_settings->Add()->mutable_settings(); + for (const auto& setting : extruder.settings().settings()) + { + settings->emplace(setting.name(), setting.value()); + } + } + + auto* object_settings = message.mutable_object_settings(); + for (const auto& object : slice_message.object_lists()) + { + auto* settings = object_settings->Add()->mutable_settings(); + for (const auto& setting : object.settings()) + { + settings->emplace(setting.name(), setting.value()); + } + } + + auto* limit_to_extruder = message.mutable_limit_to_extruder(); + for (const auto& setting_extruder : slice_message.limit_to_extruder()) + { + limit_to_extruder->emplace(setting_extruder.name(), setting_extruder.extruder()); + } + return message; +} + +handshake_request::value_type handshake_request::operator()(const handshake_request::native_value_type& slot_info) const +{ + value_type message{}; + message.set_slot_id(slot_info.slot_id); + message.set_version_range(slot_info.version_range.data()); + return message; +} + +handshake_response::native_value_type handshake_response::operator()(const handshake_response::value_type& message, std::string_view peer) const +{ + return { .slot_version = message.slot_version(), + .plugin_name = message.plugin_name(), + .plugin_version = message.plugin_version(), + .peer = std::string{ peer }, + .broadcast_subscriptions = std::set(message.broadcast_subscriptions().begin(), message.broadcast_subscriptions().end()) }; +} + +simplify_request::value_type + simplify_request::operator()(const simplify_request::native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) + const +{ + value_type message{}; + if (polygons.empty()) + { + return message; + } + + auto* msg_polygons = message.mutable_polygons(); + auto* msg_polygon = msg_polygons->add_polygons(); + auto* msg_outline = msg_polygon->mutable_outline(); + + for (const auto& point : ranges::front(polygons.paths)) + { + auto* msg_outline_path = msg_outline->add_path(); + msg_outline_path->set_x(point.X); + msg_outline_path->set_y(point.Y); + } + + auto* msg_holes = msg_polygon->mutable_holes(); + for (const auto& polygon : polygons.paths | ranges::views::drop(1)) + { + auto* msg_hole = msg_holes->Add(); + for (const auto& point : polygon) + { + auto* msg_path = msg_hole->add_path(); + msg_path->set_x(point.X); + msg_path->set_y(point.Y); + } + } + + message.set_max_resolution(max_resolution); + message.set_max_deviation(max_resolution); + message.set_max_area_deviation(max_resolution); + return message; +} + +simplify_response::native_value_type simplify_response::operator()(const simplify_response::value_type& message) const +{ + native_value_type poly{}; + for (const auto& paths : message.polygons().polygons()) + { + Polygon o{}; + for (const auto& point : paths.outline().path()) + { + o.add(Point{ point.x(), point.y() }); + } + poly.add(o); + + for (const auto& hole : paths.holes()) + { + Polygon h{}; + for (const auto& point : hole.path()) + { + h.add(Point{ point.x(), point.y() }); + } + poly.add(h); + } + } + return poly; +} + +postprocess_request::value_type postprocess_request::operator()(const postprocess_request::native_value_type& gcode) const +{ + value_type message{}; + message.set_gcode_word(gcode); + return message; +} + +postprocess_response::native_value_type postprocess_response::operator()(const postprocess_response::value_type& message) const +{ + return message.gcode_word(); +} + +infill_generate_request::value_type + infill_generate_request::operator()(const infill_generate_request::native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const +{ + value_type message{}; + message.set_pattern(pattern); + auto* msg_settings = message.mutable_settings()->mutable_settings(); + for (const auto& [key, value] : settings.getFlattendSettings()) + { + msg_settings->insert({ key, value }); + } + + if (inner_contour.empty()) + { + return message; + } + + auto* msg_polygons = message.mutable_infill_areas(); + auto* msg_polygon = msg_polygons->add_polygons(); + auto* msg_outline = msg_polygon->mutable_outline(); + + for (const auto& point : ranges::front(inner_contour.paths)) + { + auto* msg_outline_path = msg_outline->add_path(); + msg_outline_path->set_x(point.X); + msg_outline_path->set_y(point.Y); + } + + auto* msg_holes = msg_polygon->mutable_holes(); + for (const auto& polygon : inner_contour.paths | ranges::views::drop(1)) + { + auto* msg_hole = msg_holes->Add(); + for (const auto& point : polygon) + { + auto* msg_path = msg_hole->add_path(); + msg_path->set_x(point.X); + msg_path->set_y(point.Y); + } + } + + return message; +} + +infill_generate_response::native_value_type infill_generate_response::operator()(const infill_generate_response::value_type& message) const +{ + VariableWidthLines toolpaths; + Polygons result_polygons; + Polygons result_lines; + + for (auto& tool_path : message.tool_paths().tool_paths()) + { + ExtrusionLine lines; + for (auto& msg_junction : tool_path.junctions()) + { + auto& p = msg_junction.point(); + auto junction = ExtrusionJunction{ p.x(), p.y(), msg_junction.width() }; + lines.emplace_back(junction); + } + + toolpaths.push_back(lines); + } + + std::vector toolpaths_; + toolpaths_.push_back(toolpaths); + + for (auto& polygon_msg : message.polygons().polygons()) + { + Polygons polygon{}; + + Polygon outline{}; + for (auto& path_msg : polygon_msg.outline().path()) + { + outline.add(Point{ path_msg.x(), path_msg.y() }); + } + polygon.add(outline); + + + for (auto& hole_msg : polygon_msg.holes()) + { + Polygon hole{}; + for (auto& path_msg : hole_msg.path()) + { + hole.add(Point{ path_msg.x(), path_msg.y() }); + } + polygon.add(hole); + } + + result_polygons.add(polygon); + } + + for (auto& polygon : message.poly_lines().paths()) + { + Polygon poly_line; + for (auto& p : polygon.path()) + { + poly_line.emplace_back(Point{ p.x(), p.y() }); + } + result_lines.emplace_back(poly_line); + } + + return { toolpaths_, result_polygons, result_lines }; +} + + +gcode_paths_modify_request::value_type + gcode_paths_modify_request::operator()(const gcode_paths_modify_request::native_value_type& paths, const size_t extruder_nr, const LayerIndex layer_nr) const +{ + value_type message{}; + message.set_extruder_nr(extruder_nr); + message.set_layer_nr(layer_nr); + + // Construct the repeated GCodepath message + auto* gcode_paths = message.mutable_gcode_paths(); + for (const auto& path : paths) + { + auto* gcode_path = gcode_paths->Add(); + + // Construct the OpenPath from the points in a GCodePath + auto* points = gcode_path->mutable_path()->add_path(); + for (const auto& point : path.points) + { + points->set_x(point.X); + points->set_y(point.Y); + } + + // Construct the estimations for the GCodePath + auto* estimations = gcode_path->mutable_estimates(); + estimations->set_extrude_time(path.estimates.extrude_time); + } + + + return message; +} + + +gcode_paths_modify_response::native_value_type gcode_paths_modify_response::operator()(const gcode_paths_modify_response::value_type& message) const +{ + return gcode_paths_modify_response::native_value_type(); +} +} // namespace cura::plugins \ No newline at end of file From 22af2e07348316573ef3596e5fb20e841b8ab003 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 8 Aug 2023 08:58:31 +0000 Subject: [PATCH 319/656] Applied clang-format. --- src/LayerPlanBuffer.cpp | 55 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index a985c8306b..6551543bdc 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -1,17 +1,18 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include +#include "LayerPlanBuffer.h" #include "Application.h" //To flush g-code through the communication channel. #include "ExtruderTrain.h" #include "FffProcessor.h" #include "LayerPlan.h" -#include "LayerPlanBuffer.h" #include "Slice.h" #include "communication/Communication.h" //To flush g-code through the communication channel. #include "gcodeExport.h" +#include + namespace cura { @@ -99,7 +100,8 @@ void LayerPlanBuffer::addConnectingTravelMove(LayerPlan* prev_layer, const Layer prev_layer->setIsInside(new_layer_destination_state->second); const bool force_retract = extruder_settings.get("retract_at_layer_change") || (mesh_group_settings.get("travel_retract_before_outer_wall") - && (mesh_group_settings.get("inset_direction") == InsetDirection::OUTSIDE_IN || mesh_group_settings.get("wall_line_count") == 1)); // Moving towards an outer wall. + && (mesh_group_settings.get("inset_direction") == InsetDirection::OUTSIDE_IN + || mesh_group_settings.get("wall_line_count") == 1)); // Moving towards an outer wall. prev_layer->final_travel_z = newest_layer->z; GCodePath& path = prev_layer->addTravel(first_location_new_layer, force_retract); if (force_retract && ! path.retract) @@ -180,7 +182,13 @@ Preheat::WarmUpResult LayerPlanBuffer::computeStandbyTempPlan(std::vector("material_standby_temperature"), initial_print_temp, during_printing); + Preheat::WarmUpResult warm_up = preheat_config.getWarmUpPointAfterCoolDown( + in_between_time, + extruder, + temp_before, + extruder_settings.get("material_standby_temperature"), + initial_print_temp, + during_printing); warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + extra_preheat_time); return warm_up; } @@ -233,10 +241,7 @@ void LayerPlanBuffer::handleStandbyTemp(std::vector& extruder_pla spdlog::warn("Couldn't find previous extruder plan so as to set the standby temperature. Inserting temp command in earliest available layer."); ExtruderPlan& earliest_extruder_plan = *extruder_plans[0]; constexpr bool wait = false; - earliest_extruder_plan.insertCommand(NozzleTempInsert{ .path_idx = 0, - .extruder = extruder, - .temperature = standby_temp, - .wait = wait }); + earliest_extruder_plan.insertCommand(NozzleTempInsert{ .path_idx = 0, .extruder = extruder, .temperature = standby_temp, .wait = wait }); } void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector& extruder_plans, unsigned int extruder_plan_idx) @@ -281,10 +286,8 @@ void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vectorinsertCommand(NozzleTempInsert { .path_idx = path_idx, - .extruder = extruder, - .temperature = initial_print_temp, - .wait = wait }); // insert preheat command at verfy beginning of buffer + extruder_plans[0]->insertCommand( + NozzleTempInsert{ .path_idx = path_idx, .extruder = extruder, .temperature = initial_print_temp, .wait = wait }); // insert preheat command at verfy beginning of buffer } void LayerPlanBuffer::insertTempCommands(std::vector& extruder_plans, unsigned int extruder_plan_idx) @@ -350,10 +353,7 @@ void LayerPlanBuffer::insertPrintTempCommand(ExtruderPlan& extruder_plan) } } bool wait = false; - extruder_plan.insertCommand(NozzleTempInsert{ .path_idx = path_idx, - .extruder = extruder, - .temperature = print_temp, - .wait = wait }); + extruder_plan.insertCommand(NozzleTempInsert{ .path_idx = path_idx, .extruder = extruder, .temperature = print_temp, .wait = wait }); } extruder_plan.heated_pre_travel_time = heated_pre_travel_time; } @@ -388,12 +388,14 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex } } - double time_window = - 0; // The time window within which the nozzle needs to heat from the initial print temp to the printing temperature and then back to the final print temp; i.e. from the first to the last extrusion move with this extruder - double weighted_average_extrusion_temp = 0; // The average of the normal extrusion temperatures of the extruder plans (which might be different due to flow dependent temp or due to initial layer temp) Weighted by time + double time_window = 0; // The time window within which the nozzle needs to heat from the initial print temp to the printing temperature and then back to the final print temp; + // i.e. from the first to the last extrusion move with this extruder + double weighted_average_extrusion_temp = 0; // The average of the normal extrusion temperatures of the extruder plans (which might be different due to flow dependent temp or + // due to initial layer temp) Weighted by time std::optional initial_print_temp; // The initial print temp of the first extruder plan with this extruder { // compute time window and print temp statistics - double heated_pre_travel_time = -1; // The time before the first extrude move from the start of the extruder plan during which the nozzle is stable at the initial print temperature + double heated_pre_travel_time + = -1; // The time before the first extrude move from the start of the extruder plan during which the nozzle is stable at the initial print temperature for (unsigned int prev_extruder_plan_idx = last_extruder_plan_idx; (int)prev_extruder_plan_idx >= 0; prev_extruder_plan_idx--) { ExtruderPlan& prev_extruder_plan = *extruder_plans[prev_extruder_plan_idx]; @@ -442,7 +444,8 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex // This approximation is quite ok since it only determines where to insert the precool temp command, // which means the stable temperature of the previous extruder plan and the stable temperature of the next extruder plan couldn't be reached constexpr bool during_printing = true; - Preheat::CoolDownResult warm_cool_result = preheat_config.getCoolDownPointAfterWarmUp(time_window, extruder, *initial_print_temp, weighted_average_extrusion_temp, final_print_temp, during_printing); + Preheat::CoolDownResult warm_cool_result + = preheat_config.getCoolDownPointAfterWarmUp(time_window, extruder, *initial_print_temp, weighted_average_extrusion_temp, final_print_temp, during_printing); double cool_down_time = warm_cool_result.cooling_time; assert(cool_down_time >= 0); @@ -465,7 +468,8 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex } } - // at this point cool_down_time is what time is left if cool down time of extruder plans after precool_extruder_plan (up until last_extruder_plan) are already taken into account + // at this point cool_down_time is what time is left if cool down time of extruder plans after precool_extruder_plan (up until last_extruder_plan) are already taken into + // account { // insert temp command in precool_extruder_plan double extrusion_time_seen = 0; @@ -481,11 +485,8 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex } bool wait = false; double time_after_path_start = extrusion_time_seen - cool_down_time; - precool_extruder_plan->insertCommand(NozzleTempInsert { .path_idx = path_idx, - .extruder = extruder, - .temperature = final_print_temp, - .wait = wait, - .time_after_path_start = time_after_path_start }); + precool_extruder_plan->insertCommand( + NozzleTempInsert{ .path_idx = path_idx, .extruder = extruder, .temperature = final_print_temp, .wait = wait, .time_after_path_start = time_after_path_start }); } } From 62e05eb1654a24c78d9d36d176a75df7fc4c6be3 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 11:41:43 +0200 Subject: [PATCH 320/656] Use CRTP for convert specializations CURA-10446 --- include/plugins/converters.h | 125 ++++++++--------------------------- 1 file changed, 29 insertions(+), 96 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index dac97eb8b7..8ff5ad743e 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -41,6 +41,22 @@ class Settings; namespace cura::plugins { +namespace details +{ +template +struct converter +{ + using derived_type = T; ///< The derived type. + using value_type = Msg; ///< The protobuf message type. + using native_value_type = Native; ///< The native value type. + friend derived_type; + + constexpr auto operator()(auto&&... args) const + { + return static_cast(this)->operator()(std::forward(args)...); + } +}; +} struct empty { @@ -52,142 +68,59 @@ struct empty constexpr native_value_type operator()(const value_type&) const; }; -struct broadcast_settings_request +struct broadcast_settings_request : public details::converter { - using value_type = slots::broadcast::v0::BroadcastServiceSettingsRequest; ///< The protobuf message type. - using native_value_type = cura::proto::Slice; ///< The native value type. - - /** - * @brief Converts native data for broadcasting to a `proto::BroadcastServiceSettingsRequest` message. - * - * @param key The key of the setting to be broadcasted. - * @param value The value of the setting to be broadcasted. - * @return The converted `proto::BroadcastServiceSettingsRequest` message. - */ value_type operator()(const native_value_type& slice_message) const; }; - -struct handshake_request +struct handshake_request : public details::converter { - using value_type = slots::handshake::v0::CallRequest; ///< The protobuf message type. - using native_value_type = slot_metadata; ///< The native value type. - - /** - * @brief Converts native data for handshake to a `proto::HandshakeRequest` message. - * - * @param service_name The name of the service. - * @param version_range The version range of the service. - * @return The converted `proto::HandshakeRequest` message. - */ - value_type operator()(const native_value_type& slot_info) const; }; -struct handshake_response +struct handshake_response : public details::converter { - using value_type = slots::handshake::v0::CallResponse; ///< The protobuf message type. - using native_value_type = plugin_metadata; ///< The native value type. - - /** - * @brief Converts a `proto::HandshakeResponse` message to native data. - * - * @param message The `proto::HandshakeResponse` message. - * @return The native data. - */ native_value_type operator()(const value_type& message, std::string_view peer) const; }; - -struct simplify_request +struct simplify_request : public details::converter { - using value_type = slots::simplify::v0::modify::CallRequest; ///< The protobuf message type. - using native_value_type = Polygons; ///< The native value type. - - /** - * @brief Converts native data for simplification to a `proto::SimplifyRequest` message. - * - * @param polygons The polygons to be simplified. - * @param max_resolution The maximum resolution for the simplified polygons. - * @param max_deviation The maximum deviation for the simplified polygons. - * @param max_area_deviation The maximum area deviation for the simplified polygons. - * @return The converted `proto::SimplifyRequest` message. - */ value_type operator()(const native_value_type& polygons, const coord_t max_resolution, const coord_t max_deviation, const coord_t max_area_deviation) const; }; -/** - * @brief A converter struct for simplify responses. - * - * The `simplify_response` struct provides a conversion function that converts a `proto::SimplifyResponse` - * message to a native value type. - */ -struct simplify_response -{ - using value_type = slots::simplify::v0::modify::CallResponse; ///< The protobuf message type. - using native_value_type = Polygons; ///< The native value type. - - /** - * @brief Converts a `proto::SimplifyResponse` message to a native value type. - * - * @param message The `proto::SimplifyResponse` message. - * @return The converted native value. - */ +struct simplify_response : public details::converter +{ native_value_type operator()(const value_type& message) const; }; - -struct postprocess_request +struct postprocess_request : public details::converter { - using value_type = slots::postprocess::v0::modify::CallRequest; ///< The protobuf message type. - using native_value_type = std::string; ///< The native value type. - - /** - * @brief Converts a native G-code string to a `proto::PostprocessRequest` message. - * - * @param gcode The native G-code string. - * @return The converted `proto::PostprocessRequest` message. - */ value_type operator()(const native_value_type& gcode) const; }; -struct postprocess_response +struct postprocess_response : public details::converter { - using value_type = slots::postprocess::v0::modify::CallResponse; - using native_value_type = std::string; - native_value_type operator()(const value_type& message) const; }; -struct infill_generate_request +struct infill_generate_request : public details::converter { - using value_type = slots::infill::v0::generate::CallRequest; - using native_value_type = Polygons; - value_type operator()(const native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const; }; struct infill_generate_response + : public details::converter>, Polygons, Polygons>> { - using value_type = slots::infill::v0::generate::CallResponse; - using native_value_type = std::tuple>, Polygons, Polygons>; - native_value_type operator()(const value_type& message) const; }; -struct gcode_paths_modify_request +struct gcode_paths_modify_request : public details::converter> { - using value_type = slots::gcode_paths::v0::modify::CallRequest; - using native_value_type = std::vector; - - value_type operator()(const native_value_type& paths, const size_t extruder_nr, const LayerIndex layer_nr) const; + value_type operator()(const native_value_type& gcode, const size_t extruder_nr, const LayerIndex layer_nr) const; }; -struct gcode_paths_modify_response +struct gcode_paths_modify_response : public details::converter> { - using value_type = slots::gcode_paths::v0::modify::CallResponse; - using native_value_type = std::vector; - native_value_type operator()(const value_type& message) const; }; From 90ed90bc4b8d3312ce691ec2c0714b6edb2e8bc2 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 8 Aug 2023 11:05:06 +0000 Subject: [PATCH 321/656] Applied clang-format. --- include/plugins/converters.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 8ff5ad743e..083930599f 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -50,13 +50,13 @@ struct converter using value_type = Msg; ///< The protobuf message type. using native_value_type = Native; ///< The native value type. friend derived_type; - + constexpr auto operator()(auto&&... args) const { return static_cast(this)->operator()(std::forward(args)...); } }; -} +} // namespace details struct empty { From 5174cc353b2fc42366bf60b6809eb06c948c1367 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 14:12:47 +0200 Subject: [PATCH 322/656] Get settings from global stack when mesh is nullptr CURA-10619 --- src/infill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infill.cpp b/src/infill.cpp index 9e54cfc074..701d5b21f4 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -311,7 +311,7 @@ void Infill::_generate( case EFillMethod::PLUGIN: { auto [toolpaths_, generated_result_polygons_, generated_result_lines_] - = slots::instance().generate(inner_contour, mesh->settings.get("infill_pattern"), mesh->settings); + = slots::instance().generate(inner_contour, mesh ? mesh->settings.get("infill_pattern") : settings.get("infill_pattern"), mesh ? mesh->settings : settings); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); result_polygons.add(generated_result_polygons_); result_lines.add(generated_result_lines_); From 423234b4557f60dfe19817151c1f4ff2067787a4 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 8 Aug 2023 12:13:21 +0000 Subject: [PATCH 323/656] Applied clang-format. --- src/infill.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 701d5b21f4..c3e43854d9 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -310,8 +310,10 @@ void Infill::_generate( break; case EFillMethod::PLUGIN: { - auto [toolpaths_, generated_result_polygons_, generated_result_lines_] - = slots::instance().generate(inner_contour, mesh ? mesh->settings.get("infill_pattern") : settings.get("infill_pattern"), mesh ? mesh->settings : settings); + auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate( + inner_contour, + mesh ? mesh->settings.get("infill_pattern") : settings.get("infill_pattern"), + mesh ? mesh->settings : settings); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); result_polygons.add(generated_result_polygons_); result_lines.add(generated_result_lines_); From f6822d7622a0bafe9fcf6cfd91b4870600cfda03 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 15:06:37 +0200 Subject: [PATCH 324/656] Switched order of member variables To not mess up pre-refactor initialization CURA-10446 --- include/pathPlanning/TimeMaterialEstimates.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pathPlanning/TimeMaterialEstimates.h b/include/pathPlanning/TimeMaterialEstimates.h index 68a33e3cd8..eecc69348c 100644 --- a/include/pathPlanning/TimeMaterialEstimates.h +++ b/include/pathPlanning/TimeMaterialEstimates.h @@ -10,11 +10,11 @@ namespace cura struct TimeMaterialEstimates { double extrude_time{ 0.0 }; //!< Time in seconds occupied by extrusion - double extrude_time_at_slowest_path_speed{ 0.0 }; //!< Time in seconds occupied by extrusion assuming paths are printed at slowest path speed, usually the outer wall speed - double extrude_time_at_minimum_speed{ 0.0 }; //!< Time in seconds occupied by extrusion assuming paths are printed at the user specified Minimum Speed double unretracted_travel_time{ 0.0 }; //!< Time in seconds occupied by non-retracted travel (non-extrusion) double retracted_travel_time{ 0.0 }; //!< Time in seconds occupied by retracted travel (non-extrusion) double material{ 0.0 }; //!< Material used (in mm^3) + double extrude_time_at_slowest_path_speed{ 0.0 }; //!< Time in seconds occupied by extrusion assuming paths are printed at slowest path speed, usually the outer wall speed + double extrude_time_at_minimum_speed{ 0.0 }; //!< Time in seconds occupied by extrusion assuming paths are printed at the user specified Minimum Speed constexpr TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other) noexcept { From 6f34785069834b867c0c4572c69e65fdde7767ac Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 15:12:04 +0200 Subject: [PATCH 325/656] Fixed missing includes and forward decleration CURA-10446 --- include/LayerPlanBuffer.h | 77 +++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index 4e05ffcade..3f97b4e274 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -1,32 +1,35 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef LAYER_PLAN_BUFFER_H #define LAYER_PLAN_BUFFER_H -#include - #include "Preheat.h" +#include "settings/Settings.h" #include "settings/types/Duration.h" -namespace cura +#include +#include + +namespace cura { class ExtruderPlan; class LayerPlan; +class GCodeExport; /*! * Class for buffering multiple layer plans (\ref LayerPlan) / extruder plans within those layer plans, so that temperature commands can be inserted in earlier layer plans. - * + * * This class handles where to insert temperature commands for: * - initial layer temperature * - flow dependent temperature * - starting to heat up from the standby temperature * - initial printing temperature | printing temperature | final printing temperature - * + * * \image html assets/precool.png "Temperature Regulation" width=10cm * \image latex assets/precool.png "Temperature Regulation" width=10cm - * + * */ class LayerPlanBuffer { @@ -34,25 +37,30 @@ class LayerPlanBuffer Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train. - static constexpr size_t buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value + static constexpr size_t buffer_size + = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value // this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted. - static constexpr Duration extra_preheat_time = 1.0_s; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones. + static constexpr Duration extra_preheat_time + = 1.0_s; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones. - std::vector extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to the initial_print_temp or to the extrusion_temperature + std::vector extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to + //!< the initial_print_temp or to the extrusion_temperature /*! * The buffer containing several layer plans (LayerPlan) before writing them to gcode. - * + * * The front is the lowest/oldest layer. * The back is the highest/newest layer. */ std::list buffer; + public: LayerPlanBuffer(GCodeExport& gcode) - : gcode(gcode) - , extruder_used_in_meshgroup(MAX_EXTRUDERS, false) - { } + : gcode(gcode) + , extruder_used_in_meshgroup(MAX_EXTRUDERS, false) + { + } void setPreheatConfig(); @@ -64,7 +72,7 @@ class LayerPlanBuffer /*! * Push a new layer onto the buffer and handle the buffer. * Write a layer to gcode if it is popped out of the buffer. - * + * * \param layer_plan The layer to handle * \param gcode The exporter with which to write a layer to gcode if the buffer is too large after pushing the new layer. */ @@ -81,7 +89,7 @@ class LayerPlanBuffer * This inserts the temperature commands to start warming for a given layer in earlier layers; * the fan speeds and layer time settings of the most recently pushed layer are processed; * the correctly combing travel move between the last added layer and the layer before is added. - * + * * Pop out the earliest layer in the buffer if the buffer size is exceeded * \return A nullptr or the popped gcode_layer */ @@ -89,7 +97,7 @@ class LayerPlanBuffer /*! * Add the travel move to properly travel from the end location of the previous layer to the starting location of the next - * + * * \param prev_layer The layer before the just added layer, to which to add the combing travel move. * \param newest_layer The newly added layer, with a non-combing travel move as first path. */ @@ -102,7 +110,7 @@ class LayerPlanBuffer /*! * Insert a preheat command for @p extruder into @p extruder_plan_before - * + * * \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command * \param time_before_extruder_plan_end The time before the end of the extruder plan, before which to insert the preheat command * \param extruder_nr The extruder for which to set the temperature @@ -113,9 +121,9 @@ class LayerPlanBuffer /*! * Compute the time needed to preheat from standby to required (initial) printing temperature at the start of an extruder plan, * based on the time the extruder has been on standby. - * + * * Also computes the temperature to which we cool before starting to heat agian. - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to find the preheat time needed * \return the time needed to preheat and the temperature from which heating starts @@ -123,11 +131,11 @@ class LayerPlanBuffer Preheat::WarmUpResult computeStandbyTempPlan(std::vector& extruder_plans, unsigned int extruder_plan_idx); /*! - * For two consecutive extruder plans of the same extruder (so on different layers), + * For two consecutive extruder plans of the same extruder (so on different layers), * preheat the extruder to the temperature corresponding to the average flow of the second extruder plan. - * + * * The preheat commands are inserted such that the middle of the temperature change coincides with the start of the next layer. - * + * * \param prev_extruder_plan The former extruder plan (of the former layer) * \param extruder_nr The extruder for which too set the temperature * \param required_temp The required temperature for the second extruder plan @@ -139,7 +147,7 @@ class LayerPlanBuffer * Find the time window in which this extruder hasn't been used * and compute at what time the preheat command needs to be inserted. * Then insert the preheat command in the right extruder plan. - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to find the preheat time needed */ @@ -148,15 +156,15 @@ class LayerPlanBuffer /*! * Insert temperature commands related to the extruder plan corersponding to @p extruder_plan_idx * and the extruder plan before: - * + * * In case the extruder plan before has the same extruder: * - gradually change printing temperature around the layer change (\ref LayerPlanBuffer::insertPreheatCommand_singleExtrusion) - * + * * In case the previous extruder plan is a different extruder * - insert preheat command from standby to initial temp in the extruder plan(s) before (\ref LayerPlanBuffer::insertPreheatCommand_multiExtrusion) * - insert the final print temp command of the previous extruder plan (\ref LayerPlanBuffer::insertFinalPrintTempCommand) * - insert the normal extrusion temp command for the current extruder plan (\ref LayerPlanBuffer::insertPrintTempCommand) - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to generate the preheat command */ @@ -164,20 +172,20 @@ class LayerPlanBuffer /*! * Insert the temperature command to heat from the initial print temperature to the printing temperature - * + * * The temperature command is insert at the start of the very first extrusion move - * + * * \param extruder_plan The extruder plan in which to insert the heat up command */ void insertPrintTempCommand(ExtruderPlan& extruder_plan); /*! * Insert the temp command to start cooling from the printing temperature to the final print temp - * + * * The print temp is inserted before the last extrusion move of the extruder plan corresponding to \p last_extruder_plan_idx - * + * * The command is inserted at a timed offset before the end of the last extrusion move - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param last_extruder_plan_idx The index of the last extruder plan in \p extruder_plans with the same extruder as previous extruder plans */ @@ -192,7 +200,7 @@ class LayerPlanBuffer * Reconfigure the standby temperature during which we didn't print with this extruder. * Find the previous extruder plan with the same extruder as layers[layer_plan_idx].extruder_plans[extruder_plan_idx] * Set the prev_extruder_standby_temp in the next extruder plan - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans before which to reconfigure the standby temperature * \param standby_temp The temperature to which to cool down when the extruder is in standby mode. @@ -201,7 +209,6 @@ class LayerPlanBuffer }; - } // namespace cura #endif // LAYER_PLAN_BUFFER_H \ No newline at end of file From 851bac857d33136a48bb03b2df46667370eb4587 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 8 Aug 2023 15:30:17 +0200 Subject: [PATCH 326/656] Add plugin/converters.cpp to CMakeLists.txt CURA-10446 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 286820b6fa..ad72830e85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,7 @@ set(engine_SRCS # Except main.cpp. src/pathPlanning/LinePolygonsCrossings.cpp src/pathPlanning/NozzleTempInsert.cpp + src/plugins/converters.cpp src/progress/Progress.cpp src/progress/ProgressStageEstimator.cpp From b4d1828d61f7230eb35ee32540dd7024c481357c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 8 Aug 2023 17:22:41 +0200 Subject: [PATCH 327/656] Add setting to prevent the top layer from doing small-skin behaviour. Actually the other way around, so we don't have another negation you have to turn on to turn off (and so the default can be false I suppose). The idea is that recently, a feature was introduced where there should be walls instead of normal skin in small hard to reach places of the model. However, this is to be excluded in most cases for all the top-layer parts exposed to air on the buildplate. implements CURA-10829 --- include/infill.h | 3 ++- src/FffGcodeWriter.cpp | 4 +++- src/FffPolygonGenerator.cpp | 2 +- src/TopSurface.cpp | 2 +- src/infill.cpp | 7 ++++++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/infill.h b/include/infill.h index 98276f4741..b3c915ff4c 100644 --- a/include/infill.h +++ b/include/infill.h @@ -130,7 +130,8 @@ class Infill SectionType section_type, const SierpinskiFillProvider* cross_fill_provider = nullptr, const LightningLayer* lightning_layer = nullptr, - const SliceMeshStorage* mesh = nullptr); + const SliceMeshStorage* mesh = nullptr, + const Polygons& prevent_small_exposed_to_air = Polygons()); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fd836e57a8..8e56c40f74 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2763,6 +2763,8 @@ void FffGcodeWriter::processSkinPrintFeature( constexpr bool skip_some_zags = false; constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; + const bool small_areas_on_surface = mesh.settings.get("small_skin_on_surface"); + const auto& exposed_to_air = mesh.layers[gcode_layer.getLayerNr()].top_surface.areas; Infill infill_comp( pattern, @@ -2788,7 +2790,7 @@ void FffGcodeWriter::processSkinPrintFeature( skip_some_zags, zag_skip_count, pocket_size); - infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN); + infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN, nullptr, nullptr, nullptr, small_areas_on_surface ? Polygons() : exposed_to_air); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 817e91ecde..8bd7c429fd 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -808,7 +808,7 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, const La SkinInfillAreaComputation skin_infill_area_computation(layer_nr, mesh, process_infill); skin_infill_area_computation.generateSkinsAndInfill(); - if (mesh.settings.get("ironing_enabled") && (! mesh.settings.get("ironing_only_highest_layer") || mesh.layer_nr_max_filled_layer == layer_nr)) + if (mesh.settings.get("ironing_enabled") && (! mesh.settings.get("ironing_only_highest_layer") || mesh.layer_nr_max_filled_layer == layer_nr) || ! mesh.settings.get("small_skin_on_surface")) { // Generate the top surface to iron over. mesh.layers[layer_nr].top_surface.setAreasFromMeshAndLayerNumber(mesh, layer_nr); diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index 86d546e4ad..35709cfc79 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -64,7 +64,7 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage const Ratio ironing_flow = mesh.settings.get("ironing_flow"); const bool enforce_monotonic_order = mesh.settings.get("ironing_monotonic"); constexpr size_t wall_line_count = 0; - const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width = 0; // This shouldn't be on for ironing. const Point infill_origin = Point(); const bool skip_line_stitching = enforce_monotonic_order; diff --git a/src/infill.cpp b/src/infill.cpp index 812d2e45ba..f5c7e6cc26 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -87,7 +87,8 @@ void Infill::generate( SectionType section_type, const SierpinskiFillProvider* cross_fill_provider, const LightningLayer* lightning_trees, - const SliceMeshStorage* mesh) + const SliceMeshStorage* mesh, + const Polygons& prevent_small_exposed_to_air) { if (outer_contour.empty()) { @@ -110,6 +111,10 @@ void Infill::generate( inner_contour = inner_contour.offset(-small_area_width / 2); inner_contour.removeSmallAreas(to_small_length * to_small_length, true); inner_contour = inner_contour.offset(small_area_width / 2); + if (prevent_small_exposed_to_air.area() > 0) + { + inner_contour = inner_contour.unionPolygons(prevent_small_exposed_to_air).intersection(small_infill); + } inner_contour = Simplify(max_resolution, max_deviation, 0).polygon(inner_contour); small_infill = small_infill.difference(inner_contour); From 279d93877df5e8fc6db2b54d60a50f79856e4237 Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 8 Aug 2023 15:23:40 +0000 Subject: [PATCH 328/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 12 +++- src/FffPolygonGenerator.cpp | 139 ++++++++++++++++++++++-------------- src/TopSurface.cpp | 93 ++++++++++++++++-------- 3 files changed, 161 insertions(+), 83 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 8e56c40f74..dc78114de1 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2790,7 +2790,17 @@ void FffGcodeWriter::processSkinPrintFeature( skip_some_zags, zag_skip_count, pocket_size); - infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, gcode_layer.getLayerNr(), SectionType::SKIN, nullptr, nullptr, nullptr, small_areas_on_surface ? Polygons() : exposed_to_air); + infill_comp.generate( + skin_paths, + skin_polygons, + skin_lines, + mesh.settings, + gcode_layer.getLayerNr(), + SectionType::SKIN, + nullptr, + nullptr, + nullptr, + small_areas_on_surface ? Polygons() : exposed_to_air); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 8bd7c429fd..6d436fa28b 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -1,14 +1,14 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include + #include #include #include // ifstream.good() #include // multimap (ordered map allowing duplicate keys) #include -#include - // Code smell: Order of the includes is important here, probably due to some forward declarations which might be masking some undefined behaviours // clang-format off #include "Application.h" @@ -80,7 +80,9 @@ size_t FffPolygonGenerator::getDraftShieldLayerCount(const size_t total_layers) case DraftShieldHeightLimitation::FULL: return total_layers; case DraftShieldHeightLimitation::LIMITED: - return std::max((coord_t)0, (mesh_group_settings.get("draft_shield_height") - mesh_group_settings.get("layer_height_0")) / mesh_group_settings.get("layer_height") + 1); + return std::max( + (coord_t)0, + (mesh_group_settings.get("draft_shield_height") - mesh_group_settings.get("layer_height_0")) / mesh_group_settings.get("layer_height") + 1); default: spdlog::warn("A draft shield height limitation option was added without implementing the new option in getDraftShieldLayerCount."); return total_layers; @@ -128,9 +130,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe const auto variable_layer_height_max_variation = mesh_group_settings.get("adaptive_layer_height_variation"); const auto variable_layer_height_variation_step = mesh_group_settings.get("adaptive_layer_height_variation_step"); const auto adaptive_threshold = mesh_group_settings.get("adaptive_layer_height_threshold"); - adaptive_layer_heights = new AdaptiveLayerHeights(layer_thickness, variable_layer_height_max_variation, - variable_layer_height_variation_step, adaptive_threshold, - meshgroup); + adaptive_layer_heights + = new AdaptiveLayerHeights(layer_thickness, variable_layer_height_max_variation, variable_layer_height_variation_step, adaptive_threshold, meshgroup); // Get the amount of layers slice_layer_count = adaptive_layer_heights->getLayerCount(); @@ -174,7 +175,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe case SlicingTolerance::INCLUSIVE: if (mesh_height < initial_layer_thickness) { - slice_layer_count = std::max(slice_layer_count, (mesh_height > 0) ? 1 : 0); // If less than the initial layer height, it always has 1 layer unless the height is truly zero. + slice_layer_count + = std::max(slice_layer_count, (mesh_height > 0) ? 1 : 0); // If less than the initial layer height, it always has 1 layer unless the height is truly zero. } else { @@ -265,7 +267,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe } storage.support.supportLayers.resize(storage.print_layer_count); - storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated. + storage.meshes.reserve( + slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated. for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++) { Slicer* slicer = slicerList[meshIdx]; @@ -322,7 +325,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe if (has_raft) { const ExtruderTrain& train = mesh_group_settings.get("raft_surface_extruder_nr"); - layer.printZ += Raft::getTotalThickness() + train.settings.get("raft_airgap") - train.settings.get("layer_0_z_overlap"); // shift all layers (except 0) down + layer.printZ += Raft::getTotalThickness() + train.settings.get("raft_airgap") + - train.settings.get("layer_0_z_overlap"); // shift all layers (except 0) down if (layer_nr == 0) { @@ -386,7 +390,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // only remove empty layers if we haven't generate support, because then support was added underneath the model. // for some materials it's better to print on support than on the build plate. const auto has_support = mesh_group_settings.get("support_enable") || mesh_group_settings.get("support_mesh"); - const auto remove_empty_first_layers = mesh_group_settings.get("remove_empty_first_layers") && !has_support; + const auto remove_empty_first_layers = mesh_group_settings.get("remove_empty_first_layers") && ! has_support; if (remove_empty_first_layers) { removeEmptyFirstLayers(storage, storage.print_layer_count); // changes storage.print_layer_count! @@ -438,7 +442,11 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& AreaSupport::generateSupportInfillFeatures(storage); } -void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate) +void FffPolygonGenerator::processBasicWallsSkinInfill( + SliceDataStorage& storage, + const size_t mesh_order_idx, + const std::vector& mesh_order, + ProgressStageEstimator& inset_skin_progress_estimate) { size_t mesh_idx = mesh_order[mesh_order_idx]; SliceMeshStorage& mesh = storage.meshes[mesh_idx]; @@ -485,14 +493,15 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, } guarded_progress = { inset_skin_progress_estimate }; // walls - cura::parallel_for(0, - mesh_layer_count, - [&](size_t layer_number) - { - spdlog::debug("Processing insets for layer {} of {}", layer_number, mesh.layers.size()); - processWalls(mesh, layer_number); - guarded_progress++; - }); + cura::parallel_for( + 0, + mesh_layer_count, + [&](size_t layer_number) + { + spdlog::debug("Processing insets for layer {} of {}", layer_number, mesh.layers.size()); + processWalls(mesh, layer_number); + guarded_progress++; + }); ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count); mesh_inset_skin_progress_estimator->nextStage(skin_estimator); @@ -527,17 +536,18 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, } guarded_progress.reset(); - cura::parallel_for(0, - mesh_layer_count, - [&](size_t layer_number) - { - spdlog::debug("Processing skins and infill layer {} of {}", layer_number, mesh.layers.size()); - if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/downskin and infill for the first X layers when spiralize is choosen. - { - processSkinsAndInfill(mesh, layer_number, process_infill); - } - guarded_progress++; - }); + cura::parallel_for( + 0, + mesh_layer_count, + [&](size_t layer_number) + { + spdlog::debug("Processing skins and infill layer {} of {}", layer_number, mesh.layers.size()); + if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/downskin and infill for the first X layers when spiralize is choosen. + { + processSkinsAndInfill(mesh, layer_number, process_infill); + } + guarded_progress++; + }); } void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order) @@ -667,13 +677,18 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) } // Pre-compute Cross Fractal - if (mesh.settings.get("infill_line_distance") > 0 && (mesh.settings.get("infill_pattern") == EFillMethod::CROSS || mesh.settings.get("infill_pattern") == EFillMethod::CROSS_3D)) + if (mesh.settings.get("infill_line_distance") > 0 + && (mesh.settings.get("infill_pattern") == EFillMethod::CROSS || mesh.settings.get("infill_pattern") == EFillMethod::CROSS_3D)) { const std::string cross_subdivision_spec_image_file = mesh.settings.get("cross_infill_density_image"); std::ifstream cross_fs(cross_subdivision_spec_image_file.c_str()); if (! cross_subdivision_spec_image_file.empty() && cross_fs.good()) { - mesh.cross_fill_provider = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width"), cross_subdivision_spec_image_file); + mesh.cross_fill_provider = new SierpinskiFillProvider( + mesh.bounding_box, + mesh.settings.get("infill_line_distance"), + mesh.settings.get("infill_line_width"), + cross_subdivision_spec_image_file); } else { @@ -681,7 +696,8 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) { spdlog::error("Cannot find density image: {}.", cross_subdivision_spec_image_file); } - mesh.cross_fill_provider = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width")); + mesh.cross_fill_provider + = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width")); } } @@ -696,7 +712,7 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) SkinInfillAreaComputation::combineInfillLayers(mesh); // Fuzzy skin. Disabled when using interlocking structures, the internal interlocking walls become fuzzy. - if (mesh.settings.get("magic_fuzzy_skin_enabled") && !mesh.settings.get("interlocking_enable")) + if (mesh.settings.get("magic_fuzzy_skin_enabled") && ! mesh.settings.get("interlocking_enable")) { processFuzzyWalls(mesh); } @@ -808,7 +824,8 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, const La SkinInfillAreaComputation skin_infill_area_computation(layer_nr, mesh, process_infill); skin_infill_area_computation.generateSkinsAndInfill(); - if (mesh.settings.get("ironing_enabled") && (! mesh.settings.get("ironing_only_highest_layer") || mesh.layer_nr_max_filled_layer == layer_nr) || ! mesh.settings.get("small_skin_on_surface")) + if (mesh.settings.get("ironing_enabled") && (! mesh.settings.get("ironing_only_highest_layer") || mesh.layer_nr_max_filled_layer == layer_nr) + || ! mesh.settings.get("small_skin_on_surface")) { // Generate the top surface to iron over. mesh.layers[layer_nr].top_surface.setAreasFromMeshAndLayerNumber(mesh, layer_nr); @@ -847,11 +864,14 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage // Height of where the support reaches. Scene& scene = Application::getInstance().current_slice->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const size_t support_infill_extruder_nr = mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; // TODO: Support extruder should be configurable per object. + const size_t support_infill_extruder_nr + = mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; // TODO: Support extruder should be configurable per object. max_print_height_per_extruder[support_infill_extruder_nr] = std::max(max_print_height_per_extruder[support_infill_extruder_nr], storage.support.layer_nr_max_filled_layer); - const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; // TODO: Support roof extruder should be configurable per object. + const size_t support_roof_extruder_nr + = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; // TODO: Support roof extruder should be configurable per object. max_print_height_per_extruder[support_roof_extruder_nr] = std::max(max_print_height_per_extruder[support_roof_extruder_nr], storage.support.layer_nr_max_filled_layer); - const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; // TODO: Support bottom extruder should be configurable per object. + const size_t support_bottom_extruder_nr + = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; // TODO: Support bottom extruder should be configurable per object. max_print_height_per_extruder[support_bottom_extruder_nr] = std::max(max_print_height_per_extruder[support_bottom_extruder_nr], storage.support.layer_nr_max_filled_layer); // Height of where the platform adhesion reaches. @@ -861,12 +881,12 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage case EPlatformAdhesion::SKIRT: case EPlatformAdhesion::BRIM: { - const std::vector skirt_brim_extruder_trains = mesh_group_settings.get>("skirt_brim_extruder_nr"); - for (ExtruderTrain* train : skirt_brim_extruder_trains) - { - const size_t skirt_brim_extruder_nr = train->extruder_nr; - max_print_height_per_extruder[skirt_brim_extruder_nr] = std::max(0, max_print_height_per_extruder[skirt_brim_extruder_nr]); // Includes layer 0. - } + const std::vector skirt_brim_extruder_trains = mesh_group_settings.get>("skirt_brim_extruder_nr"); + for (ExtruderTrain* train : skirt_brim_extruder_trains) + { + const size_t skirt_brim_extruder_nr = train->extruder_nr; + max_print_height_per_extruder[skirt_brim_extruder_nr] = std::max(0, max_print_height_per_extruder[skirt_brim_extruder_nr]); // Includes layer 0. + } break; } case EPlatformAdhesion::RAFT: @@ -874,9 +894,11 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage const size_t base_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; max_print_height_per_extruder[base_extruder_nr] = std::max(-raft_layers, max_print_height_per_extruder[base_extruder_nr]); // Includes the lowest raft layer. const size_t interface_extruder_nr = mesh_group_settings.get("raft_interface_extruder_nr").extruder_nr; - max_print_height_per_extruder[interface_extruder_nr] = std::max(-raft_layers + 1, max_print_height_per_extruder[interface_extruder_nr]); // Includes the second-lowest raft layer. + max_print_height_per_extruder[interface_extruder_nr] + = std::max(-raft_layers + 1, max_print_height_per_extruder[interface_extruder_nr]); // Includes the second-lowest raft layer. const size_t surface_extruder_nr = mesh_group_settings.get("raft_surface_extruder_nr").extruder_nr; - max_print_height_per_extruder[surface_extruder_nr] = std::max(-1, max_print_height_per_extruder[surface_extruder_nr]); // Includes up to the first layer below the model (so -1). + max_print_height_per_extruder[surface_extruder_nr] + = std::max(-1, max_print_height_per_extruder[surface_extruder_nr]); // Includes up to the first layer below the model (so -1). break; } default: @@ -917,7 +939,8 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) const AngleDegrees angle = mesh_group_settings.get("ooze_shield_angle"); if (angle <= 89) { - const coord_t allowed_angle_offset = tan(mesh_group_settings.get("ooze_shield_angle")) * mesh_group_settings.get("layer_height"); // Allow for a 60deg angle in the oozeShield. + const coord_t allowed_angle_offset + = tan(mesh_group_settings.get("ooze_shield_angle")) * mesh_group_settings.get("layer_height"); // Allow for a 60deg angle in the oozeShield. for (LayerIndex layer_nr = 1; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++) { storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr - 1].offset(-allowed_angle_offset)); @@ -941,7 +964,8 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) const auto& extruders = Application::getInstance().current_slice->scene.extruders; for (int extruder_nr = 0; extruder_nr < int(extruders.size()); extruder_nr++) { - if ( ! extruder_is_used[extruder_nr]) continue; + if (! extruder_is_used[extruder_nr]) + continue; max_line_width = std::max(max_line_width, extruders[extruder_nr].settings.get("skirt_brim_line_width")); } } @@ -992,7 +1016,8 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) const auto& extruders = Application::getInstance().current_slice->scene.extruders; for (int extruder_nr = 0; extruder_nr < int(extruders.size()); extruder_nr++) { - if ( ! extruder_is_used[extruder_nr]) continue; + if (! extruder_is_used[extruder_nr]) + continue; max_line_width = std::max(max_line_width, extruders[extruder_nr].settings.get("skirt_brim_line_width")); } } @@ -1047,10 +1072,14 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) const coord_t avg_dist_between_points = mesh.settings.get("magic_fuzzy_skin_point_dist"); const coord_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value const coord_t range_random_point_dist = avg_dist_between_points / 2; - unsigned int start_layer_nr = (mesh.settings.get("adhesion_type") == EPlatformAdhesion::BRIM) ? 1 : 0; // don't make fuzzy skin on first layer if there's a brim + unsigned int start_layer_nr + = (mesh.settings.get("adhesion_type") == EPlatformAdhesion::BRIM) ? 1 : 0; // don't make fuzzy skin on first layer if there's a brim auto hole_area = Polygons(); - std::function accumulate_is_in_hole = [](const bool& prev_result, const ExtrusionJunction& junction) { return false; }; + std::function accumulate_is_in_hole = [](const bool& prev_result, const ExtrusionJunction& junction) + { + return false; + }; for (unsigned int layer_nr = start_layer_nr; layer_nr < mesh.layers.size(); layer_nr++) { @@ -1071,7 +1100,10 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) if (apply_outside_only) { hole_area = part.print_outline.getOutsidePolygons().offset(-line_width); - accumulate_is_in_hole = [&hole_area](const bool& prev_result, const ExtrusionJunction& junction) { return prev_result || hole_area.inside(junction.p); }; + accumulate_is_in_hole = [&hole_area](const bool& prev_result, const ExtrusionJunction& junction) + { + return prev_result || hole_area.inside(junction.p); + }; } for (auto& line : toolpath) { @@ -1087,7 +1119,8 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) result.is_closed = line.is_closed; // generate points in between p0 and p1 - int64_t dist_left_over = (min_dist_between_points / 4) + rand() % (min_dist_between_points / 4); // the distance to be traversed on the line before making the first new point + int64_t dist_left_over + = (min_dist_between_points / 4) + rand() % (min_dist_between_points / 4); // the distance to be traversed on the line before making the first new point auto* p0 = &line.front(); for (auto& p1 : line) { diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index 35709cfc79..9ed9cc7ede 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -1,28 +1,29 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include "infill.h" -#include "LayerPlan.h" -#include "sliceDataStorage.h" #include "TopSurface.h" + #include "ExtruderTrain.h" +#include "LayerPlan.h" +#include "infill.h" +#include "sliceDataStorage.h" namespace cura { TopSurface::TopSurface() { - //Do nothing. Areas stays empty. + // Do nothing. Areas stays empty. } void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t layer_number) { - //The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first. + // The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first. Polygons mesh_above; if (layer_number < mesh.layers.size() - 1) { mesh_above = mesh.layers[layer_number + 1].getOutlines(); - } //If this is the top-most layer, mesh_above stays empty. + } // If this is the top-most layer, mesh_above stays empty. if (mesh.settings.get("magic_spiralize")) { @@ -39,13 +40,14 @@ void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t l } } -bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer, const FffGcodeWriter& gcode_writer) const +bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer, const FffGcodeWriter& gcode_writer) + const { if (areas.empty()) { - return false; //Nothing to do. + return false; // Nothing to do. } - //Generate the lines to cover the surface. + // Generate the lines to cover the surface. const int extruder_nr = mesh.settings.get("top_bottom_extruder_nr").extruder_nr; const EFillMethod pattern = mesh.settings.get("ironing_pattern"); const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG; @@ -55,7 +57,7 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); const std::vector& top_most_skin_angles = (roofing_layer_count > 0) ? mesh.roofing_angles : mesh.skin_angles; assert(top_most_skin_angles.size() > 0); - const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); //Always perpendicular to the skin lines. + const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); // Always perpendicular to the skin lines. constexpr coord_t infill_overlap = 0; constexpr int infill_multiplier = 1; constexpr coord_t shift = 0; @@ -71,52 +73,70 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage coord_t ironing_inset = -mesh.settings.get("ironing_inset"); if (pattern == EFillMethod::ZIG_ZAG) { - //Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern + // Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern const Ratio width_scale = (float)mesh.settings.get("layer_height") / mesh.settings.get("infill_sparse_thickness"); ironing_inset += width_scale * line_width / 2; - //Align the edge of the ironing line with the edge of the outer wall + // Align the edge of the ironing line with the edge of the outer wall ironing_inset -= ironing_flow * line_width / 2; } else if (pattern == EFillMethod::CONCENTRIC) { - //Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern + // Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern ironing_inset += line_spacing - line_width / 2; - //Align the edge of the ironing line with the edge of the outer wall + // Align the edge of the ironing line with the edge of the outer wall ironing_inset -= ironing_flow * line_width / 2; } 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, small_area_width, 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 - 10, + shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_line_stitching); std::vector ironing_paths; Polygons ironing_polygons; Polygons ironing_lines; infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, mesh.settings, layer.getLayerNr(), SectionType::IRONING); - if(ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty()) + if (ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty()) { - return false; //Nothing to do. + return false; // Nothing to do. } layer.mode_skip_agressive_merge = true; bool added = false; - if(!ironing_polygons.empty()) + if (! ironing_polygons.empty()) { constexpr bool force_comb_retract = false; layer.addTravel(ironing_polygons[0][0], force_comb_retract); layer.addPolygonsByOptimizer(ironing_polygons, line_config, ZSeamConfig()); added = true; } - if(!ironing_lines.empty()) + if (! ironing_lines.empty()) { if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG) { - //Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams. + // Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams. const AABB bounding_box(ironed_areas); PointMatrix rotate(-direction + 90); const Point center = bounding_box.getMiddle(); - const Point far_away = rotate.apply(Point(0, vSize(bounding_box.max - center) * 100)); //Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre. - //Two options to start, both perpendicular to the ironing lines. Which is closer? + const Point far_away = rotate.apply( + Point(0, vSize(bounding_box.max - center) * 100)); // Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre. + // Two options to start, both perpendicular to the ironing lines. Which is closer? const Point front_side = PolygonUtils::findNearestVert(center + far_away, ironed_areas).p(); const Point back_side = PolygonUtils::findNearestVert(center - far_away, ironed_areas).p(); if (vSize2(layer.getLastPlannedPositionOrStartingPosition() - front_side) < vSize2(layer.getLastPlannedPositionOrStartingPosition() - back_side)) @@ -129,25 +149,40 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage } } - if( ! enforce_monotonic_order) + if (! enforce_monotonic_order) { layer.addLinesByOptimizer(ironing_lines, line_config, SpaceFillType::PolyLines); } else { - const coord_t max_adjacent_distance = line_spacing * 1.1; //Lines are considered adjacent - meaning they need to be printed in monotonic order - if spaced 1 line apart, with 10% extra play. + const coord_t max_adjacent_distance + = line_spacing * 1.1; // Lines are considered adjacent - meaning they need to be printed in monotonic order - if spaced 1 line apart, with 10% extra play. layer.addLinesMonotonic(Polygons(), ironing_lines, line_config, SpaceFillType::PolyLines, AngleRadians(direction), max_adjacent_distance); } added = true; } - if(!ironing_paths.empty()) + if (! ironing_paths.empty()) { constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0u; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); - InsetOrderOptimizer wall_orderer(gcode_writer, storage, layer, mesh.settings, extruder_nr, - line_config, line_config, line_config, line_config, - retract_before_outer_wall, wipe_dist, wipe_dist, extruder_nr, extruder_nr, z_seam_config, ironing_paths); + InsetOrderOptimizer wall_orderer( + gcode_writer, + storage, + layer, + mesh.settings, + extruder_nr, + line_config, + line_config, + line_config, + line_config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + extruder_nr, + extruder_nr, + z_seam_config, + ironing_paths); wall_orderer.addToLayer(); added = true; } @@ -156,4 +191,4 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage return added; } -} +} // namespace cura From 8677b106c5daff638bfdfb6c01290ef00f4b39ca Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 8 Aug 2023 19:48:55 +0200 Subject: [PATCH 329/656] Fix include-issue exposed by auto-formatter. done as part of CURA-10829 --- include/TopSurface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/TopSurface.h b/include/TopSurface.h index dcc5e5b9c2..62fd7539bb 100644 --- a/include/TopSurface.h +++ b/include/TopSurface.h @@ -12,6 +12,7 @@ namespace cura class GCodePathConfig; class FffGcodeWriter; class LayerPlan; +class SliceDataStorage; class SliceMeshStorage; class TopSurface From 6c375d0ab3dd8e19f9fea2608d06e6b7d468b3d2 Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 8 Aug 2023 17:49:39 +0000 Subject: [PATCH 330/656] Applied clang-format. --- include/TopSurface.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/TopSurface.h b/include/TopSurface.h index 62fd7539bb..49dba4e203 100644 --- a/include/TopSurface.h +++ b/include/TopSurface.h @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef TOPSURFACE_H #define TOPSURFACE_H @@ -60,6 +60,6 @@ class TopSurface Polygons areas; }; -} +} // namespace cura #endif /* TOPSURFACE_H */ From 51707468c25f11c594d54b5e2e8bbfaeb391f175 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 9 Aug 2023 00:42:38 +0200 Subject: [PATCH 331/656] Refactor numeric types for better type safety and reduced code repetition Numeric data types in the codebase are now defined as a numeric facade, providing greater type safety and reducing code repetition. This encompasses the `Acceleration`, `Velocity`, `Ratio`, and `LayerIndex` types. This change updates the constructor and assignment operators, and ensures the facade can be used in a consistent manner with standard arithmetic types. It also supports user-defined type conversions to provide interoperability with built-in types. In addition, correct type suffixes are added to numeric literals in a number of places to avoid potential bugs due to unexpected promotions or type coercions. As part of this refactor, we also standardized the category of numeric types applied throughout the codebase, ensuring compatibility with these new types. This refactor is needed to prep the GCodePaths/GCodePathConfigs for the plugin system. Since the changes for this refactor all over the placed, decided to make a seperate branch against main. Contributes to CURA-10466 --- include/gcodeExport.h | 8 +- include/settings/types/LayerIndex.h | 121 ++----------- include/settings/types/Ratio.h | 105 ++--------- include/settings/types/Velocity.h | 84 ++------- include/timeEstimate.h | 12 +- include/utils/types/generic.h | 5 + include/utils/types/numeric_facade.h | 212 +++++++++++++++++++++++ src/LayerPlan.cpp | 6 +- src/SkeletalTrapezoidation.cpp | 2 +- src/TreeModelVolumes.cpp | 8 +- src/TreeSupportTipGenerator.cpp | 5 +- src/communication/ArcusCommunication.cpp | 4 +- src/gcodeExport.cpp | 12 +- src/infill/SubDivCube.cpp | 6 +- src/settings/Settings.cpp | 8 +- src/skin.cpp | 2 +- src/support.cpp | 18 +- src/timeEstimate.cpp | 21 ++- 18 files changed, 330 insertions(+), 309 deletions(-) create mode 100644 include/utils/types/numeric_facade.h diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 207f9df46c..3e660ffe93 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -1,5 +1,5 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef GCODEEXPORT_H #define GCODEEXPORT_H @@ -462,14 +462,14 @@ class GCodeExport : public NoCopy * \param hop_height The height to move above the current layer. * \param speed The speed used for moving. */ - void writeZhopStart(const coord_t hop_height, Velocity speed = 0); + void writeZhopStart(const coord_t hop_height, Velocity speed = 0.0); /*! * End a z hop: go back to the layer height * * \param speed The speed used for moving. */ - void writeZhopEnd(Velocity speed = 0); + void writeZhopEnd(Velocity speed = 0.0); /*! * Start the new_extruder: diff --git a/include/settings/types/LayerIndex.h b/include/settings/types/LayerIndex.h index 1e48bbcb68..4588642cd5 100644 --- a/include/settings/types/LayerIndex.h +++ b/include/settings/types/LayerIndex.h @@ -1,9 +1,11 @@ -//Copyright (c) 2019 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef LAYERINDEX_H #define LAYERINDEX_H +#include "utils/types/numeric_facade.h" + #include namespace cura @@ -15,112 +17,27 @@ namespace cura * This is a facade. It behaves exactly like an integer but is used to indicate * that it is a layer number. */ -struct LayerIndex +struct LayerIndex : public utils::NumericFacade { - /* - * \brief Default constructor setting the layer index to 0. - */ - constexpr LayerIndex() : value(0) {}; - - /* - * \brief Casts an integer to a LayerIndex instance. - */ - constexpr LayerIndex(int value) : value(value) {}; - - /* - * \brief Casts the LayerIndex instance to an integer. - */ - constexpr operator int() const - { - return value; - } - - /* - * Some operators to add and subtract layer numbers. - */ - LayerIndex operator +(const LayerIndex& other) const - { - return LayerIndex(value + other.value); - } - template LayerIndex operator +(const E& other) const - { - return LayerIndex(value + other); - } + using base_type = utils::NumericFacade; + using base_type::NumericFacade; - LayerIndex operator -(const LayerIndex& other) const - { - return LayerIndex(value - other.value); - } - template LayerIndex operator -(const E& other) const - { - return LayerIndex(value - other); - } - - LayerIndex& operator +=(const LayerIndex& other) - { - value += other.value; - return *this; - } - template LayerIndex& operator +=(const E& other) - { - value += other; - return *this; - } - - LayerIndex& operator -=(const LayerIndex& other) - { - value -= other.value; - return *this; - } - template LayerIndex& operator -=(const E& other) - { - value -= other; - return *this; - } - - LayerIndex& operator ++() - { - value++; - return *this; - } - LayerIndex operator ++(int) //Postfix. - { - LayerIndex original_value(value); - operator++(); //Increment myself. - return original_value; - } - LayerIndex& operator --() - { - value--; - return *this; - } - LayerIndex operator --(int) //Postfix. - { - LayerIndex original_value(value); - operator--(); //Decrement myself. - return original_value; - } - - /* - * \brief The actual layer index. - * - * Note that this could be negative for raft layers. - */ - int value = 0; + constexpr LayerIndex(const base_type& base) noexcept + : base_type{ base } {}; }; -} +} // namespace cura namespace std { - template<> - struct hash +template<> +struct hash +{ + auto operator()(const cura::LayerIndex& layer_index) const { - size_t operator()(const cura::LayerIndex& layer_index) const - { - return hash()(layer_index.value); - } - }; -} + return hash()(layer_index.value); + } +}; +} // namespace std -#endif //LAYERINDEX_H +#endif // LAYERINDEX_H diff --git a/include/settings/types/Ratio.h b/include/settings/types/Ratio.h index a20cd2e2d6..76871ff301 100644 --- a/include/settings/types/Ratio.h +++ b/include/settings/types/Ratio.h @@ -1,9 +1,12 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef RATIO_H #define RATIO_H +#include "utils/types/numeric_facade.h" + + namespace cura { @@ -12,104 +15,28 @@ namespace cura * * This is a facade. It behaves like a double. */ -class Ratio +struct Ratio : public utils::NumericFacade { -public: - /* - * \brief Default constructor setting the ratio to 1. - */ - constexpr Ratio() : value(1.0) {}; + using base_type = utils::NumericFacade; + using base_type::NumericFacade; - /* - * \brief Casts a double to a Ratio instance. - */ - constexpr Ratio(double value) : value(value) {}; + constexpr Ratio(const base_type& base) noexcept + : base_type{ base } {}; /*! * Create the Ratio with a numerator and a divisor from arbitrary types - * \tparam E1 required to be castable to a double - * \tparam E2 required to be castable to a double * \param numerator the numerator of the ratio * \param divisor the divisor of the ratio */ - template - constexpr Ratio(const E1& numerator, const E2& divisor) - : value(static_cast(numerator) / static_cast(divisor)) {}; - - /* - * \brief Casts the Ratio instance to a double. - */ - operator double() const - { - return value; - } - - /* - * Some Relational operators - */ - template - constexpr bool operator==(const E& rhs) const - { - return value == static_cast(rhs); - } - - template - constexpr bool operator!=(const E& rhs) const - { - return !(rhs == *this); - } - - /* - * Some operators for arithmetic on ratios. - */ - Ratio operator *(const Ratio& other) const - { - return Ratio(value * other.value); - } - template Ratio operator *(const E& other) const - { - return Ratio(value * other); - } - Ratio operator /(const Ratio& other) const - { - return Ratio(value / other.value); - } - template Ratio operator /(const E& other) const - { - return Ratio(value / other); - } - Ratio& operator *=(const Ratio& other) - { - value *= other.value; - return *this; - } - template Ratio& operator *=(const E& other) - { - value *= other; - return *this; - } - Ratio& operator /=(const Ratio& other) - { - value /= other.value; - return *this; - } - template Ratio& operator /=(const E& other) - { - value /= other; - return *this; - } - - /* - * \brief The actual ratio, as a double. - */ - double value = 0; + constexpr Ratio(const utils::numeric auto numerator, const utils::numeric auto divisor) + : base_type{ static_cast(numerator) / static_cast(divisor) } {}; }; -constexpr Ratio operator "" _r(const long double ratio) +constexpr Ratio operator"" _r(const long double ratio) { - return Ratio(ratio); + return { ratio }; } -} +} // namespace cura -#endif //RATIO_H +#endif // RATIO_H diff --git a/include/settings/types/Velocity.h b/include/settings/types/Velocity.h index 0a56c077d4..f897c13c97 100644 --- a/include/settings/types/Velocity.h +++ b/include/settings/types/Velocity.h @@ -1,9 +1,11 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef VELOCITY_H #define VELOCITY_H +#include "utils/types/numeric_facade.h" + namespace cura { @@ -12,74 +14,24 @@ namespace cura * * This is a facade. It behaves like a double, only it can't be negative. */ -struct Velocity +struct Velocity : public utils::NumericFacade { - /* - * \brief Default constructor setting velocity to 0. - */ - constexpr Velocity() : value(0.0) {}; - - /* - * \brief Casts a double to a Velocity instance. - */ - constexpr Velocity(double value) : value(value) {}; + using base_type = utils::NumericFacade; + using base_type::NumericFacade; - /* - * \brief Casts the Temperature instance to a double. - */ - constexpr operator double() const - { - return value; - } + constexpr Velocity(const base_type& base) noexcept + : base_type{ base } {}; +}; - /* - * Some operators for arithmetic on velocities. - */ - Velocity operator *(const Velocity& other) const - { - return Velocity(value * other.value); - } - template Velocity operator *(const E& other) const - { - return Velocity(value * other); - } - Velocity operator /(const Velocity& other) const - { - return Velocity(value / other.value); - } - template Velocity operator /(const E& other) const - { - return Velocity(value / other); - } - Velocity& operator *=(const Velocity& other) - { - value *= other.value; - return *this; - } - template Velocity& operator *=(const E& other) - { - value *= other; - return *this; - } - Velocity& operator /=(const Velocity& other) - { - value /= other.value; - return *this; - } - template Velocity& operator /=(const E& other) - { - value /= other; - return *this; - } +struct Acceleration : public utils::NumericFacade +{ + using base_type = utils::NumericFacade; + using base_type::NumericFacade; - /* - * \brief The actual temperature, as a double. - */ - double value = 0; + constexpr Acceleration(const base_type& base) noexcept + : base_type{ base } {}; }; -using Acceleration = Velocity; //Use the same logic for acceleration variables. - -} +} // namespace cura -#endif //VELOCITY_H \ No newline at end of file +#endif // VELOCITY_H \ No newline at end of file diff --git a/include/timeEstimate.h b/include/timeEstimate.h index ea80d9a6f9..a2ec403bd1 100644 --- a/include/timeEstimate.h +++ b/include/timeEstimate.h @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef TIME_ESTIMATE_H #define TIME_ESTIMATE_H @@ -11,11 +11,11 @@ #include "PrintFeature.h" #include "settings/types/Duration.h" //Print time estimates. #include "settings/types/Velocity.h" //Speeds and accelerations at which we print. +#include "settings/types/Ratio.h" namespace cura { -class Ratio; class Settings; /*! @@ -68,10 +68,10 @@ class TimeEstimateCalculator }; private: - Velocity max_feedrate[NUM_AXIS] = {600, 600, 40, 25}; // mm/s + Velocity max_feedrate[NUM_AXIS] = {600.0, 600.0, 40.0, 25.0}; // mm/s Velocity minimumfeedrate = 0.01; - Acceleration acceleration = 3000; - Acceleration max_acceleration[NUM_AXIS] = {9000, 9000, 100, 10000}; + Acceleration acceleration = 3000.0; + Acceleration max_acceleration[NUM_AXIS] = {9000.0, 9000.0, 100.0, 10000.0}; Velocity max_xy_jerk = 20.0; Velocity max_z_jerk = 0.4; Velocity max_e_jerk = 5.0; diff --git a/include/utils/types/generic.h b/include/utils/types/generic.h index 751a47342d..78dc919ad0 100644 --- a/include/utils/types/generic.h +++ b/include/utils/types/generic.h @@ -4,6 +4,8 @@ #ifndef CURAENGINE_GENERIC_H #define CURAENGINE_GENERIC_H +#include + #include #include #include @@ -53,6 +55,9 @@ template concept floating_point = std::floating_point; #endif // clang-format on + +template +concept numeric = std::is_arithmetic_v>; } // namespace cura::utils #endif // CURAENGINE_GENERIC_H diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h new file mode 100644 index 0000000000..7b0b22560e --- /dev/null +++ b/include/utils/types/numeric_facade.h @@ -0,0 +1,212 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_TYPES_ARITHMITIC_FACADE_H +#define UTILS_TYPES_ARITHMITIC_FACADE_H + +#include "utils/types/generic.h" + +namespace cura::utils +{ + +template +struct NumericFacade +{ + using value_type = T; + using difference_type = std::ptrdiff_t; + + value_type value{}; + + constexpr NumericFacade() noexcept = default; + + constexpr NumericFacade(const NumericFacade& other) noexcept = default; + constexpr NumericFacade(NumericFacade&& other) noexcept = default; + + constexpr NumericFacade(const floating_point auto value) noexcept + requires floating_point + : value{ static_cast(value) } {}; + + constexpr NumericFacade(const integral auto value) noexcept + requires integral + : value{ static_cast(value) } {}; + + constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; + + constexpr NumericFacade& operator=(const floating_point auto& other) noexcept + requires floating_point + { + this->value = static_cast(other); + return *this; + } + constexpr NumericFacade& operator=(const integral auto& other) noexcept + requires integral + { + this->value = static_cast(other); + return *this; + } + + constexpr NumericFacade& operator=(NumericFacade&& other) noexcept = default; + constexpr NumericFacade& operator=(const integral auto&& other) noexcept + requires integral + { + this->value = static_cast(other); + return *this; + } + constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept + requires floating_point + { + this->value = static_cast(other); + return *this; + } + + ~NumericFacade() noexcept = default; + + constexpr operator value_type() const noexcept + { + return value; + } + + constexpr bool operator==(const NumericFacade& other) const noexcept + { + return value == other.value; + } + + constexpr bool operator==(const numeric auto& other) const noexcept + { + return value == static_cast(other); + } + + constexpr auto operator<=>(const NumericFacade& other) const noexcept = default; + constexpr auto operator<=>(const numeric auto& other) const noexcept + { + return value <=> static_cast(other); + }; + + constexpr NumericFacade& operator+=(const NumericFacade& other) noexcept + { + value += other.value; + return *this; + } + + constexpr NumericFacade& operator+=(const numeric auto& other) noexcept + { + value += static_cast(other); + return *this; + } + + constexpr NumericFacade& operator-=(const NumericFacade& other) noexcept + { + value -= other.value; + return *this; + } + + constexpr NumericFacade& operator-=(const numeric auto& other) noexcept + { + value -= static_cast(other); + return *this; + } + + constexpr NumericFacade& operator*=(const NumericFacade& other) noexcept + { + value *= other.value; + return *this; + } + + constexpr NumericFacade& operator*=(const numeric auto& other) noexcept + { + value *= static_cast(other); + return *this; + } + + constexpr NumericFacade& operator/=(const NumericFacade& other) + { + value /= other.value; + return *this; + } + + constexpr NumericFacade& operator/=(const numeric auto& other) + { + value /= static_cast(other); + return *this; + } + + constexpr NumericFacade operator+(const NumericFacade& other) const noexcept + { + return { value + other.value }; + } + + constexpr NumericFacade operator+(NumericFacade&& other) const noexcept + { + return { value + other.value }; + } + + constexpr NumericFacade operator+(const numeric auto& other) const noexcept + { + return { value + static_cast(other) }; + } + + constexpr NumericFacade operator-(const NumericFacade& other) const noexcept + { + return { value - other.value }; + } + + constexpr NumericFacade operator-(const numeric auto& other) const noexcept + { + return { value - static_cast(other) }; + } + + constexpr NumericFacade operator*(const NumericFacade& other) const noexcept + { + return { value * other.value }; + } + + constexpr NumericFacade operator*(const numeric auto& other) const noexcept + { + return { value * static_cast(other) }; + } + + constexpr NumericFacade operator/(const NumericFacade& other) const + { + return { value / other.value }; + } + + constexpr NumericFacade operator/(const numeric auto& other) const + { + return { value / static_cast(other) }; + } + + constexpr NumericFacade operator-() const noexcept + { + return { -value }; + } + + constexpr NumericFacade& operator++() noexcept + requires integral + { + ++value; + return *this; + } + + constexpr NumericFacade operator++(int) noexcept + requires integral + { + return { value++ }; + } + + constexpr NumericFacade& operator--() noexcept + requires integral + { + --value; + return *this; + } + + constexpr NumericFacade operator--(int) noexcept + requires integral + { + return { value-- }; + } +}; + +} // namespace cura::utils + +#endif // UTILS_TYPES_ARITHMITIC_FACADE_H diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 5c60180631..cafa6a1f65 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -750,7 +750,7 @@ void LayerPlan::addWallLine(const Point& p0, speed_factor = 1 - (1 - speed_factor) * acceleration_factor; if (speed_factor >= 0.9) { - speed_factor = 1; + speed_factor = 1.0; } distance_to_line_end = vSize(cur_point - line_end); } @@ -899,7 +899,7 @@ void LayerPlan::addWall(const ExtrusionLine& wall, const coord_t min_bridge_line_len = settings.get("bridge_wall_min_length"); - const Ratio nominal_line_width_multiplier = 1.0 / Ratio(non_bridge_config.getLineWidth()); // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this + const Ratio nominal_line_width_multiplier { 1.0 / Ratio { static_cast(non_bridge_config.getLineWidth()) } }; // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this // helper function to calculate the distance from the start of the current wall line to the first bridge segment @@ -1109,7 +1109,7 @@ void LayerPlan::addInfillWall(const ExtrusionLine& wall, const GCodePathConfig& for (const auto& junction_n : wall) { - const Ratio width_factor = junction_n.w / Ratio(path_config.getLineWidth()); + const Ratio width_factor { static_cast(junction_n.w) / Ratio { static_cast(path_config.getLineWidth()) } }; constexpr SpaceFillType space_fill_type = SpaceFillType::Polygons; constexpr Ratio flow = 1.0_r; addExtrusionMove(junction_n.p, path_config, space_fill_type, flow, width_factor); diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index ed32823839..04dd141bf1 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -1074,7 +1074,7 @@ void SkeletalTrapezoidation::generateTransitionEnds(edge_t& edge, coord_t mid_po const float transition_mid_position = beading_strategy.getTransitionAnchorPos(lower_bead_count); constexpr float inner_bead_width_ratio_after_transition = 1.0; - constexpr coord_t start_rest = 0; + constexpr Ratio start_rest { 0.0 }; const float mid_rest = transition_mid_position * inner_bead_width_ratio_after_transition; constexpr float end_rest = inner_bead_width_ratio_after_transition; diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index f7ab838ab0..9fb625cc3f 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2021 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "TreeModelVolumes.h" #include "TreeSupport.h" @@ -801,7 +801,7 @@ void TreeModelVolumes::calculateAccumulatedPlaceable0(const LayerIndex max_layer { start_layer++; } - start_layer = std::max(start_layer.value + 1, 1); + start_layer = std::max(LayerIndex { start_layer + 1 }, LayerIndex { 1 }); } if (start_layer > max_layer) { @@ -823,7 +823,7 @@ void TreeModelVolumes::calculateAccumulatedPlaceable0(const LayerIndex max_layer } cura::parallel_for ( - std::max(start_layer-1,LayerIndex(1)), + std::max(LayerIndex{ start_layer - 1 }, LayerIndex{ 1 }), data.size(), [&](const coord_t layer_idx) { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 7eb6958e34..5dd76000f3 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + #include "TreeSupportTipGenerator.h" #include "Application.h" //To get settings. #include "infill/SierpinskiFillProvider.h" @@ -518,7 +521,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof further up/down. // This prevents the situation where a roof gets removed even tough its area would contribute to a (better) printable roof area further down. - for (const LayerIndex layer_offset : ranges::views::iota(-LayerIndex(std::min(size_t(layer_idx),support_roof_layers)), LayerIndex(std::min(size_t(potential_support_roofs.size()-layer_idx),support_roof_layers+1)))) + for (const LayerIndex layer_offset : ranges::views::iota(-LayerIndex { std::min(layer_idx, LayerIndex { support_roof_layers }) }, LayerIndex { std::min(LayerIndex { potential_support_roofs.size() - layer_idx }, LayerIndex{ support_roof_layers + 1} ) })) { fuzzy_area.add(support_roof_drawn[layer_idx+layer_offset]); fuzzy_area.add(potential_support_roofs[layer_idx+layer_offset]); diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 30786e2cce..6d06e29751 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #ifdef ARCUS @@ -133,7 +133,7 @@ class ArcusCommunication::PathCompiler } else if (initial_point != last_point) { - addLineSegment(PrintFeatureType::NoneType, initial_point, 1, 0, 0); + addLineSegment(PrintFeatureType::NoneType, initial_point, 1, 0, 0.0); } } diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 82978bdbbf..b4f800db6e 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include @@ -45,10 +45,10 @@ GCodeExport::GCodeExport() : output_stream(&std::cout), currentPosition(0, 0, MM total_print_times = std::vector(static_cast(PrintFeatureType::NumPrintFeatureTypes), 0.0); - currentSpeed = 1; - current_print_acceleration = -1; - current_travel_acceleration = -1; - current_jerk = -1; + currentSpeed = 1.0; + current_print_acceleration = -1.0; + current_travel_acceleration = -1.0; + current_jerk = -1.0; is_z_hopped = 0; setFlavor(EGCodeFlavor::MARLIN); @@ -1122,7 +1122,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)), - 25, + 25.0, PrintFeatureType::MoveRetraction); // TODO: hardcoded values! } else diff --git a/src/infill/SubDivCube.cpp b/src/infill/SubDivCube.cpp index 20e4a3f0e5..890fed2865 100644 --- a/src/infill/SubDivCube.cpp +++ b/src/infill/SubDivCube.cpp @@ -1,5 +1,5 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "infill/SubDivCube.h" @@ -201,7 +201,7 @@ bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, coor int top_layer = (center.z + radius) / layer_height; for (int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain. { - part_dist = static_cast(test_layer * layer_height - center.z) / radius; + part_dist = Ratio { static_cast(test_layer * layer_height - center.z) } / radius; sphere_slice_radius2 = radius * radius * (1.0 - (part_dist * part_dist)); Point loc(center.x, center.y); diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 56ddaa26ef..2b6bcbea5a 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include @@ -166,6 +166,12 @@ Velocity Settings::get(const std::string& key) const return get(key); } +template<> +Acceleration Settings::get(const std::string& key) const +{ + return get(key); +} + template<> Ratio Settings::get(const std::string& key) const { diff --git a/src/skin.cpp b/src/skin.cpp index c7bb0c736f..1c0e8da708 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -175,7 +175,7 @@ void SkinInfillAreaComputation::calculateBottomSkin(const SliceLayerPart& part, { return; // don't subtract anything form the downskin } - LayerIndex bottom_check_start_layer_idx = std::max(LayerIndex(0), layer_nr - bottom_layer_count); + LayerIndex bottom_check_start_layer_idx { std::max(LayerIndex { 0 }, LayerIndex { layer_nr - bottom_layer_count }) }; Polygons not_air = getOutlineOnLayer(part, bottom_check_start_layer_idx); if (!no_small_gaps_heuristic) { diff --git a/src/support.cpp b/src/support.cpp index 2fc782c93c..77b53d61d1 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -192,18 +192,18 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) const coord_t wall_width = infill_extruder.settings.get("support_line_width"); // no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density] - float layer_skip_count = 8; // skip every so many layers as to ignore small gaps in the model making computation more easy + double layer_skip_count { 8.0 }; // skip every so many layers as to ignore small gaps in the model making computation more easy size_t gradual_support_step_layer_count = round_divide(gradual_support_step_height, mesh_group_settings.get("layer_height")); // The difference in layer count between consecutive density infill areas. // make gradual_support_step_height divisable by layer_skip_count - const float n_skip_steps_per_gradual_step = std::max(1.0f, std::ceil(gradual_support_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_support_step_layer_count + const auto n_skip_steps_per_gradual_step = std::max(1.0, std::ceil(gradual_support_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_support_step_layer_count layer_skip_count = gradual_support_step_layer_count / n_skip_steps_per_gradual_step; LayerIndex min_layer = 0; LayerIndex max_layer = total_layer_count - 1; // compute different density areas for each support island - for (LayerIndex layer_nr = 0; layer_nr < static_cast(total_layer_count) - 1; layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < total_layer_count - 1; layer_nr++) { if (layer_nr < min_layer || layer_nr > max_layer) { @@ -229,8 +229,8 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) Polygons less_dense_support = infill_area; // one step less dense with each density_step for (unsigned int density_step = 0; density_step < max_density_steps; ++density_step) { - LayerIndex min_layer = layer_nr + density_step * gradual_support_step_layer_count + LayerIndex(layer_skip_count); - LayerIndex max_layer = layer_nr + (density_step + 1) * gradual_support_step_layer_count; + LayerIndex min_layer { layer_nr + density_step * gradual_support_step_layer_count + static_cast(layer_skip_count) }; + LayerIndex max_layer { layer_nr + (density_step + 1) * gradual_support_step_layer_count }; for (float upper_layer_idx = min_layer; upper_layer_idx <= max_layer; upper_layer_idx += layer_skip_count) { @@ -831,7 +831,7 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st constexpr LayerIndex layer_index_offset { 1 }; - const LayerIndex layer_idx_below { std::max(layer_idx - layer_index_offset, LayerIndex { 0 }) }; + const LayerIndex layer_idx_below { std::max(LayerIndex{ layer_idx - layer_index_offset }, LayerIndex { 0 }) }; if (layer_idx_below != layer_idx) { auto layer_below = simplify.polygon(storage.layers[layer_idx_below].getOutlines() @@ -851,7 +851,7 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st ); } - const LayerIndex layer_idx_above { std::min(layer_idx + layer_index_offset, LayerIndex(static_cast(storage.layers.size()) - 1)) }; + const LayerIndex layer_idx_above { std::min(LayerIndex{ layer_idx + layer_index_offset }, LayerIndex { storage.layers.size() - 1 }) }; if (layer_idx_above != layer_idx) { auto layer_above = simplify.polygon(storage.layers[layer_idx_below].getOutlines() @@ -1487,7 +1487,7 @@ std::pair AreaSupport::computeBasicAndFullOverhang(const Sli constexpr bool no_prime_tower = false; constexpr double smooth_height = 0.4; //mm - const auto layers_below = static_cast(std::round(smooth_height / mesh.settings.get("layer_height"))); + const LayerIndex layers_below { static_cast(std::round(smooth_height / mesh.settings.get("layer_height"))) }; const coord_t layer_height = mesh.settings.get("layer_height"); const AngleRadians support_angle = mesh.settings.get("support_angle"); @@ -1766,7 +1766,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh std::vector& support_layers = storage.support.supportLayers; for (LayerIndex layer_idx = 0; layer_idx < static_cast(support_layers.size() - z_distance_top); layer_idx++) { - const LayerIndex top_layer_idx_above = std::min(static_cast(support_layers.size() - 1), layer_idx + roof_layer_count + z_distance_top); // Maximum layer of the model that generates support roof. + const LayerIndex top_layer_idx_above { std::min(LayerIndex { support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) }; // Maximum layer of the model that generates support roof. Polygons mesh_outlines; for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip) { diff --git a/src/timeEstimate.cpp b/src/timeEstimate.cpp index 3a535dfc13..4066ff8329 100644 --- a/src/timeEstimate.cpp +++ b/src/timeEstimate.cpp @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include #include @@ -9,7 +9,6 @@ #include "timeEstimate.h" #include "utils/math.h" #include "settings/Settings.h" -#include "settings/types/Ratio.h" namespace cura { @@ -44,7 +43,7 @@ void TimeEstimateCalculator::addTime(const Duration& time) extra_time += time; } -void TimeEstimateCalculator::setAcceleration(const Velocity& acc) +void TimeEstimateCalculator::setAcceleration(const Acceleration& acc) { acceleration = acc; } @@ -219,15 +218,15 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu } } - Velocity vmax_junction = max_xy_jerk / 2; - Ratio vmax_junction_factor = 1.0; - if (current_abs_feedrate[Z_AXIS] > max_z_jerk / 2) + Velocity vmax_junction { max_xy_jerk / 2.0 }; + Ratio vmax_junction_factor { 1.0 }; + if (current_abs_feedrate[Z_AXIS] > max_z_jerk / 2.0) { - vmax_junction = std::min(vmax_junction, max_z_jerk / 2); + vmax_junction = std::min(vmax_junction, Velocity { max_z_jerk / 2.0 }); } - if (current_abs_feedrate[E_AXIS] > max_e_jerk / 2) + if (current_abs_feedrate[E_AXIS] > max_e_jerk / 2.0) { - vmax_junction = std::min(vmax_junction, max_e_jerk / 2); + vmax_junction = std::min(vmax_junction, Velocity{ max_e_jerk / 2.0 }); } vmax_junction = std::min(vmax_junction, block.nominal_feedrate); const Velocity safe_speed = vmax_junction; @@ -250,7 +249,7 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu { vmax_junction_factor = std::min(vmax_junction_factor, Ratio(max_e_jerk / e_jerk)); } - vmax_junction = std::min(previous_nominal_feedrate, vmax_junction * vmax_junction_factor); // Limit speed to max previous speed + vmax_junction = std::min(previous_nominal_feedrate, Velocity { vmax_junction * vmax_junction_factor }); // Limit speed to max previous speed } block.max_entry_speed = vmax_junction; From 1a32cc2970e7db8ac4b6a2af3276589773e87be7 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 8 Aug 2023 22:43:11 +0000 Subject: [PATCH 332/656] Applied clang-format. --- include/gcodeExport.h | 189 ++-- include/timeEstimate.h | 55 +- include/utils/types/numeric_facade.h | 32 +- src/LayerPlan.cpp | 527 ++++++----- src/SkeletalTrapezoidation.cpp | 411 ++++++--- src/TreeModelVolumes.cpp | 326 +++---- src/TreeSupportTipGenerator.cpp | 1055 +++++++++++++--------- src/communication/ArcusCommunication.cpp | 27 +- src/gcodeExport.cpp | 105 ++- src/infill/SubDivCube.cpp | 47 +- src/settings/Settings.cpp | 45 +- src/skin.cpp | 188 ++-- src/support.cpp | 481 +++++----- src/timeEstimate.cpp | 95 +- 14 files changed, 2098 insertions(+), 1485 deletions(-) diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 3e660ffe93..9ab087cd75 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -6,20 +6,20 @@ #include // for extrusionAmountAtPreviousRetractions #ifdef BUILD_TESTS - #include //To allow tests to use protected members. +#include //To allow tests to use protected members. #endif -#include // for stream.str() -#include - -#include "utils/AABB3D.h" //To track the used build volume for the Griffin header. -#include "timeEstimate.h" #include "settings/EnumSettings.h" #include "settings/Settings.h" //For MAX_EXTRUDERS. #include "settings/types/Temperature.h" //Bed temperature. #include "settings/types/Velocity.h" +#include "sliceDataStorage.h" +#include "timeEstimate.h" +#include "utils/AABB3D.h" //To track the used build volume for the Griffin header. #include "utils/IntPoint.h" #include "utils/NoCopy.h" -#include "sliceDataStorage.h" + +#include // for stream.str() +#include namespace cura { @@ -28,8 +28,8 @@ struct LayerIndex; class RetractionConfig; struct WipeScriptConfig; -//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels. -// Any customizations on GCodes flavors are done in this class. +// The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels. +// Any customizations on GCodes flavors are done in this class. class GCodeExport : public NoCopy { #ifdef BUILD_TESTS @@ -79,8 +79,10 @@ class GCodeExport : public NoCopy bool waited_for_temperature; //!< Whether the most recent temperature command has been a heat-and-wait command (M109) or not (M104). Temperature initial_temp; //!< Temperature this nozzle needs to be at the start of the print. - double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted amount, so negative impact on E values) - double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation where the filament is at the tip of the nozzle. + double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted + //!< amount, so negative impact on E values) + double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation + //!< where the filament is at the tip of the nozzle. double prime_volume; //!< Amount of material (in mm^3) to be primed after an unretration (due to oozing and/or coasting) Velocity last_retraction_prime_speed; //!< The last prime speed (in mm/s) of the to-be-primed amount @@ -92,16 +94,19 @@ class GCodeExport : public NoCopy std::deque extruded_volume_at_previous_n_retractions; // in mm^3 ExtruderTrainAttributes() - : is_primed(false) - , is_used(false) - , extruderCharacter(0) - , filament_area(0) - , totalFilament(0) - , currentTemperature(0) - , waited_for_temperature(false) - , initial_temp(0) - , retraction_e_amount_current(0.0), retraction_e_amount_at_e_start(0.0), - prime_volume(0.0), last_retraction_prime_speed(0.0), fan_number(0) + : is_primed(false) + , is_used(false) + , extruderCharacter(0) + , filament_area(0) + , totalFilament(0) + , currentTemperature(0) + , waited_for_temperature(false) + , initial_temp(0) + , retraction_e_amount_current(0.0) + , retraction_e_amount_at_e_start(0.0) + , prime_volume(0.0) + , last_retraction_prime_speed(0.0) + , fan_number(0) { } }; @@ -120,23 +125,27 @@ class GCodeExport : public NoCopy double max_extrusion_offset; //!< 0 to turn it off, normally 4 double extrusion_offset_factor; //!< default 1 - Point3 currentPosition; //!< The last build plate coordinates written to gcode (which might be different from actually written gcode coordinates when the extruder offset is encoded in the gcode) + Point3 currentPosition; //!< The last build plate coordinates written to gcode (which might be different from actually written gcode coordinates when the extruder offset is + //!< encoded in the gcode) Velocity currentSpeed; //!< The current speed (F values / 60) in mm/s - Acceleration current_print_acceleration; //!< The current acceleration (in mm/s^2) used for print moves (and also for travel moves if the gcode flavor doesn't have separate travel acceleration) - Acceleration current_travel_acceleration; //!< The current acceleration (in mm/s^2) used for travel moves for those gcode flavors that have separate print and travel accelerations + Acceleration current_print_acceleration; //!< The current acceleration (in mm/s^2) used for print moves (and also for travel moves if the gcode flavor doesn't have separate + //!< travel acceleration) + Acceleration + current_travel_acceleration; //!< The current acceleration (in mm/s^2) used for travel moves for those gcode flavors that have separate print and travel accelerations Velocity current_jerk; //!< The current jerk in the XY direction (in mm/s^3) AABB3D total_bounding_box; //!< The bounding box of all g-code. /*! * The z position to be used on the next xy move, if the head wasn't in the correct z position yet. - * + * * \see GCodeExport::writeExtrusion(Point, double, double) - * + * * \note After GCodeExport::writeExtrusion(Point, double, double) has been called currentPosition.z coincides with this value */ coord_t current_layer_z; - coord_t is_z_hopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with other layer parts) + coord_t is_z_hopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with + //!< other layer parts) size_t current_extruder; double current_fan_speed; @@ -154,15 +163,15 @@ class GCodeExport : public NoCopy Temperature initial_bed_temp; //!< bed temperature at the beginning of the print. Temperature bed_temperature; //!< Current build plate temperature. - Temperature build_volume_temperature; //!< build volume temperature - bool machine_heated_build_volume; //!< does the machine have the ability to control/stabilize build-volume-temperature + Temperature build_volume_temperature; //!< build volume temperature + bool machine_heated_build_volume; //!< does the machine have the ability to control/stabilize build-volume-temperature protected: /*! * Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder. - * + * * E values are either in mm or in mm^3 * The current extruder is used to determine the filament area to make the conversion. - * + * * \param e the value to convert * \return the value converted to mm */ @@ -170,10 +179,10 @@ class GCodeExport : public NoCopy /*! * Convert a volume value to an E value (which might be volumetric as well) for the current extruder. - * + * * E values are either in mm or in mm^3 * The current extruder is used to determine the filament area to make the conversion. - * + * * \param mm3 the value to convert * \return the value converted to mm or mm3 depending on whether the E axis is volumetric */ @@ -181,10 +190,10 @@ class GCodeExport : public NoCopy /*! * Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder. - * + * * E values are either in mm or in mm^3 * The current extruder is used to determine the filament area to make the conversion. - * + * * \param mm the value to convert * \return the value converted to mm or mm3 depending on whether the E axis is volumetric */ @@ -203,7 +212,6 @@ class GCodeExport : public NoCopy double eToMm3(double e, size_t extruder); public: - GCodeExport(); ~GCodeExport(); @@ -224,10 +232,11 @@ class GCodeExport : public NoCopy * \param mat_ids The material GUIDs for each material. * \return The string representing the file header */ - std::string getFileHeader(const std::vector& extruder_is_used, - const Duration* print_time = nullptr, - const std::vector& filament_used = std::vector(), - const std::vector& mat_ids = std::vector()); + std::string getFileHeader( + const std::vector& extruder_is_used, + const Duration* print_time = nullptr, + const std::vector& filament_used = std::vector(), + const std::vector& mat_ids = std::vector()); void setSliceUUID(const std::string& slice_uuid); @@ -241,7 +250,7 @@ class GCodeExport : public NoCopy void setFlavor(EGCodeFlavor flavor); EGCodeFlavor getFlavor() const; - + void setZ(int z); void setFlowRateExtrusionSettings(double max_extrusion_offset, double extrusion_offset_factor); @@ -252,24 +261,24 @@ class GCodeExport : public NoCopy * \param extra_prime_distance Amount of material in mm. */ void addExtraPrimeAmount(double extra_prime_volume); - + Point3 getPosition() const; - + Point getPositionXY() const; int getPositionZ() const; int getExtruderNr() const; - + void setFilamentDiameter(size_t extruder, const coord_t diameter); - + double getCurrentExtrudedVolume() const; /*! * Get the total extruded volume for a specific extruder in mm^3 - * + * * Retractions and unretractions don't contribute to this. - * + * * \param extruder_nr The extruder number for which to get the total netto extruded volume * \return total filament printed in mm^3 */ @@ -277,19 +286,19 @@ class GCodeExport : public NoCopy /*! * Get the total estimated print time in seconds for each feature - * + * * \return total print time in seconds for each feature */ std::vector getTotalPrintTimePerFeature(); /*! * Get the total print time in seconds for the complete print - * + * * \return total print time in seconds for the complete print */ double getSumTotalPrintTimes(); void updateTotalPrintTime(); void resetTotalPrintTimeAndFilament(); - + void writeComment(const std::string& comment); void writeTypeComment(const PrintFeatureType& type); @@ -304,7 +313,7 @@ class GCodeExport : public NoCopy /*! * Write a comment saying what (estimated) time has passed up to this point - * + * * \param time The time passed up till this point */ void writeTimeComment(const Duration time); @@ -318,21 +327,21 @@ class GCodeExport : public NoCopy * Write a comment saying that the print has a certain number of layers. */ void writeLayerCountComment(const size_t layer_count); - + void writeLine(const char* line); - + /*! * Reset the current_e_value to prevent too high E values. - * + * * The current extruded volume is added to the current extruder_attr. */ void resetExtrusionValue(); - + void writeDelay(const Duration& time_amount); /*! * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. - * + * * \param p location to go to * \param speed movement speed */ @@ -340,7 +349,7 @@ class GCodeExport : public NoCopy /*! * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. - * + * * \param p location to go to * \param speed movement speed * \param feature the feature that's currently printing @@ -351,7 +360,7 @@ class GCodeExport : public NoCopy /*! * Go to a X/Y location with the z-hopped Z value * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. - * + * * \param p location to go to * \param speed movement speed */ @@ -361,9 +370,9 @@ class GCodeExport : public NoCopy * Go to a X/Y location with the extrusion Z * Perform un-z-hop * Perform unretraction - * + * * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. - * + * * \param p location to go to * \param speed movement speed * \param feature the feature that's currently printing @@ -380,10 +389,10 @@ class GCodeExport : public NoCopy bool initializeExtruderTrains(const SliceDataStorage& storage, const size_t start_extruder_nr); /*! - * Set temperatures for the initial layer. Called by 'processStartingCode' and whenever a new object is started at layer 0. - * - * \param[in] storage where the slice data is stored. - * \param[in] start_extruder_nr The extruder with which to start the print. + * Set temperatures for the initial layer. Called by 'processStartingCode' and whenever a new object is started at layer 0. + * + * \param[in] storage where the slice data is stored. + * \param[in] start_extruder_nr The extruder with which to start the print. */ void processInitialLayerTemperature(const SliceDataStorage& storage, const size_t start_extruder_nr); @@ -398,7 +407,7 @@ class GCodeExport : public NoCopy private: /*! * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. - * + * * \param x build plate x * \param y build plate y * \param z build plate z @@ -411,7 +420,7 @@ class GCodeExport : public NoCopy * Perform unretract * Write extrusion move * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. - * + * * \param x build plate x * \param y build plate y * \param z build plate z @@ -420,13 +429,20 @@ class GCodeExport : public NoCopy * \param feature the print feature that's currently printing * \param update_extrusion_offset whether to update the extrusion offset to match the current flow rate */ - void writeExtrusion(const coord_t x, const coord_t y, const coord_t z, const Velocity& speed, const double extrusion_mm3_per_mm, const PrintFeatureType& feature, const bool update_extrusion_offset = false); + void writeExtrusion( + const coord_t x, + const coord_t y, + const coord_t z, + const Velocity& speed, + const double extrusion_mm3_per_mm, + const PrintFeatureType& feature, + const bool update_extrusion_offset = false); /*! * Write the F, X, Y, Z and E value (if they are not different from the last) - * + * * convenience function called from writeExtrusion and writeTravel - * + * * This function also applies the gcode offset by calling \ref GCodeExport::getGcodePos * This function updates the \ref GCodeExport::total_bounding_box * It estimates the time in \ref GCodeExport::estimateCalculator for the correct feature @@ -444,12 +460,13 @@ class GCodeExport : public NoCopy * \param feature print feature to track print time for */ void writeMoveBFB(const int x, const int y, const int z, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature); + public: /*! * Get ready for extrusion moves: * - unretract (G11 or G1 E.) * - prime blob (G1 E) - * + * * It estimates the time in \ref GCodeExport::estimateCalculator * It updates \ref GCodeExport::current_e_value and \ref GCodeExport::currentSpeed */ @@ -458,9 +475,9 @@ class GCodeExport : public NoCopy /*! * Start a z hop with the given \p hop_height. - * + * * \param hop_height The height to move above the current layer. - * \param speed The speed used for moving. + * \param speed The speed used for moving. */ void writeZhopStart(const coord_t hop_height, Velocity speed = 0.0); @@ -472,23 +489,23 @@ class GCodeExport : public NoCopy void writeZhopEnd(Velocity speed = 0.0); /*! - * Start the new_extruder: + * Start the new_extruder: * - set new extruder * - zero E value * - write extruder start gcode - * + * * \param new_extruder The extruder to start with */ void startExtruder(const size_t new_extruder); /*! - * Switch to the new_extruder: + * Switch to the new_extruder: * - perform neccessary retractions * - fiddle with E-values * - write extruder end gcode * - set new extruder * - write extruder start gcode - * + * * \param new_extruder The extruder to switch to * \param retraction_config_old_extruder The extruder switch retraction config of the old extruder, to perform the extruder switch retraction with. * \param perform_z_hop The amount by which the print head should be z hopped during extruder switch, or zero if it should not z hop. @@ -501,7 +518,7 @@ class GCodeExport : public NoCopy /*! * Write the gcode for priming the current extruder train so that it can be used. - * + * * \param travel_speed The travel speed when priming involves a movement */ void writePrimeTrain(const Velocity& travel_speed); @@ -512,9 +529,9 @@ class GCodeExport : public NoCopy * \param extruder The current extruder */ void setExtruderFanNumber(int extruder); - + void writeFanCommand(double speed); - + void writeTemperatureCommand(const size_t extruder, const Temperature& temperature, const bool wait = false); void writeBedTemperatureCommand(const Temperature& temperature, const bool wait = false); void writeBuildVolumeTemperatureCommand(const Temperature& temperature, const bool wait = false); @@ -542,7 +559,7 @@ class GCodeExport : public NoCopy /*! * Handle the initial (bed/nozzle) temperatures before any gcode is processed. * These temperatures are set in the pre-print setup in the firmware. - * + * * See FffGcodeWriter::processStartingCode * \param start_extruder_nr The extruder with which to start this print */ @@ -551,10 +568,10 @@ class GCodeExport : public NoCopy /*! * Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps * This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand - * + * * \warning This function must be called before any of the layers in the meshgroup are written to file! * That's because it sets the current temperature in the gcode! - * + * * \param extruder_nr The extruder number for which to better specify the temp * \param temp The temp at which the nozzle should be at startup */ @@ -562,7 +579,7 @@ class GCodeExport : public NoCopy /*! * Finish the gcode: turn fans off, write end gcode and flush all gcode left in the buffer. - * + * * \param endCode The end gcode to be appended at the very end. */ void finalize(const char* endCode); @@ -579,7 +596,7 @@ class GCodeExport : public NoCopy * * \param extruder Extruder number which last_e_value_after_wipe value to reset. */ - void ResetLastEValueAfterWipe(size_t extruder); + void ResetLastEValueAfterWipe(size_t extruder); /*! * Generate g-code for wiping current nozzle using provided config. @@ -589,6 +606,6 @@ class GCodeExport : public NoCopy void insertWipeScript(const WipeScriptConfig& wipe_config); }; -} +} // namespace cura -#endif//GCODEEXPORT_H +#endif // GCODEEXPORT_H diff --git a/include/timeEstimate.h b/include/timeEstimate.h index a2ec403bd1..4ce741a6ba 100644 --- a/include/timeEstimate.h +++ b/include/timeEstimate.h @@ -4,14 +4,14 @@ #ifndef TIME_ESTIMATE_H #define TIME_ESTIMATE_H -#include -#include -#include - #include "PrintFeature.h" #include "settings/types/Duration.h" //Print time estimates. -#include "settings/types/Velocity.h" //Speeds and accelerations at which we print. #include "settings/types/Ratio.h" +#include "settings/types/Velocity.h" //Speeds and accelerations at which we print. + +#include +#include +#include namespace cura { @@ -36,18 +36,31 @@ class TimeEstimateCalculator class Position { public: - Position() {for(unsigned int n=0;n blocks; + public: /*! * \brief Set the movement configuration of the firmware. @@ -96,8 +110,9 @@ class TimeEstimateCalculator void setMaxXyJerk(const Velocity& jerk); //!< Set the max xy jerk to \p jerk void reset(); - + std::vector calculate(); + private: void reversePass(); void forwardPass(); @@ -108,14 +123,14 @@ class TimeEstimateCalculator void recalculateTrapezoids(); // Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. - void calculateTrapezoidForBlock(Block *block, const Ratio entry_factor, const Ratio exit_factor); + void calculateTrapezoidForBlock(Block* block, const Ratio entry_factor, const Ratio exit_factor); // The kernel called by accelerationPlanner::calculate() when scanning the plan from last to first entry. - void plannerReversePassKernel(Block *previous, Block *current, Block *next); + void plannerReversePassKernel(Block* previous, Block* current, Block* next); // The kernel called by accelerationPlanner::calculate() when scanning the plan from first to last entry. - void plannerForwardPassKernel(Block *previous, Block *current, Block *next); + void plannerForwardPassKernel(Block* previous, Block* current, Block* next); }; -}//namespace cura -#endif//TIME_ESTIMATE_H +} // namespace cura +#endif // TIME_ESTIMATE_H diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h index 7b0b22560e..309fa0f8e2 100644 --- a/include/utils/types/numeric_facade.h +++ b/include/utils/types/numeric_facade.h @@ -22,38 +22,30 @@ struct NumericFacade constexpr NumericFacade(const NumericFacade& other) noexcept = default; constexpr NumericFacade(NumericFacade&& other) noexcept = default; - constexpr NumericFacade(const floating_point auto value) noexcept - requires floating_point - : value{ static_cast(value) } {}; + constexpr NumericFacade(const floating_point auto value) noexcept requires floating_point : value{ static_cast(value) } {}; - constexpr NumericFacade(const integral auto value) noexcept - requires integral - : value{ static_cast(value) } {}; + constexpr NumericFacade(const integral auto value) noexcept requires integral : value{ static_cast(value) } {}; constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; - constexpr NumericFacade& operator=(const floating_point auto& other) noexcept - requires floating_point + constexpr NumericFacade& operator=(const floating_point auto& other) noexcept requires floating_point { this->value = static_cast(other); return *this; } - constexpr NumericFacade& operator=(const integral auto& other) noexcept - requires integral + constexpr NumericFacade& operator=(const integral auto& other) noexcept requires integral { this->value = static_cast(other); return *this; } constexpr NumericFacade& operator=(NumericFacade&& other) noexcept = default; - constexpr NumericFacade& operator=(const integral auto&& other) noexcept - requires integral + constexpr NumericFacade& operator=(const integral auto&& other) noexcept requires integral { this->value = static_cast(other); return *this; } - constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept - requires floating_point + constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept requires floating_point { this->value = static_cast(other); return *this; @@ -180,28 +172,24 @@ struct NumericFacade return { -value }; } - constexpr NumericFacade& operator++() noexcept - requires integral + constexpr NumericFacade& operator++() noexcept requires integral { ++value; return *this; } - constexpr NumericFacade operator++(int) noexcept - requires integral + constexpr NumericFacade operator++(int) noexcept requires integral { return { value++ }; } - constexpr NumericFacade& operator--() noexcept - requires integral + constexpr NumericFacade& operator--() noexcept requires integral { --value; return *this; } - constexpr NumericFacade operator--(int) noexcept - requires integral + constexpr NumericFacade operator--(int) noexcept requires integral { return { value-- }; } diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index cafa6a1f65..7988479dd9 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1,17 +1,10 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include -#include -#include - -#include -#include +#include "LayerPlan.h" #include "Application.h" //To communicate layer view data. #include "ExtruderTrain.h" -#include "LayerPlan.h" #include "PathOrderMonotonic.h" //Monotonic ordering of skin lines. #include "Slice.h" #include "WipeScriptConfig.h" @@ -25,34 +18,40 @@ #include "utils/linearAlg2D.h" #include "utils/polygonUtils.h" +#include +#include + +#include +#include +#include +#include + 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; -ExtruderPlan::ExtruderPlan -( +ExtruderPlan::ExtruderPlan( const size_t extruder, const LayerIndex layer_nr, const bool is_initial_layer, const bool is_raft_layer, const coord_t layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, - const RetractionConfig& retraction_config -) : - heated_pre_travel_time(0), - required_start_temperature(-1), - extruder_nr(extruder), - layer_nr(layer_nr), - is_initial_layer(is_initial_layer), - is_raft_layer(is_raft_layer), - layer_thickness(layer_thickness), - fan_speed_layer_time_settings(fan_speed_layer_time_settings), - retraction_config(retraction_config), - extraTime(0.0), - temperatureFactor(0.0), - slowest_path_speed(0.0) + const RetractionConfig& retraction_config) + : heated_pre_travel_time(0) + , required_start_temperature(-1) + , extruder_nr(extruder) + , layer_nr(layer_nr) + , is_initial_layer(is_initial_layer) + , is_raft_layer(is_raft_layer) + , layer_thickness(layer_thickness) + , fan_speed_layer_time_settings(fan_speed_layer_time_settings) + , retraction_config(retraction_config) + , extraTime(0.0) + , temperatureFactor(0.0) + , slowest_path_speed(0.0) { } @@ -99,19 +98,17 @@ void ExtruderPlan::applyBackPressureCompensation(const Ratio back_pressure_compe } } -GCodePath* LayerPlan::getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, bool spiralize, const Ratio speed_factor) +GCodePath* LayerPlan::getLatestPathWithConfig( + const GCodePathConfig& config, + SpaceFillType space_fill_type, + const Ratio flow, + const Ratio width_factor, + bool spiralize, + const Ratio speed_factor) { std::vector& paths = extruder_plans.back().paths; - if - ( - paths.size() > 0 && - paths.back().config == &config && - ! paths.back().done && - paths.back().flow == flow && - paths.back().width_factor == width_factor && - paths.back().speed_factor == speed_factor && - paths.back().mesh == current_mesh - ) // spiralize can only change when a travel path is in between + if (paths.size() > 0 && paths.back().config == &config && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor + && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } @@ -133,8 +130,7 @@ void LayerPlan::forceNewPathStart() paths[paths.size() - 1].done = true; } -LayerPlan::LayerPlan -( +LayerPlan::LayerPlan( const SliceDataStorage& storage, LayerIndex layer_nr, coord_t z, @@ -143,26 +139,26 @@ LayerPlan::LayerPlan const std::vector& fan_speed_layer_time_settings_per_extruder, coord_t comb_boundary_offset, coord_t comb_move_inside_distance, - coord_t travel_avoid_distance -) : - configs_storage(storage, layer_nr, layer_thickness), - z(z), - final_travel_z(z), - mode_skip_agressive_merge(false), - storage(storage), - layer_nr(layer_nr), - is_initial_layer(layer_nr == 0 - static_cast(Raft::getTotalExtraLayers())), - is_raft_layer(layer_nr < 0 - static_cast(Raft::getFillerLayerCount())), - layer_thickness(layer_thickness), - has_prime_tower_planned_per_extruder(Application::getInstance().current_slice->scene.extruders.size(), false), - current_mesh(nullptr), - last_extruder_previous_layer(start_extruder), - last_planned_extruder(&Application::getInstance().current_slice->scene.extruders[start_extruder]), - first_travel_destination_is_inside(false), // set properly when addTravel is called for the first time (otherwise not set properly) - comb_boundary_minimum(computeCombBoundary(CombBoundary::MINIMUM)), - comb_boundary_preferred(computeCombBoundary(CombBoundary::PREFERRED)), - comb_move_inside_distance(comb_move_inside_distance), - fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder) + coord_t travel_avoid_distance) + : configs_storage(storage, layer_nr, layer_thickness) + , z(z) + , final_travel_z(z) + , mode_skip_agressive_merge(false) + , storage(storage) + , layer_nr(layer_nr) + , is_initial_layer(layer_nr == 0 - static_cast(Raft::getTotalExtraLayers())) + , is_raft_layer(layer_nr < 0 - static_cast(Raft::getFillerLayerCount())) + , layer_thickness(layer_thickness) + , has_prime_tower_planned_per_extruder(Application::getInstance().current_slice->scene.extruders.size(), false) + , current_mesh(nullptr) + , last_extruder_previous_layer(start_extruder) + , last_planned_extruder(&Application::getInstance().current_slice->scene.extruders[start_extruder]) + , first_travel_destination_is_inside(false) + , // set properly when addTravel is called for the first time (otherwise not set properly) + comb_boundary_minimum(computeCombBoundary(CombBoundary::MINIMUM)) + , comb_boundary_preferred(computeCombBoundary(CombBoundary::PREFERRED)) + , comb_move_inside_distance(comb_move_inside_distance) + , fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder) { size_t current_extruder = start_extruder; was_inside = true; // not used, because the first travel move is bogus @@ -180,7 +176,14 @@ LayerPlan::LayerPlan layer_start_pos_per_extruder.emplace_back(extruder.settings.get("layer_start_x"), extruder.settings.get("layer_start_y")); } extruder_plans.reserve(Application::getInstance().current_slice->scene.extruders.size()); - extruder_plans.emplace_back(current_extruder, layer_nr, is_initial_layer, is_raft_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_wipe_config_per_extruder[current_extruder].retraction_config); + extruder_plans.emplace_back( + current_extruder, + layer_nr, + is_initial_layer, + is_raft_layer, + layer_thickness, + fan_speed_layer_time_settings_per_extruder[current_extruder], + storage.retraction_wipe_config_per_extruder[current_extruder].retraction_config); for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { // Skirt and brim. @@ -303,7 +306,14 @@ bool LayerPlan::setExtruder(const size_t extruder_nr) { // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer extruder_plans.back().extruder_nr = extruder_nr; } - extruder_plans.emplace_back(extruder_nr, layer_nr, is_initial_layer, is_raft_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder_nr], storage.retraction_wipe_config_per_extruder[extruder_nr].retraction_config); + extruder_plans.emplace_back( + extruder_nr, + layer_nr, + is_initial_layer, + is_raft_layer, + layer_thickness, + fan_speed_layer_time_settings_per_extruder[extruder_nr], + storage.retraction_wipe_config_per_extruder[extruder_nr].retraction_config); assert(extruder_plans.size() <= Application::getInstance().current_slice->scene.extruders.size() && "Never use the same extruder twice on one layer!"); last_planned_extruder = &Application::getInstance().current_slice->scene.extruders[extruder_nr]; @@ -374,7 +384,8 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) { const GCodePathConfig& travel_config = configs_storage.travel_config_per_extruder[getExtruder()]; - const RetractionConfig& retraction_config = current_mesh ? current_mesh->retraction_wipe_config.retraction_config : storage.retraction_wipe_config_per_extruder[getExtruder()].retraction_config; + const RetractionConfig& retraction_config + = current_mesh ? current_mesh->retraction_wipe_config.retraction_config : storage.retraction_wipe_config_per_extruder[getExtruder()].retraction_config; GCodePath* path = getLatestPathWithConfig(travel_config, SpaceFillType::None); @@ -422,20 +433,17 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) bool unretract_before_last_travel_move = false; // Decided when calculating the combing const bool perform_z_hops = mesh_or_extruder_settings.get("retraction_hop_enabled"); const bool perform_z_hops_only_when_collides = mesh_or_extruder_settings.get("retraction_hop_only_when_collides"); - combed = - comb->calc - ( - perform_z_hops, - perform_z_hops_only_when_collides, - *extruder, - *last_planned_position, - p, - combPaths, - was_inside, - is_inside, - max_distance_ignored, - unretract_before_last_travel_move - ); + combed = comb->calc( + perform_z_hops, + perform_z_hops_only_when_collides, + *extruder, + *last_planned_position, + p, + combPaths, + was_inside, + is_inside, + max_distance_ignored, + unretract_before_last_travel_move); if (combed) { bool retract = path->retract || (combPaths.size() > 1 && retraction_enable); @@ -503,8 +511,9 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) { if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc) { // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print. - assert (extruder != nullptr); - coord_t innermost_wall_line_width = mesh_or_extruder_settings.get((mesh_or_extruder_settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); + assert(extruder != nullptr); + coord_t innermost_wall_line_width + = mesh_or_extruder_settings.get((mesh_or_extruder_settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (layer_nr == 0) { innermost_wall_line_width *= mesh_or_extruder_settings.get("initial_layer_line_width_factor"); @@ -550,7 +559,15 @@ void LayerPlan::planPrime(const float& prime_blob_wipe_length) forceNewPathStart(); } -void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow, const Ratio width_factor, bool spiralize, Ratio speed_factor, double fan_speed) +void LayerPlan::addExtrusionMove( + Point p, + const GCodePathConfig& config, + SpaceFillType space_fill_type, + const Ratio& flow, + const Ratio width_factor, + bool spiralize, + Ratio speed_factor, + double fan_speed) { GCodePath* path = getLatestPathWithConfig(config, space_fill_type, flow, width_factor, spiralize, speed_factor); path->points.push_back(p); @@ -562,7 +579,15 @@ void LayerPlan::addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFi last_planned_position = p; } -void LayerPlan::addPolygon(ConstPolygonRef polygon, int start_idx, const bool backwards, const GCodePathConfig& config, coord_t wall_0_wipe_dist, bool spiralize, const Ratio& flow_ratio, bool always_retract) +void LayerPlan::addPolygon( + ConstPolygonRef polygon, + int start_idx, + const bool backwards, + const GCodePathConfig& config, + coord_t wall_0_wipe_dist, + bool spiralize, + const Ratio& flow_ratio, + bool always_retract) { constexpr Ratio width_ratio = 1.0_r; // Not printed with variable line width. Point p0 = polygon[start_idx]; @@ -610,15 +635,16 @@ void LayerPlan::addPolygon(ConstPolygonRef polygon, int start_idx, const bool ba } } -void LayerPlan::addPolygonsByOptimizer(const Polygons& polygons, - const GCodePathConfig& config, - const ZSeamConfig& z_seam_config, - coord_t wall_0_wipe_dist, - bool spiralize, - const Ratio flow_ratio, - bool always_retract, - bool reverse_order, - const std::optional start_near_location) +void LayerPlan::addPolygonsByOptimizer( + const Polygons& polygons, + const GCodePathConfig& config, + const ZSeamConfig& z_seam_config, + coord_t wall_0_wipe_dist, + bool spiralize, + const Ratio flow_ratio, + bool always_retract, + bool reverse_order, + const std::optional start_near_location) { if (polygons.empty()) { @@ -650,16 +676,17 @@ 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 -void LayerPlan::addWallLine(const Point& p0, - const Point& p1, - const Settings& settings, - const GCodePathConfig& non_bridge_config, - const GCodePathConfig& bridge_config, - float flow, - const Ratio width_factor, - float& non_bridge_line_volume, - Ratio speed_factor, - double distance_to_bridge_start) +void LayerPlan::addWallLine( + const Point& p0, + const Point& p1, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + float flow, + const Ratio width_factor, + float& non_bridge_line_volume, + Ratio speed_factor, + double distance_to_bridge_start) { const coord_t min_line_len = settings.get("meshfix_maximum_resolution") / 2; // Shouldn't cut up stuff (too much) below the required simplify resolution. const double acceleration_segment_len = MM2INT(1); // accelerate using segments of this length @@ -685,7 +712,9 @@ void LayerPlan::addWallLine(const Point& p0, while (distance_to_line_end > min_line_len) { // if we are accelerating after a bridge line, the segment length is less than the whole line length - Point segment_end = (speed_factor == 1 || distance_to_line_end < acceleration_segment_len) ? line_end : cur_point + (line_end - cur_point) * acceleration_segment_len / distance_to_line_end; + Point segment_end = (speed_factor == 1 || distance_to_line_end < acceleration_segment_len) + ? line_end + : cur_point + (line_end - cur_point) * acceleration_segment_len / distance_to_line_end; // flow required for the next line segment - when accelerating after a bridge segment, the flow is increased in inverse proportion to the speed_factor // so the slower the feedrate, the greater the flow - the idea is to get the extruder back to normal pressure as quickly as possible @@ -714,7 +743,14 @@ void LayerPlan::addWallLine(const Point& p0, if ((len - coast_dist) > min_line_len) { // segment is longer than coast distance so extrude using non-bridge config to start of coast - addExtrusionMove(segment_end + coast_dist * (cur_point - segment_end) / len, non_bridge_config, SpaceFillType::Polygons, segment_flow, width_factor, spiralize, speed_factor); + addExtrusionMove( + segment_end + coast_dist * (cur_point - segment_end) / len, + non_bridge_config, + SpaceFillType::Polygons, + segment_flow, + width_factor, + spiralize, + speed_factor); } // then coast to start of bridge segment constexpr Ratio flow = 0.0_r; // Coasting has no flow rate. @@ -723,13 +759,14 @@ void LayerPlan::addWallLine(const Point& p0, else { // no coasting required, just normal segment using non-bridge config - addExtrusionMove(segment_end, - non_bridge_config, - SpaceFillType::Polygons, - segment_flow, - width_factor, - spiralize, - (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); + addExtrusionMove( + segment_end, + non_bridge_config, + SpaceFillType::Polygons, + segment_flow, + width_factor, + spiralize, + (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); } distance_to_bridge_start -= len; @@ -737,13 +774,14 @@ void LayerPlan::addWallLine(const Point& p0, else { // no coasting required, just normal segment using non-bridge config - addExtrusionMove(segment_end, - non_bridge_config, - SpaceFillType::Polygons, - segment_flow, - width_factor, - spiralize, - (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); + addExtrusionMove( + segment_end, + non_bridge_config, + SpaceFillType::Polygons, + segment_flow, + width_factor, + spiralize, + (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? speed_factor : overhang_speed_factor); } non_bridge_line_volume += vSize(cur_point - segment_end) * segment_flow * width_factor * speed_factor * non_bridge_config.getSpeed(); cur_point = segment_end; @@ -759,7 +797,14 @@ void LayerPlan::addWallLine(const Point& p0, if (bridge_wall_mask.empty()) { // no bridges required - addExtrusionMove(p1, non_bridge_config, SpaceFillType::Polygons, flow, width_factor, spiralize, (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? 1.0_r : overhang_speed_factor); + addExtrusionMove( + p1, + non_bridge_config, + SpaceFillType::Polygons, + flow, + width_factor, + spiralize, + (overhang_mask.empty() || (! overhang_mask.inside(p0, true) && ! overhang_mask.inside(p1, true))) ? 1.0_r : overhang_speed_factor); } else { @@ -851,7 +896,15 @@ void LayerPlan::addWallLine(const Point& p0, } } -void LayerPlan::addWall(ConstPolygonRef wall, int start_idx, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, float flow_ratio, bool always_retract) +void LayerPlan::addWall( + ConstPolygonRef wall, + int start_idx, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract) { // TODO: Deprecated in favor of ExtrusionJunction version below. if (wall.size() < 3) @@ -860,10 +913,18 @@ void LayerPlan::addWall(ConstPolygonRef wall, int start_idx, const Settings& set } constexpr size_t dummy_perimeter_id = 0; // <-- Here, don't care about which perimeter any more. - const coord_t nominal_line_width = non_bridge_config.getLineWidth(); // <-- The line width which it's 'supposed to' be will be used to adjust the flow ratio each time, this'll give a flow-ratio-multiplier of 1. + const coord_t nominal_line_width + = non_bridge_config + .getLineWidth(); // <-- The line width which it's 'supposed to' be will be used to adjust the flow ratio each time, this'll give a flow-ratio-multiplier of 1. ExtrusionLine ewall; - std::for_each(wall.begin(), wall.end(), [&dummy_perimeter_id, &nominal_line_width, &ewall](const Point& p) { ewall.emplace_back(p, nominal_line_width, dummy_perimeter_id); }); + std::for_each( + wall.begin(), + wall.end(), + [&dummy_perimeter_id, &nominal_line_width, &ewall](const Point& p) + { + ewall.emplace_back(p, nominal_line_width, dummy_perimeter_id); + }); ewall.emplace_back(*wall.begin(), nominal_line_width, dummy_perimeter_id); constexpr bool is_closed = true; constexpr bool is_reversed = false; @@ -871,17 +932,18 @@ void LayerPlan::addWall(ConstPolygonRef wall, int start_idx, const Settings& set addWall(ewall, start_idx, settings, non_bridge_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract, is_closed, is_reversed, is_linked_path); } -void LayerPlan::addWall(const ExtrusionLine& wall, - int start_idx, - const Settings& settings, - const GCodePathConfig& non_bridge_config, - const GCodePathConfig& bridge_config, - coord_t wall_0_wipe_dist, - float flow_ratio, - bool always_retract, - const bool is_closed, - const bool is_reversed, - const bool is_linked_path) +void LayerPlan::addWall( + const ExtrusionLine& wall, + int start_idx, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract, + const bool is_closed, + const bool is_reversed, + const bool is_linked_path) { if (wall.empty()) { @@ -899,7 +961,9 @@ void LayerPlan::addWall(const ExtrusionLine& wall, const coord_t min_bridge_line_len = settings.get("bridge_wall_min_length"); - const Ratio nominal_line_width_multiplier { 1.0 / Ratio { static_cast(non_bridge_config.getLineWidth()) } }; // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this + const Ratio nominal_line_width_multiplier{ + 1.0 / Ratio{ static_cast(non_bridge_config.getLineWidth()) } + }; // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this // helper function to calculate the distance from the start of the current wall line to the first bridge segment @@ -1047,12 +1111,29 @@ void LayerPlan::addWall(const ExtrusionLine& wall, if (is_small_feature) { constexpr bool spiralize = false; - addExtrusionMove(destination, non_bridge_config, SpaceFillType::Polygons, flow_ratio, line_width * nominal_line_width_multiplier, spiralize, small_feature_speed_factor); + addExtrusionMove( + destination, + non_bridge_config, + SpaceFillType::Polygons, + flow_ratio, + line_width * nominal_line_width_multiplier, + spiralize, + small_feature_speed_factor); } else { const Point origin = p0.p + normal(line_vector, piece_length * piece); - addWallLine(origin, destination, settings, non_bridge_config, bridge_config, flow_ratio, line_width * nominal_line_width_multiplier, non_bridge_line_volume, speed_factor, distance_to_bridge_start); + addWallLine( + origin, + destination, + settings, + non_bridge_config, + bridge_config, + flow_ratio, + line_width * nominal_line_width_multiplier, + non_bridge_line_volume, + speed_factor, + distance_to_bridge_start); } } @@ -1109,7 +1190,7 @@ void LayerPlan::addInfillWall(const ExtrusionLine& wall, const GCodePathConfig& for (const auto& junction_n : wall) { - const Ratio width_factor { static_cast(junction_n.w) / Ratio { static_cast(path_config.getLineWidth()) } }; + const Ratio width_factor{ static_cast(junction_n.w) / Ratio{ static_cast(path_config.getLineWidth()) } }; constexpr SpaceFillType space_fill_type = SpaceFillType::Polygons; constexpr Ratio flow = 1.0_r; addExtrusionMove(junction_n.p, path_config, space_fill_type, flow, width_factor); @@ -1117,14 +1198,15 @@ void LayerPlan::addInfillWall(const ExtrusionLine& wall, const GCodePathConfig& } } -void LayerPlan::addWalls(const Polygons& walls, - const Settings& settings, - const GCodePathConfig& non_bridge_config, - const GCodePathConfig& bridge_config, - const ZSeamConfig& z_seam_config, - coord_t wall_0_wipe_dist, - float flow_ratio, - bool always_retract) +void LayerPlan::addWalls( + const Polygons& walls, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + const ZSeamConfig& z_seam_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract) { // TODO: Deprecated in favor of ExtrusionJunction version below. PathOrderOptimizer orderOptimizer(getLastPlannedPositionOrStartingPosition(), z_seam_config); @@ -1140,16 +1222,17 @@ void LayerPlan::addWalls(const Polygons& walls, } -void LayerPlan::addLinesByOptimizer(const Polygons& polygons, - const GCodePathConfig& config, - const SpaceFillType space_fill_type, - const bool enable_travel_optimization, - const coord_t wipe_dist, - const Ratio flow_ratio, - const std::optional near_start_location, - const double fan_speed, - const bool reverse_print_direction, - const std::unordered_multimap& order_requirements) +void LayerPlan::addLinesByOptimizer( + const Polygons& polygons, + const GCodePathConfig& config, + const SpaceFillType space_fill_type, + const bool enable_travel_optimization, + const coord_t wipe_dist, + const Ratio flow_ratio, + const std::optional near_start_location, + const double fan_speed, + const bool reverse_print_direction, + const std::unordered_multimap& order_requirements) { Polygons boundary; if (enable_travel_optimization && ! comb_boundary_minimum.empty()) @@ -1174,7 +1257,13 @@ void LayerPlan::addLinesByOptimizer(const Polygons& polygons, boundary = Simplify(MM2INT(0.1), MM2INT(0.1), 0).polygon(boundary); } constexpr bool detect_loops = true; - PathOrderOptimizer order_optimizer(near_start_location.value_or(getLastPlannedPositionOrStartingPosition()), ZSeamConfig(), detect_loops, &boundary, reverse_print_direction, order_requirements); + PathOrderOptimizer order_optimizer( + near_start_location.value_or(getLastPlannedPositionOrStartingPosition()), + ZSeamConfig(), + detect_loops, + &boundary, + reverse_print_direction, + order_requirements); for (size_t line_idx = 0; line_idx < polygons.size(); line_idx++) { order_optimizer.addPolyline(polygons[line_idx]); @@ -1185,7 +1274,13 @@ void LayerPlan::addLinesByOptimizer(const Polygons& polygons, } -void LayerPlan::addLinesInGivenOrder(const std::vector>& paths, const GCodePathConfig& config, const SpaceFillType space_fill_type, const coord_t wipe_dist, const Ratio flow_ratio, const double fan_speed) +void LayerPlan::addLinesInGivenOrder( + const std::vector>& paths, + const GCodePathConfig& config, + const SpaceFillType space_fill_type, + const coord_t wipe_dist, + const Ratio flow_ratio, + const double fan_speed) { coord_t half_line_width = config.getLineWidth() / 2; coord_t line_width_2 = half_line_width * half_line_width; @@ -1282,16 +1377,17 @@ void LayerPlan::addLinesInGivenOrder(const std::vector order(monotonic_direction, max_adjacent_distance, last_position); @@ -1335,7 +1434,14 @@ void LayerPlan::addLinesMonotonic(const Polygons& area, addLinesByOptimizer(left_over, config, space_fill_type, true, wipe_dist, flow_ratio, getLastPlannedPositionOrStartingPosition(), fan_speed); } -void LayerPlan::spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRef wall, ConstPolygonRef last_wall, const int seam_vertex_idx, const int last_seam_vertex_idx, const bool is_top_layer, const bool is_bottom_layer) +void LayerPlan::spiralizeWallSlice( + const GCodePathConfig& config, + ConstPolygonRef wall, + ConstPolygonRef last_wall, + const int seam_vertex_idx, + const int last_seam_vertex_idx, + const bool is_top_layer, + const bool is_bottom_layer) { const bool smooth_contours = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("smooth_spiralized_contours"); constexpr bool spiralize = true; // In addExtrusionMove calls, enable spiralize and use nominal line width. @@ -1503,10 +1609,10 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ double factor = 0.0; double target_speed = 0.0; - std::function slow_down_func - { - [&target_speed](const GCodePath& path) { return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); } - }; + std::function slow_down_func{ [&target_speed](const GCodePath& path) + { + return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); + } }; if (minExtrudeTime >= total_extrude_time_at_minimum_speed) { @@ -1525,8 +1631,9 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ { // Slowing down to the slowest path speed is not sufficient, need to slow down further to the minimum speed. // Linear interpolate between total_extrude_time_at_slowest_speed and total_extrude_time_at_minimum_speed - const double factor = (1/total_extrude_time_at_minimum_speed - 1/minExtrudeTime) / (1/total_extrude_time_at_minimum_speed - 1/total_extrude_time_at_slowest_speed); - target_speed = minimalSpeed * (1.0-factor) + slowest_path_speed * factor; + const double factor + = (1 / total_extrude_time_at_minimum_speed - 1 / minExtrudeTime) / (1 / total_extrude_time_at_minimum_speed - 1 / total_extrude_time_at_slowest_speed); + target_speed = minimalSpeed * (1.0 - factor) + slowest_path_speed * factor; temperatureFactor = 1.0 - factor; // Update stored naive time estimates @@ -1536,13 +1643,12 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ { // Slowing down to the slowest_speed is sufficient to respect the minimum layer time. // Linear interpolate between extrudeTime and total_extrude_time_at_slowest_speed - factor = (1/total_extrude_time_at_slowest_speed - 1/minExtrudeTime) / (1/total_extrude_time_at_slowest_speed - 1/extrudeTime); - slow_down_func = - [&slowest_path_speed = slowest_path_speed, &factor](const GCodePath& path) - { - const double target_speed = slowest_path_speed * (1.0 - factor) + (path.config->getSpeed() * path.speed_factor) * factor; - return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); - }; + factor = (1 / total_extrude_time_at_slowest_speed - 1 / minExtrudeTime) / (1 / total_extrude_time_at_slowest_speed - 1 / extrudeTime); + slow_down_func = [&slowest_path_speed = slowest_path_speed, &factor](const GCodePath& path) + { + const double target_speed = slowest_path_speed * (1.0 - factor) + (path.config->getSpeed() * path.speed_factor) * factor; + return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); + }; // Update stored naive time estimates estimates.extrude_time = minExtrudeTime; @@ -1577,17 +1683,14 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point starting_pos Point p0 = starting_position; const double min_path_speed = fan_speed_layer_time_settings.cool_min_speed; - slowest_path_speed = - std::accumulate - ( - paths.begin(), - paths.end(), - std::numeric_limits::max(), - [](double value, const GCodePath& path) - { - return path.isTravelPath() ? value : std::min(value, path.config->getSpeed().value * path.speed_factor); - } - ); + slowest_path_speed = std::accumulate( + paths.begin(), + paths.end(), + std::numeric_limits::max(), + [](double value, const GCodePath& path) + { + return path.isTravelPath() ? value : std::min(value, path.config->getSpeed().value * path.speed_factor); + }); bool was_retracted = false; // wrong assumption; won't matter that much. (TODO) for (GCodePath& path : paths) @@ -1706,19 +1809,27 @@ void ExtruderPlan::processFanSpeedForFirstLayers() */ fan_speed = fan_speed_layer_time_settings.cool_fan_speed_min; - if (layer_nr < fan_speed_layer_time_settings.cool_fan_full_layer && fan_speed_layer_time_settings.cool_fan_full_layer > 0 // don't apply initial layer fan speed speedup if disabled. + if (layer_nr < fan_speed_layer_time_settings.cool_fan_full_layer + && fan_speed_layer_time_settings.cool_fan_full_layer > 0 // don't apply initial layer fan speed speedup if disabled. && ! is_raft_layer // don't apply initial layer fan speed speedup to raft, but to model layers ) { // Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0. - fan_speed = fan_speed_layer_time_settings.cool_fan_speed_0 + (fan_speed - fan_speed_layer_time_settings.cool_fan_speed_0) * std::max(LayerIndex(0), layer_nr) / fan_speed_layer_time_settings.cool_fan_full_layer; + fan_speed = fan_speed_layer_time_settings.cool_fan_speed_0 + + (fan_speed - fan_speed_layer_time_settings.cool_fan_speed_0) * std::max(LayerIndex(0), layer_nr) / fan_speed_layer_time_settings.cool_fan_full_layer; } } void LayerPlan::processFanSpeedAndMinimalLayerTime(Point starting_position) { // the minimum layer time behaviour is only applied to the last extruder. - const size_t last_extruder_nr = ranges::max_element(extruder_plans, [](const ExtruderPlan& a, const ExtruderPlan& b) { return a.extruder_nr < b.extruder_nr; })->extruder_nr; + const size_t last_extruder_nr = ranges::max_element( + extruder_plans, + [](const ExtruderPlan& a, const ExtruderPlan& b) + { + return a.extruder_nr < b.extruder_nr; + }) + ->extruder_nr; Point starting_position_last_extruder; unsigned int last_extruder_idx; double other_extr_plan_time = 0.0; @@ -1770,10 +1881,13 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // flow-rate compensation const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - gcode.setFlowRateExtrusionSettings(mesh_group_settings.get("flow_rate_max_extrusion_offset"), mesh_group_settings.get("flow_rate_extrusion_offset_factor")); // Offset is in mm. + gcode.setFlowRateExtrusionSettings( + mesh_group_settings.get("flow_rate_max_extrusion_offset"), + mesh_group_settings.get("flow_rate_extrusion_offset_factor")); // Offset is in mm. static LayerIndex layer_1{ 1 - static_cast(Raft::getTotalExtraLayers()) }; - if (layer_nr == layer_1 && mesh_group_settings.get("machine_heated_bed") && mesh_group_settings.get("material_bed_temperature") != mesh_group_settings.get("material_bed_temperature_layer_0")) + if (layer_nr == layer_1 && mesh_group_settings.get("machine_heated_bed") + && mesh_group_settings.get("material_bed_temperature") != mesh_group_settings.get("material_bed_temperature_layer_0")) { constexpr bool wait = false; gcode.writeBedTemperatureCommand(mesh_group_settings.get("material_bed_temperature"), wait); @@ -1793,7 +1907,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) for (size_t extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; - const RetractionAndWipeConfig* retraction_config = current_mesh ? ¤t_mesh->retraction_wipe_config: &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; + const RetractionAndWipeConfig* retraction_config + = current_mesh ? ¤t_mesh->retraction_wipe_config : &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; coord_t z_hop_height = retraction_config->retraction_config.zHop; if (extruder_nr != extruder_plan.extruder_nr) @@ -1866,8 +1981,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) bool update_extrusion_offset = true; double cumulative_path_time = 0.; // Time in seconds. - const std::function insertTempOnTime = - [&](const double to_add, const int64_t path_idx) + const std::function insertTempOnTime = [&](const double to_add, const int64_t path_idx) { cumulative_path_time += to_add; extruder_plan.handleInserts(path_idx, gcode, cumulative_path_time); @@ -1990,7 +2104,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // for some movements such as prime tower purge, the speed may get changed by this factor speed *= path.speed_factor; - //This seems to be the best location to place this, but still not ideal. + // This seems to be the best location to place this, but still not ideal. if (path.mesh != current_mesh) { current_mesh = path.mesh; @@ -2102,7 +2216,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) if (extruder.settings.get("cool_lift_head") && extruder_plan.extraTime > 0.0) { gcode.writeComment("Small layer, adding delay"); - const RetractionAndWipeConfig& retraction_config = current_mesh ? current_mesh->retraction_wipe_config: storage.retraction_wipe_config_per_extruder[gcode.getExtruderNr()]; + const RetractionAndWipeConfig& retraction_config + = current_mesh ? current_mesh->retraction_wipe_config : storage.retraction_wipe_config_per_extruder[gcode.getExtruderNr()]; gcode.writeRetraction(retraction_config.retraction_config); if (extruder_plan_idx == extruder_plans.size() - 1 || ! extruder.settings.get("machine_extruder_end_pos_abs")) { // only do the z-hop if it's the last extruder plan; otherwise it's already at the switching bay area @@ -2154,7 +2269,12 @@ bool LayerPlan::makeRetractSwitchRetract(unsigned int extruder_plan_idx, unsigne } } -bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_plan_idx, const size_t path_idx, const coord_t layer_thickness, const std::function insertTempOnTime) +bool LayerPlan::writePathWithCoasting( + GCodeExport& gcode, + const size_t extruder_plan_idx, + const size_t path_idx, + const coord_t layer_thickness, + const std::function insertTempOnTime) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; const ExtruderTrain& extruder = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr]; @@ -2174,9 +2294,11 @@ bool LayerPlan::writePathWithCoasting(GCodeExport& gcode, const size_t extruder_ const double extrude_speed = path.config->getSpeed() * 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 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 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 // /\ 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...) @@ -2281,7 +2403,8 @@ void LayerPlan::applyBackPressureCompensation() { for (auto& extruder_plan : extruder_plans) { - const Ratio back_pressure_compensation = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr].settings.get("speed_equalize_flow_width_factor"); + const Ratio back_pressure_compensation + = Application::getInstance().current_slice->scene.extruders[extruder_plan.extruder_nr].settings.get("speed_equalize_flow_width_factor"); if (back_pressure_compensation != 0.0) { extruder_plan.applyBackPressureCompensation(back_pressure_compensation); diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index 04dd141bf1..5d6babcdd6 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -3,22 +3,24 @@ #include "SkeletalTrapezoidation.h" -#include -#include -#include -#include -#include - -#include -#include - #include "BoostInterface.hpp" #include "settings/types/Ratio.h" #include "utils/VoronoiUtils.h" #include "utils/linearAlg2D.h" #include "utils/macros.h" -#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 // A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance). +#include +#include + +#include +#include +#include +#include +#include + +#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX \ + 1000 // A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing + // performance). namespace cura { @@ -39,7 +41,15 @@ SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_ty } } -void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type& vd_edge, edge_t*& prev_edge, Point& start_source_point, Point& end_source_point, const std::vector& points, const std::vector& segments) +void SkeletalTrapezoidation::transferEdge( + Point from, + Point to, + vd_t::edge_type& vd_edge, + edge_t*& prev_edge, + Point& start_source_point, + Point& end_source_point, + const std::vector& points, + const std::vector& segments) { auto he_edge_it = vd_edge_to_he_edge.find(vd_edge.twin()); if (he_edge_it != vd_edge_to_he_edge.end()) @@ -107,7 +117,8 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type& { spdlog::warn("Previous edge doesn't go anywhere."); } - node_t* v0 = (prev_edge) ? prev_edge->to : &makeNode(*vd_edge.vertex0(), from); // TODO: investigate whether boost:voronoi can produce multiple verts and violates consistency + node_t* v0 + = (prev_edge) ? prev_edge->to : &makeNode(*vd_edge.vertex0(), from); // TODO: investigate whether boost:voronoi can produce multiple verts and violates consistency Point p0 = discretized.front(); for (size_t p1_idx = 1; p1_idx < discretized.size(); p1_idx++) { @@ -254,13 +265,14 @@ std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_ } -bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, - Point& start_source_point, - Point& end_source_point, - vd_t::edge_type*& starting_vd_edge, - vd_t::edge_type*& ending_vd_edge, - const std::vector& points, - const std::vector& segments) +bool SkeletalTrapezoidation::computePointCellRange( + vd_t::cell_type& cell, + Point& start_source_point, + Point& end_source_point, + vd_t::edge_type*& starting_vd_edge, + vd_t::edge_type*& ending_vd_edge, + const std::vector& points, + const std::vector& segments) { if (cell.incident_edge()->is_infinite()) { @@ -298,7 +310,9 @@ bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, } else { - assert((VoronoiUtils::p(vd_edge->vertex0()) == source_point || ! vd_edge->is_secondary()) && "point cells must end in the point! They cannot cross the point with an edge, because collinear edges are not allowed in the input."); + assert( + (VoronoiUtils::p(vd_edge->vertex0()) == source_point || ! vd_edge->is_secondary()) + && "point cells must end in the point! They cannot cross the point with an edge, because collinear edges are not allowed in the input."); } } while (vd_edge = vd_edge->next(), vd_edge != cell.incident_edge()); assert(starting_vd_edge && ending_vd_edge); @@ -306,13 +320,14 @@ bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, return true; } -void SkeletalTrapezoidation::computeSegmentCellRange(vd_t::cell_type& cell, - Point& start_source_point, - Point& end_source_point, - vd_t::edge_type*& starting_vd_edge, - vd_t::edge_type*& ending_vd_edge, - const std::vector& points, - const std::vector& segments) +void SkeletalTrapezoidation::computeSegmentCellRange( + vd_t::cell_type& cell, + Point& start_source_point, + Point& end_source_point, + vd_t::edge_type*& starting_vd_edge, + vd_t::edge_type*& ending_vd_edge, + const std::vector& points, + const std::vector& segments) { const Segment& source_segment = VoronoiUtils::getSourceSegment(cell, points, segments); Point from = source_segment.from(); @@ -357,15 +372,16 @@ void SkeletalTrapezoidation::computeSegmentCellRange(vd_t::cell_type& cell, end_source_point = source_segment.from(); } -SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, - const BeadingStrategy& beading_strategy, - AngleRadians transitioning_angle, - coord_t discretization_step_size, - coord_t transition_filter_dist, - coord_t allowed_filter_deviation, - coord_t beading_propagation_transition_dist, - int layer_idx, - SectionType section_type) +SkeletalTrapezoidation::SkeletalTrapezoidation( + const Polygons& polys, + const BeadingStrategy& beading_strategy, + AngleRadians transitioning_angle, + coord_t discretization_step_size, + coord_t transition_filter_dist, + coord_t allowed_filter_deviation, + coord_t beading_propagation_transition_dist, + int layer_idx, + SectionType section_type) : transitioning_angle(transitioning_angle) , discretization_step_size(discretization_step_size) , transition_filter_dist(transition_filter_dist) @@ -432,7 +448,15 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) // Copy start to end edge to graph edge_t* prev_edge = nullptr; - transferEdge(start_source_point, VoronoiUtils::p(starting_vonoroi_edge->vertex1()), *starting_vonoroi_edge, prev_edge, start_source_point, end_source_point, points, segments); + transferEdge( + start_source_point, + VoronoiUtils::p(starting_vonoroi_edge->vertex1()), + *starting_vonoroi_edge, + prev_edge, + start_source_point, + end_source_point, + points, + segments); node_t* starting_node = vd_node_to_he_node[starting_vonoroi_edge->vertex0()]; starting_node->data.distance_to_boundary = 0; @@ -516,44 +540,164 @@ void SkeletalTrapezoidation::generateToolpaths(std::vector& } updateBeadCount(); - scripta::log("st_graph_0", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_0", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); filterNoncentralRegions(); - scripta::log("st_graph_1", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_1", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); generateTransitioningRibs(); - scripta::log("st_graph_2", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_2", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); generateExtraRibs(); - scripta::log("st_graph_3", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_3", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); generateSegments(); - scripta::log("st_graph_4", graph, section_type, layer_idx, - scripta::CellVDI{"is_central", [](const auto& edge){ return static_cast(edge.data.is_central); } }, - scripta::CellVDI{"type", [](const auto& edge){ return static_cast(edge.data.type); } }, - scripta::PointVDI{"distance_to_boundary", [](const auto& node){ return node->data.distance_to_boundary; } }, - scripta::PointVDI{"bead_count", [](const auto& node){ return node->data.bead_count; } }, - scripta::PointVDI{"transition_ratio", [](const auto& node){ return node->data.transition_ratio; } }); + scripta::log( + "st_graph_4", + graph, + section_type, + layer_idx, + scripta::CellVDI{ "is_central", + [](const auto& edge) + { + return static_cast(edge.data.is_central); + } }, + scripta::CellVDI{ "type", + [](const auto& edge) + { + return static_cast(edge.data.type); + } }, + scripta::PointVDI{ "distance_to_boundary", + [](const auto& node) + { + return node->data.distance_to_boundary; + } }, + scripta::PointVDI{ "bead_count", + [](const auto& node) + { + return node->data.bead_count; + } }, + scripta::PointVDI{ "transition_ratio", + [](const auto& node) + { + return node->data.transition_ratio; + } }); } void SkeletalTrapezoidation::updateIsCentral() @@ -920,7 +1064,8 @@ void SkeletalTrapezoidation::filterTransitionMids() } } -std::list SkeletalTrapezoidation::dissolveNearbyTransitions(edge_t* edge_to_start, TransitionMiddle& origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up) +std::list + SkeletalTrapezoidation::dissolveNearbyTransitions(edge_t* edge_to_start, TransitionMiddle& origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up) { std::list to_be_dissolved; if (traveled_dist > max_dist) @@ -947,7 +1092,9 @@ std::list SkeletalTrapezoidation::diss const coord_t radius_here = edge->from->data.distance_to_boundary; const bool dissolve_result_is_odd = bool(origin_transition.lower_bead_count % 2) == going_up; const coord_t width_deviation = std::abs(origin_radius - radius_here) * 2; // times by two because the deviation happens at both sides of the significant edge - const coord_t line_width_deviation = dissolve_result_is_odd ? width_deviation : width_deviation / 2; // assume the deviation will be split over either 1 or 2 lines, i.e. assume wall_distribution_count = 1 + const coord_t line_width_deviation = dissolve_result_is_odd + ? width_deviation + : width_deviation / 2; // assume the deviation will be split over either 1 or 2 lines, i.e. assume wall_distribution_count = 1 if (line_width_deviation > allowed_filter_deviation) { should_dissolve = false; @@ -974,7 +1121,8 @@ std::list SkeletalTrapezoidation::diss } if (should_dissolve && ! seen_transition_on_this_edge) { - std::list to_be_dissolved_here = dissolveNearbyTransitions(edge, origin_transition, traveled_dist + ab_size, max_dist, going_up); + std::list to_be_dissolved_here + = dissolveNearbyTransitions(edge, origin_transition, traveled_dist + ab_size, max_dist, going_up); if (to_be_dissolved_here.empty()) { // The region is too long to be dissolved in this direction, so it cannot be dissolved in any direction. to_be_dissolved.clear(); @@ -1074,7 +1222,7 @@ void SkeletalTrapezoidation::generateTransitionEnds(edge_t& edge, coord_t mid_po const float transition_mid_position = beading_strategy.getTransitionAnchorPos(lower_bead_count); constexpr float inner_bead_width_ratio_after_transition = 1.0; - constexpr Ratio start_rest { 0.0 }; + constexpr Ratio start_rest{ 0.0 }; const float mid_rest = transition_mid_position * inner_bead_width_ratio_after_transition; constexpr float end_rest = inner_bead_width_ratio_after_transition; @@ -1100,14 +1248,15 @@ void SkeletalTrapezoidation::generateTransitionEnds(edge_t& edge, coord_t mid_po } } -bool SkeletalTrapezoidation::generateTransitionEnd(edge_t& edge, - coord_t start_pos, - coord_t end_pos, - coord_t transition_half_length, - Ratio start_rest, - Ratio end_rest, - coord_t lower_bead_count, - ptr_vector_t>& edge_transition_ends) +bool SkeletalTrapezoidation::generateTransitionEnd( + edge_t& edge, + coord_t start_pos, + coord_t end_pos, + coord_t transition_half_length, + Ratio start_rest, + Ratio end_rest, + coord_t lower_bead_count, + ptr_vector_t>& edge_transition_ends) { Point a = edge.from->p; Point b = edge.to->p; @@ -1299,7 +1448,11 @@ void SkeletalTrapezoidation::applyTransitions(ptr_vector_tp - edge.from->p, discretization_step_size) || edge.from->data.distance_to_boundary >= edge.to->data.distance_to_boundary) + if (! edge.data.isCentral() || shorterThen(edge.to->p - edge.from->p, discretization_step_size) + || edge.from->data.distance_to_boundary >= edge.to->data.distance_to_boundary) { continue; } @@ -1433,34 +1587,35 @@ void SkeletalTrapezoidation::generateSegments() } } - std::sort(upward_quad_mids.begin(), - upward_quad_mids.end(), - [this](edge_t* a, edge_t* b) - { - if (a->to->data.distance_to_boundary == b->to->data.distance_to_boundary) - { // PathOrdering between two 'upward' edges of the same distance is important when one of the edges is flat and connected to the other - if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary && b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) - { - coord_t max = std::numeric_limits::max(); - coord_t a_dist_from_up = std::min(a->distToGoUp().value_or(max), a->twin->distToGoUp().value_or(max)) - vSize(a->to->p - a->from->p); - coord_t b_dist_from_up = std::min(b->distToGoUp().value_or(max), b->twin->distToGoUp().value_or(max)) - vSize(b->to->p - b->from->p); - return a_dist_from_up < b_dist_from_up; - } - else if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary) - { - return true; // Edge a might be 'above' edge b - } - else if (b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) - { - return false; // Edge b might be 'above' edge a - } - else - { - // PathOrdering is not important - } - } - return a->to->data.distance_to_boundary > b->to->data.distance_to_boundary; - }); + std::sort( + upward_quad_mids.begin(), + upward_quad_mids.end(), + [this](edge_t* a, edge_t* b) + { + if (a->to->data.distance_to_boundary == b->to->data.distance_to_boundary) + { // PathOrdering between two 'upward' edges of the same distance is important when one of the edges is flat and connected to the other + if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary && b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) + { + coord_t max = std::numeric_limits::max(); + coord_t a_dist_from_up = std::min(a->distToGoUp().value_or(max), a->twin->distToGoUp().value_or(max)) - vSize(a->to->p - a->from->p); + coord_t b_dist_from_up = std::min(b->distToGoUp().value_or(max), b->twin->distToGoUp().value_or(max)) - vSize(b->to->p - b->from->p); + return a_dist_from_up < b_dist_from_up; + } + else if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary) + { + return true; // Edge a might be 'above' edge b + } + else if (b->from->data.distance_to_boundary == b->to->data.distance_to_boundary) + { + return false; // Edge b might be 'above' edge a + } + else + { + // PathOrdering is not important + } + } + return a->to->data.distance_to_boundary > b->to->data.distance_to_boundary; + }); ptr_vector_t node_beadings; { // Store beading @@ -1550,8 +1705,10 @@ void SkeletalTrapezoidation::propagateBeadingsUpward(std::vector& upwar { // Only propagate to places where there is place continue; } - assert((upward_edge->from->data.distance_to_boundary != upward_edge->to->data.distance_to_boundary || shorterThen(upward_edge->to->p - upward_edge->from->p, central_filter_dist)) - && "zero difference R edges should always be central"); + assert( + (upward_edge->from->data.distance_to_boundary != upward_edge->to->data.distance_to_boundary + || shorterThen(upward_edge->to->p - upward_edge->from->p, central_filter_dist)) + && "zero difference R edges should always be central"); coord_t length = vSize(upward_edge->to->p - upward_edge->from->p); BeadingPropagation upper_beading = lower_beading; upper_beading.dist_to_bottom_source += length; @@ -1570,7 +1727,8 @@ void SkeletalTrapezoidation::propagateBeadingsDownward(std::vector& upw if (! upward_quad_mid->data.isCentral()) { // for equidistant edge: propagate from known beading to node with unknown beading - if (upward_quad_mid->from->data.distance_to_boundary == upward_quad_mid->to->data.distance_to_boundary && upward_quad_mid->from->data.hasBeading() && ! upward_quad_mid->to->data.hasBeading()) + if (upward_quad_mid->from->data.distance_to_boundary == upward_quad_mid->to->data.distance_to_boundary && upward_quad_mid->from->data.hasBeading() + && ! upward_quad_mid->to->data.hasBeading()) { propagateBeadingsDownward(upward_quad_mid->twin, node_beadings); } @@ -1666,7 +1824,8 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin // f*(l-r) + r = s // f*(l-r) = s - r // f = (s-r) / (l-r) - float new_ratio = static_cast(switching_radius - right.toolpath_locations[next_inset_idx]) / static_cast(left.toolpath_locations[next_inset_idx] - right.toolpath_locations[next_inset_idx]); + float new_ratio = static_cast(switching_radius - right.toolpath_locations[next_inset_idx]) + / static_cast(left.toolpath_locations[next_inset_idx] - right.toolpath_locations[next_inset_idx]); new_ratio = std::min(1.0, new_ratio + 0.1); return interpolate(left, new_ratio, right); } @@ -1814,12 +1973,17 @@ std::shared_ptr SkeletalTrapezo { edge_t* edge_to; coord_t dist; - DistEdge(edge_t* edge_to, coord_t dist) : edge_to(edge_to), dist(dist) + DistEdge(edge_t* edge_to, coord_t dist) + : edge_to(edge_to) + , dist(dist) { } }; - auto compare = [](const DistEdge& l, const DistEdge& r) -> bool { return l.dist > r.dist; }; + auto compare = [](const DistEdge& l, const DistEdge& r) -> bool + { + return l.dist > r.dist; + }; std::priority_queue, decltype(compare)> further_edges(compare); bool first = true; for (edge_t* outgoing = node->incident_edge; outgoing && (first || outgoing != node->incident_edge); outgoing = outgoing->twin->next) @@ -1864,19 +2028,21 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c generated_toolpaths.resize(inset_idx + 1); } assert((generated_toolpaths[inset_idx].empty() || ! generated_toolpaths[inset_idx].back().junctions.empty()) && "empty extrusion lines should never have been generated"); - if (generated_toolpaths[inset_idx].empty() || generated_toolpaths[inset_idx].back().is_odd != is_odd || generated_toolpaths[inset_idx].back().junctions.back().perimeter_index != inset_idx // inset_idx should always be consistent + if (generated_toolpaths[inset_idx].empty() || generated_toolpaths[inset_idx].back().is_odd != is_odd + || generated_toolpaths[inset_idx].back().junctions.back().perimeter_index != inset_idx // inset_idx should always be consistent ) { 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 - && ! from_is_3way // force new path at 3way intersection + 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 && ! 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 - && ! to_is_3way // force new path at 3way intersection + 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 && ! to_is_3way // force new path at 3way intersection ) { if (! is_odd) @@ -1973,7 +2139,10 @@ void SkeletalTrapezoidation::connectJunctions(ptr_vector_t& edge_ assert(std::abs(int(from_junctions.size()) - int(to_junctions.size())) <= 1); // at transitions one end has more beads if (std::abs(int(from_junctions.size()) - int(to_junctions.size())) > 1) { - spdlog::warn("Can't create a transition when connecting two perimeters where the number of beads differs too much! {} vs. {}", from_junctions.size(), to_junctions.size()); + spdlog::warn( + "Can't create a transition when connecting two perimeters where the number of beads differs too much! {} vs. {}", + from_junctions.size(), + to_junctions.size()); } size_t segment_count = std::min(from_junctions.size(), to_junctions.size()); diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 9fb625cc3f..fef53433e7 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -2,6 +2,7 @@ // CuraEngine is released under the terms of the AGPLv3 or higher #include "TreeModelVolumes.h" + #include "TreeSupport.h" #include "TreeSupportEnums.h" #include "progress/Progress.h" @@ -17,8 +18,7 @@ namespace cura { -TreeModelVolumes::TreeModelVolumes -( +TreeModelVolumes::TreeModelVolumes( const SliceDataStorage& storage, const coord_t max_move, const coord_t max_move_slow, @@ -26,15 +26,16 @@ TreeModelVolumes::TreeModelVolumes size_t current_mesh_idx, double progress_multiplier, double progress_offset, - const std::vector& additional_excluded_areas -) : - max_move_{ std::max(max_move - 2, coord_t(0)) }, // -2 to avoid rounding errors - max_move_slow_{ std::max(max_move_slow - 2, coord_t(0)) }, // -2 to avoid rounding errors - min_offset_per_step_{ min_offset_per_step }, - progress_multiplier{ progress_multiplier }, - progress_offset{ progress_offset }, - machine_border_{ calculateMachineBorderCollision(storage.getMachineBorder())}, - machine_area_ { storage.getMachineBorder()} + const std::vector& additional_excluded_areas) + : max_move_{ std::max(max_move - 2, coord_t(0)) } + , // -2 to avoid rounding errors + max_move_slow_{ std::max(max_move_slow - 2, coord_t(0)) } + , // -2 to avoid rounding errors + min_offset_per_step_{ min_offset_per_step } + , progress_multiplier{ progress_multiplier } + , progress_offset{ progress_offset } + , machine_border_{ calculateMachineBorderCollision(storage.getMachineBorder()) } + , machine_area_{ storage.getMachineBorder() } { anti_overhang_ = std::vector(storage.support.supportLayers.size(), Polygons()); std::unordered_map mesh_to_layeroutline_idx; @@ -105,8 +106,7 @@ TreeModelVolumes::TreeModelVolumes const auto& mesh_l = mesh; // ^^^ Remove when fixed (and rename accordingly in the below parallel-for). - cura::parallel_for - ( + cura::parallel_for( 0, LayerIndex(layer_outlines_[mesh_to_layeroutline_idx[mesh_idx_l]].second.size()), [&](const LayerIndex layer_idx) @@ -117,26 +117,22 @@ TreeModelVolumes::TreeModelVolumes } Polygons outline = extractOutlineFromMesh(mesh_l, layer_idx); layer_outlines_[mesh_to_layeroutline_idx[mesh_idx_l]].second[layer_idx].add(outline); - } - ); + }); } // Merge all the layer outlines together. for (auto& layer_outline : layer_outlines_) { - cura::parallel_for - ( + cura::parallel_for( 0, LayerIndex(anti_overhang_.size()), [&](const LayerIndex layer_idx) { layer_outline.second[layer_idx] = layer_outline.second[layer_idx].unionPolygons(); - } - ); + }); } // Gather all excluded areas, like support-blockers and trees that where already generated. - cura::parallel_for - ( + cura::parallel_for( 0, LayerIndex(anti_overhang_.size()), [&](const LayerIndex layer_idx) @@ -156,11 +152,11 @@ TreeModelVolumes::TreeModelVolumes anti_overhang_[layer_idx].add(storage.primeTower.outer_poly); } anti_overhang_[layer_idx] = anti_overhang_[layer_idx].unionPolygons(); - } - ); + }); - for (max_layer_idx_without_blocker =0; max_layer_idx_without_blocker +1 radius_until_layer; - // while it is possible to calculate, up to which layer the avoidance should be calculated, this simulation is easier to understand, and does not need to be adjusted if something of the radius calculation is changed. - // Tested overhead was neligable (milliseconds for thounds of layers). + // while it is possible to calculate, up to which layer the avoidance should be calculated, this simulation is easier to understand, and does not need to be adjusted if + // something of the radius calculation is changed. Tested overhead was neligable (milliseconds for thounds of layers). for (LayerIndex simulated_dtt = 0; simulated_dtt <= max_layer; simulated_dtt++) { const LayerIndex current_layer = max_layer - simulated_dtt; @@ -235,7 +232,8 @@ void TreeModelVolumes::precalculate(coord_t max_layer) // Append additional radiis needed for collision. - radius_until_layer[ceilRadius(increase_until_radius, false)] = max_layer; // To calculate collision holefree for every radius, the collision of radius increase_until_radius will be required. + radius_until_layer[ceilRadius(increase_until_radius, false)] + = max_layer; // To calculate collision holefree for every radius, the collision of radius increase_until_radius will be required. // Collision for radius 0 needs to be calculated everywhere, as it will be used to ensure valid xy_distance in drawAreas. radius_until_layer[0] = max_layer; if (current_min_xy_dist_delta != 0) @@ -244,7 +242,10 @@ void TreeModelVolumes::precalculate(coord_t max_layer) } std::deque relevant_collision_radiis; - relevant_collision_radiis.insert(relevant_collision_radiis.end(), radius_until_layer.begin(), radius_until_layer.end()); // Now that required_avoidance_limit contains the maximum of old and regular required radius just copy. + relevant_collision_radiis.insert( + relevant_collision_radiis.end(), + radius_until_layer.begin(), + radius_until_layer.end()); // Now that required_avoidance_limit contains the maximum of old and regular required radius just copy. // ### Calculate the relevant collisions calculateCollision(relevant_collision_radiis); @@ -253,7 +254,7 @@ void TreeModelVolumes::precalculate(coord_t max_layer) std::deque relevant_hole_collision_radiis; for (RadiusLayerPair key : relevant_avoidance_radiis) { - spdlog::debug("Calculating avoidance of radius {} up to layer {}",key.first,key.second); + spdlog::debug("Calculating avoidance of radius {} up to layer {}", key.first, key.second); if (key.first < increase_until_radius + current_min_xy_dist_delta) { relevant_hole_collision_radiis.emplace_back(key); @@ -267,12 +268,10 @@ void TreeModelVolumes::precalculate(coord_t max_layer) auto t_acc = std::chrono::high_resolution_clock::now(); - if (max_layer_idx_without_blocker (t_coll - t_start).count(); - const auto dur_acc = 0.001 * std::chrono::duration_cast(t_acc-t_coll).count(); + const auto dur_acc = 0.001 * std::chrono::duration_cast(t_acc - t_coll).count(); const auto dur_avo = 0.001 * std::chrono::duration_cast(t_avo - t_acc).count(); const auto dur_col_avo = 0.001 * std::chrono::duration_cast(t_colAvo - t_avo).count(); - spdlog::info("Pre-calculating collision took {} ms. Pre-calculating avoidance took {} ms. Pre-calculating accumulated Placeables with radius 0 took {} ms. Pre-calculating collision-avoidance took {} ms. ", dur_col, dur_avo, dur_acc, dur_col_avo); - - + spdlog::info( + "Pre-calculating collision took {} ms. Pre-calculating avoidance took {} ms. Pre-calculating accumulated Placeables with radius 0 took {} ms. Pre-calculating " + "collision-avoidance took {} ms. ", + dur_col, + dur_avo, + dur_acc, + dur_col_avo); } const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) @@ -334,7 +336,8 @@ const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_ radius += current_min_xy_dist_delta; } - // special case as if a radius 0 is requested it could be to ensure correct xy distance. As such it is beneficial if the collision is as close to the configured values as possible. + // special case as if a radius 0 is requested it could be to ensure correct xy distance. As such it is beneficial if the collision is as close to the configured values as + // possible. if (orig_radius != 0) { radius = ceilRadius(radius); @@ -463,9 +466,14 @@ const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_ } if (precalculated) { - spdlog::warn("Had to calculate Avoidance (to model-bool: {}) at radius {} and layer {} and type {}, but precalculate was called. Performance may suffer!", to_model, key.first, key.second,coord_t(type)); + spdlog::warn( + "Had to calculate Avoidance (to model-bool: {}) at radius {} and layer {} and type {}, but precalculate was called. Performance may suffer!", + to_model, + key.first, + key.second, + coord_t(type)); } - if(type == AvoidanceType::COLLISION) + if (type == AvoidanceType::COLLISION) { calculateCollisionAvoidance(key); } @@ -528,7 +536,7 @@ const Polygons& TreeModelVolumes::getWallRestriction(coord_t radius, LayerIndex std::unordered_map* cache_ptr = min_xy_dist ? &wall_restrictions_cache_min_ : &wall_restrictions_cache_; { - std::lock_guard critical_section(min_xy_dist ? *critical_wall_restrictions_cache_min_ : *critical_wall_restrictions_cache_); + std::lock_guard critical_section(min_xy_dist ? *critical_wall_restrictions_cache_min_ : *critical_wall_restrictions_cache_); result = getArea(*cache_ptr, key); } if (result) @@ -604,8 +612,7 @@ LayerIndex TreeModelVolumes::getMaxCalculatedLayer(coord_t radius, const std::un void TreeModelVolumes::calculateCollision(const std::deque& keys) { - cura::parallel_for - ( + cura::parallel_for( 0, keys.size(), [&](const size_t i) @@ -627,8 +634,8 @@ void TreeModelVolumes::calculateCollision(const std::deque& key const LayerIndex max_anti_overhang_layer = anti_overhang_.size() - 1; const LayerIndex max_required_layer = keys[i].second + std::max(coord_t(1), z_distance_top_layers); const coord_t xy_distance = outline_idx == current_outline_idx ? current_min_xy_dist : layer_outlines_[outline_idx].first.get("support_xy_distance"); - // Technically this causes collision for the normal xy_distance to be larger by current_min_xy_dist_delta for all not currently processing meshes as this delta will be added at request time. - // Avoiding this would require saving each collision for each outline_idx separately, + // Technically this causes collision for the normal xy_distance to be larger by current_min_xy_dist_delta for all not currently processing meshes as this delta will + // be added at request time. Avoiding this would require saving each collision for each outline_idx separately, // and later for each avoidance... But avoidance calculation has to be for the whole scene and can NOT be done for each outline_idx separately and combined later. // So avoiding this inaccuracy seems infeasible as it would require 2x the avoidance calculations => 0.5x the performance. coord_t min_layer_bottom; @@ -649,7 +656,9 @@ void TreeModelVolumes::calculateCollision(const std::deque& key { collision_areas.add(layer_outlines_[outline_idx].second[layer_idx]); } - collision_areas = collision_areas.offset(radius + xy_distance); // jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model. + collision_areas = collision_areas.offset( + radius + + xy_distance); // jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model. data[key].add(collision_areas); // if a key does not exist when it is accessed it is added! } @@ -661,13 +670,14 @@ void TreeModelVolumes::calculateCollision(const std::deque& key { data[key].add(data[RadiusLayerPair(radius, layer_idx - layer_offset)]); } - //Placeable areas also have to be calculated when a collision has to be calculated if called outside of precalculate to prevent an infinite loop when they are invalidly requested... - if ((support_rests_on_this_model||precalculationFinished||!precalculated) && radius == 0 && layer_idx < coord_t(1 + keys[i].second)) + // Placeable areas also have to be calculated when a collision has to be calculated if called outside of precalculate to prevent an infinite loop when they are + // invalidly requested... + if ((support_rests_on_this_model || precalculationFinished || ! precalculated) && radius == 0 && layer_idx < coord_t(1 + keys[i].second)) { data[key] = data[key].unionPolygons(); Polygons above = data[RadiusLayerPair(radius, layer_idx + 1)]; above = above.unionPolygons(max_anti_overhang_layer >= layer_idx + 1 ? anti_overhang_[layer_idx] : Polygons()); - // Empty polygons on condition: Just to be sure the area is correctly unioned as otherwise difference may behave unexpectedly. + // Empty polygons on condition: Just to be sure the area is correctly unioned as otherwise difference may behave unexpectedly. Polygons placeable = data[key].unionPolygons().difference(above); data_placeable[RadiusLayerPair(radius, layer_idx + 1)] = data_placeable[RadiusLayerPair(radius, layer_idx + 1)].unionPolygons(placeable); @@ -678,7 +688,9 @@ void TreeModelVolumes::calculateCollision(const std::deque& key for (const auto layer_idx : ranges::views::iota(min_layer_bottom, max_required_layer + 1)) { key.second = layer_idx; - for (coord_t layer_offset = 1; layer_offset <= z_distance_top_layers && layer_offset + layer_idx < std::min(coord_t(layer_outlines_[outline_idx].second.size()), coord_t(max_required_layer + 1)); layer_offset++) + for (coord_t layer_offset = 1; layer_offset <= z_distance_top_layers + && layer_offset + layer_idx < std::min(coord_t(layer_outlines_[outline_idx].second.size()), coord_t(max_required_layer + 1)); + layer_offset++) { // If just the collision (including the xy distance) of the layers above is accumulated, it leads to the following issue: // Example: assuming the z distance is 2 layer @@ -693,20 +705,20 @@ void TreeModelVolumes::calculateCollision(const std::deque& key // If just the collision above is accumulated the overhang will get overwritten by the xy_distance of the layer below the overhang... // // This only causes issues if the overhang area is thinner than xy_distance - // Just accumulating areas of the model above without the xy distance is also problematic, as then support may get closer to the model (on the diagonal downwards) than the user intended. - // Example (s = support): + // Just accumulating areas of the model above without the xy distance is also problematic, as then support may get closer to the model (on the diagonal + // downwards) than the user intended. Example (s = support): // +-----+ // +-----+ // +-----+ // s+-----+ - // Technically the calculation below is off by one layer, as the actual distance between plastic one layer down is 0 not layer height, as this layer is filled with said plastic. - // But otherwise a part of the overhang that is expected to be supported is overwritten by the remaining part of the xy distance of the layer below the to be supported area. + // Technically the calculation below is off by one layer, as the actual distance between plastic one layer down is 0 not layer height, as this layer is + // filled with said plastic. But otherwise a part of the overhang that is expected to be supported is overwritten by the remaining part of the xy distance + // of the layer below the to be supported area. const coord_t required_range_x = coord_t(xy_distance - ((layer_offset - (z_distance_top_layers == 1 ? 0.5 : 0)) * xy_distance / z_distance_top_layers)); // ^^^ The conditional -0.5 ensures that plastic can never touch on the diagonal downward when the z_distance_top_layers = 1. // It is assumed to be better to not support an overhang<90� than to risk fusing to it. data[key].add(layer_outlines_[outline_idx].second[layer_idx + layer_offset].offset(radius + required_range_x)); - } data[key] = data[key].unionPolygons(max_anti_overhang_layer >= layer_idx ? anti_overhang_[layer_idx].offset(radius) : Polygons()); } @@ -752,8 +764,7 @@ void TreeModelVolumes::calculateCollision(const std::deque& key placeable_areas_cache_.insert(data_placeable_outer.begin(), data_placeable_outer.end()); } } - } - ); + }); } void TreeModelVolumes::calculateCollisionHolefree(const std::deque& keys) @@ -764,8 +775,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::deque - ( + cura::parallel_for( 0, LayerIndex(max_layer + 1), [&](const LayerIndex layer_idx) @@ -786,8 +796,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::deque critical_section(*critical_collision_cache_holefree_); collision_cache_holefree_.insert(data.begin(), data.end()); } - } - ); + }); } void TreeModelVolumes::calculateAccumulatedPlaceable0(const LayerIndex max_layer) @@ -801,14 +810,15 @@ void TreeModelVolumes::calculateAccumulatedPlaceable0(const LayerIndex max_layer { start_layer++; } - start_layer = std::max(LayerIndex { start_layer + 1 }, LayerIndex { 1 }); + start_layer = std::max(LayerIndex{ start_layer + 1 }, LayerIndex{ 1 }); } if (start_layer > max_layer) { spdlog::debug("Requested calculation for value already calculated ?"); return; } - Polygons accumulated_placeable_0 = start_layer == 1 ? machine_area_ : getAccumulatedPlaceable0(start_layer - 1).offset(FUDGE_LENGTH + (current_min_xy_dist + current_min_xy_dist_delta)); + Polygons accumulated_placeable_0 + = start_layer == 1 ? machine_area_ : getAccumulatedPlaceable0(start_layer - 1).offset(FUDGE_LENGTH + (current_min_xy_dist + current_min_xy_dist_delta)); // ^^^ The calculation here is done on the areas that are increased by xy_distance, but the result is saved without xy_distance, // so here it "restores" the previous state to continue calculating from about where it ended. // It would be better to ensure placeable areas of radius 0 do not include the xy distance, and removing the code compensating for it here and in calculatePlaceables. @@ -821,75 +831,73 @@ void TreeModelVolumes::calculateAccumulatedPlaceable0(const LayerIndex max_layer accumulated_placeable_0 = simplifier.polygon(accumulated_placeable_0); data[layer] = std::pair(layer, accumulated_placeable_0); } - cura::parallel_for - ( - std::max(LayerIndex{ start_layer - 1 }, LayerIndex{ 1 }), - data.size(), - [&](const coord_t layer_idx) - { - data[layer_idx].second = data[layer_idx].second.offset(-(current_min_xy_dist+current_min_xy_dist_delta)); - }); + cura::parallel_for( + std::max(LayerIndex{ start_layer - 1 }, LayerIndex{ 1 }), + data.size(), + [&](const coord_t layer_idx) + { + data[layer_idx].second = data[layer_idx].second.offset(-(current_min_xy_dist + current_min_xy_dist_delta)); + }); { - std::lock_guard critical_section(* critical_accumulated_placeables_cache_radius_0_); + std::lock_guard critical_section(*critical_accumulated_placeables_cache_radius_0_); accumulated_placeables_cache_radius_0_.insert(data.begin(), data.end()); } } - void TreeModelVolumes::calculateCollisionAvoidance(const std::deque& keys) { - cura::parallel_for - ( - 0, - keys.size(), - [&, keys](const size_t key_idx) + cura::parallel_for( + 0, + keys.size(), + [&, keys](const size_t key_idx) + { + const coord_t radius = keys[key_idx].first; + const LayerIndex max_required_layer = keys[key_idx].second; + const coord_t max_step_move = std::max(1.9 * radius, current_min_xy_dist * 1.9); + LayerIndex start_layer = 0; { + std::lock_guard critical_section(*critical_avoidance_cache_collision_); + start_layer = 1 + std::max(getMaxCalculatedLayer(radius, avoidance_cache_collision_), max_layer_idx_without_blocker); + } - const coord_t radius = keys[key_idx].first; - const LayerIndex max_required_layer = keys[key_idx].second; - const coord_t max_step_move = std::max(1.9 * radius, current_min_xy_dist * 1.9); - LayerIndex start_layer = 0; - { - std::lock_guard critical_section(*critical_avoidance_cache_collision_); - start_layer = 1 + std::max(getMaxCalculatedLayer(radius, avoidance_cache_collision_), max_layer_idx_without_blocker); - } + if (start_layer > max_required_layer) + { + return; + } - if(start_layer>max_required_layer) - { - return; - } + std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); + RadiusLayerPair key(radius, 0); - std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); - RadiusLayerPair key(radius, 0); + Polygons latest_avoidance = getAvoidance(radius, start_layer - 1, AvoidanceType::COLLISION, true, true); + for (const LayerIndex layer : ranges::views::iota(static_cast(start_layer), max_required_layer + 1UL)) + { + key.second = layer; + Polygons col = getCollision(radius, layer, true); + latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col); - Polygons latest_avoidance = getAvoidance(radius,start_layer-1,AvoidanceType::COLLISION,true,true); - for (const LayerIndex layer : ranges::views::iota(static_cast(start_layer), max_required_layer + 1UL)) - { - key.second = layer; - Polygons col = getCollision(radius, layer, true); - latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col); - - Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius,increase_until_radius), ClipperLib::jtRound); - latest_avoidance = latest_avoidance.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius,layer,true)); - - Polygons next_latest_avoidance = simplifier.polygon(latest_avoidance); - latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); - // ^^^ Ensure the simplification only causes the avoidance to become larger. - // If the deviation of the simplification causes the avoidance to become smaller than it should be it can cause issues, if it is larger the worst case is that the xy distance is effectively increased by deviation. - // If there would be an option to ensure the resulting polygon only gets larger by simplifying, it should improve performance further. - data[layer] = std::pair(key, latest_avoidance); - } + Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius), ClipperLib::jtRound); + latest_avoidance = latest_avoidance.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius, layer, true)); - { - std::lock_guard critical_section(* critical_avoidance_cache_collision_); - avoidance_cache_collision_.insert(data.begin(), data.end()); - } - }); + Polygons next_latest_avoidance = simplifier.polygon(latest_avoidance); + latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); + // ^^^ Ensure the simplification only causes the avoidance to become larger. + // If the deviation of the simplification causes the avoidance to become smaller than it should be it can cause issues, if it is larger the worst case is that the + // xy distance is effectively increased by deviation. If there would be an option to ensure the resulting polygon only gets larger by simplifying, it should improve + // performance further. + data[layer] = std::pair(key, latest_avoidance); + } + + { + std::lock_guard critical_section(*critical_avoidance_cache_collision_); + avoidance_cache_collision_.insert(data.begin(), data.end()); + } + }); } -// Ensures offsets are only done in sizes with a max step size per offset while adding the collision offset after each step, this ensures that areas cannot glitch through walls defined by the collision when offsetting to fast. +// Ensures offsets are only done in sizes with a max step size per offset while adding the collision offset after each step, this ensures that areas cannot glitch through walls +// defined by the collision when offsetting to fast. Polygons TreeModelVolumes::safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision) const { const size_t steps = std::abs(distance / std::max(min_offset_per_step_, std::abs(max_safe_step_distance))); @@ -909,9 +917,9 @@ void TreeModelVolumes::calculateAvoidance(const std::deque& key { // For every RadiusLayer pair there are 3 avoidances that have to be calculate, calculated in the same paralell_for loop for better parallelization. const std::vector all_types = { AvoidanceType::SLOW, AvoidanceType::FAST_SAFE, AvoidanceType::FAST }; - // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' one right now. - cura::parallel_for - ( + // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' + // one right now. + cura::parallel_for( 0, keys.size() * 3, [&, keys, all_types](const size_t iter_idx) @@ -949,7 +957,8 @@ void TreeModelVolumes::calculateAvoidance(const std::deque& key start_layer = std::max(start_layer, LayerIndex(1)); // Ensure StartLayer is at least 1 as if no avoidance was calculated getMaxCalculatedLayer returns -1 std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); - latest_avoidance = getAvoidance(radius, start_layer - 1, type, false, true); // minDist as the delta was already added, also avoidance for layer 0 will return the collision. + latest_avoidance + = getAvoidance(radius, start_layer - 1, type, false, true); // minDist as the delta was already added, also avoidance for layer 0 will return the collision. // ### main loop doing the calculation for (const LayerIndex layer : ranges::views::iota(static_cast(start_layer), max_required_layer + 1UL)) @@ -969,8 +978,9 @@ void TreeModelVolumes::calculateAvoidance(const std::deque& key Polygons next_latest_avoidance = simplifier.polygon(latest_avoidance); latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); // ^^^ Ensure the simplification only causes the avoidance to become larger. - // If the deviation of the simplification causes the avoidance to become smaller than it should be it can cause issues, if it is larger the worst case is that the xy distance is effectively increased by deviation. - // If there would be an option to ensure the resulting polygon only gets larger by simplifying, it should improve performance further. + // If the deviation of the simplification causes the avoidance to become smaller than it should be it can cause issues, if it is larger the worst case is that the + // xy distance is effectively increased by deviation. If there would be an option to ensure the resulting polygon only gets larger by simplifying, it should improve + // performance further. data[layer] = std::pair(key, latest_avoidance); } @@ -988,15 +998,14 @@ void TreeModelVolumes::calculateAvoidance(const std::deque& key std::lock_guard critical_section(*(slow ? critical_avoidance_cache_slow_ : holefree ? critical_avoidance_cache_holefree_ : critical_avoidance_cache_)); (slow ? avoidance_cache_slow_ : holefree ? avoidance_cache_hole_ : avoidance_cache_).insert(data.begin(), data.end()); } - } - ); + }); } void TreeModelVolumes::calculatePlaceables(const std::deque& keys) { - // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' one right now. - cura::parallel_for - ( + // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' + // one right now. + cura::parallel_for( 0, keys.size(), [&, keys](const size_t key_idx) @@ -1046,8 +1055,7 @@ void TreeModelVolumes::calculatePlaceables(const std::deque& ke std::lock_guard critical_section(*critical_placeable_areas_cache_); placeable_areas_cache_.insert(data.begin(), data.end()); } - } - ); + }); } @@ -1055,9 +1063,9 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque all_types = { AvoidanceType::SLOW, AvoidanceType::FAST_SAFE, AvoidanceType::FAST }; - // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' one right now. - cura::parallel_for - ( + // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' + // one right now. + cura::parallel_for( 0, keys.size() * 3, [&, keys, all_types](const size_t iter_idx) @@ -1085,7 +1093,10 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque critical_section(*(slow ? critical_avoidance_cache_to_model_slow_ : holefree ? critical_avoidance_cache_holefree_to_model_ : critical_avoidance_cache_to_model_)); + std::lock_guard critical_section( + *(slow ? critical_avoidance_cache_to_model_slow_ + : holefree ? critical_avoidance_cache_holefree_to_model_ + : critical_avoidance_cache_to_model_)); start_layer = 1 + getMaxCalculatedLayer(radius, slow ? avoidance_cache_to_model_slow_ : holefree ? avoidance_cache_hole_to_model_ : avoidance_cache_to_model_); } if (start_layer > max_required_layer) @@ -1094,7 +1105,8 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque(start_layer), max_required_layer + 1)) @@ -1115,8 +1127,9 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque(key, latest_avoidance); } @@ -1131,18 +1144,20 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque critical_section(*(slow ? critical_avoidance_cache_to_model_slow_ : holefree ? critical_avoidance_cache_holefree_to_model_ : critical_avoidance_cache_to_model_)); + std::lock_guard critical_section( + *(slow ? critical_avoidance_cache_to_model_slow_ + : holefree ? critical_avoidance_cache_holefree_to_model_ + : critical_avoidance_cache_to_model_)); (slow ? avoidance_cache_to_model_slow_ : holefree ? avoidance_cache_hole_to_model_ : avoidance_cache_to_model_).insert(data.begin(), data.end()); } - } - ); + }); } void TreeModelVolumes::calculateWallRestrictions(const std::deque& keys) { - // Wall restrictions are mainly important when they represent actual walls that are printed, and not "just" the configured z_distance, because technically valid placement is no excuse for moving through a wall. - // As they exist to prevent accidentially moving though a wall at high speed between layers like thie (x = wall,i = influence area,o= empty space,d = blocked area because of z distance) - // Assume maximum movement distance is two characters and maximum safe movement distance of one character + // Wall restrictions are mainly important when they represent actual walls that are printed, and not "just" the configured z_distance, because technically valid placement is no + // excuse for moving through a wall. As they exist to prevent accidentially moving though a wall at high speed between layers like thie (x = wall,i = influence area,o= empty + // space,d = blocked area because of z distance) Assume maximum movement distance is two characters and maximum safe movement distance of one character /* Potential issue addressed by the wall restrictions: Influence area may lag through a wall * layer z+1:iiiiiiiiiiioooo @@ -1150,7 +1165,8 @@ void TreeModelVolumes::calculateWallRestrictions(const std::deque - ( + // FIXME: This should be a parallel-for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' + // one right now. + cura::parallel_for( 0, keys.size(), [&, keys](const size_t key_idx) @@ -1201,7 +1217,8 @@ void TreeModelVolumes::calculateWallRestrictions(const std::deque 0) { @@ -1219,8 +1236,7 @@ void TreeModelVolumes::calculateWallRestrictions(const std::deque critical_section(*critical_wall_restrictions_cache_min_); wall_restrictions_cache_min_.insert(data_min.begin(), data_min.end()); } - } - ); + }); } coord_t TreeModelVolumes::ceilRadius(coord_t radius) const @@ -1240,7 +1256,7 @@ coord_t TreeModelVolumes::ceilRadius(coord_t radius) const for (const auto step : ranges::views::iota(0UL, SUPPORT_TREE_PRE_EXPONENTIAL_STEPS)) { result += stepsize; - if (result >= radius && !ignorable_radii_.count(result)) + if (result >= radius && ! ignorable_radii_.count(result)) { return result; } @@ -1253,7 +1269,7 @@ coord_t TreeModelVolumes::ceilRadius(coord_t radius) const return exponential_result; } -template +template const std::optional> TreeModelVolumes::getArea(const std::unordered_map& cache, const KEY key) const { const auto it = cache.find(key); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 5dd76000f3..1103f693f7 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -2,55 +2,64 @@ // CuraEngine is released under the terms of the AGPLv3 or higher #include "TreeSupportTipGenerator.h" + #include "Application.h" //To get settings. +#include "TreeSupportUtils.h" #include "infill/SierpinskiFillProvider.h" #include "settings/EnumSettings.h" -#include "utils/algorithm.h" #include "utils/Simplify.h" +#include "utils/ThreadPool.h" +#include "utils/algorithm.h" #include "utils/math.h" //For round_up_divide and PI. #include "utils/polygonUtils.h" //For moveInside. -#include "utils/ThreadPool.h" -#include "TreeSupportUtils.h" -#include -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include namespace cura { -TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_s): - config(mesh.settings), - use_fake_roof(!mesh.settings.get("support_roof_enable")), - minimum_roof_area(!use_fake_roof? mesh.settings.get("minimum_roof_area"): SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA), - minimum_support_area(mesh.settings.get("minimum_support_area")), - support_roof_layers(mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config.layer_height) : use_fake_roof ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS : 0), - connect_length((config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config.min_radius - 1.0 * config.support_line_width, 0.0)), - support_tree_branch_distance((config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length), - support_roof_line_distance( use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) : mesh.settings.get("support_roof_line_distance")), //todo propper - support_outset(0), // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") - roof_outset(use_fake_roof ? support_outset: mesh.settings.get("support_roof_offset")), - force_tip_to_roof((config.min_radius * config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && support_roof_layers && !use_fake_roof), - support_tree_limit_branch_reach(mesh.settings.get("support_tree_limit_branch_reach")), - support_tree_branch_reach_limit(support_tree_limit_branch_reach ? mesh.settings.get("support_tree_branch_reach_limit") : 0), - z_distance_delta(std::min(config.z_distance_top_layers+1,mesh.overhang_areas.size())), - xy_overrides(config.support_overrides == SupportDistPriority::XY_OVERRIDES_Z), - tip_roof_size(force_tip_to_roof?config.min_radius * config.min_radius * M_PI:0), - already_inserted(mesh.overhang_areas.size()), - support_roof_drawn(mesh.overhang_areas.size(),Polygons()), - roof_tips_drawn(mesh.overhang_areas.size(),Polygons()), - volumes_(volumes_s), - force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) +TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_s) + : config(mesh.settings) + , use_fake_roof(! mesh.settings.get("support_roof_enable")) + , minimum_roof_area(! use_fake_roof ? mesh.settings.get("minimum_roof_area") : SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA) + , minimum_support_area(mesh.settings.get("minimum_support_area")) + , support_roof_layers( + mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config.layer_height) + : use_fake_roof ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS + : 0) + , connect_length( + (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config.min_radius - 1.0 * config.support_line_width, 0.0)) + , support_tree_branch_distance((config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length) + , support_roof_line_distance( + use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) + * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + : mesh.settings.get("support_roof_line_distance")) + , // todo propper + support_outset(0) + , // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") + roof_outset(use_fake_roof ? support_outset : mesh.settings.get("support_roof_offset")) + , force_tip_to_roof((config.min_radius * config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && support_roof_layers && ! use_fake_roof) + , support_tree_limit_branch_reach(mesh.settings.get("support_tree_limit_branch_reach")) + , support_tree_branch_reach_limit(support_tree_limit_branch_reach ? mesh.settings.get("support_tree_branch_reach_limit") : 0) + , z_distance_delta(std::min(config.z_distance_top_layers + 1, mesh.overhang_areas.size())) + , xy_overrides(config.support_overrides == SupportDistPriority::XY_OVERRIDES_Z) + , tip_roof_size(force_tip_to_roof ? config.min_radius * config.min_radius * M_PI : 0) + , already_inserted(mesh.overhang_areas.size()) + , support_roof_drawn(mesh.overhang_areas.size(), Polygons()) + , roof_tips_drawn(mesh.overhang_areas.size(), Polygons()) + , volumes_(volumes_s) + , force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) { - const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); @@ -61,9 +70,10 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage else { max_overhang_insert_lag = std::max((size_t)round_up_divide(config.xy_distance, max_overhang_speed / 2), 2 * config.z_distance_top_layers); - // ^^^ Cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point may cause extra material and time cost. - // Could also be an user setting or differently calculated. Idea is that if an overhang does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. - // The 2*z_distance_delta is only a catch for when the support angle is very high. + // ^^^ Cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point may cause extra material and + // time cost. + // Could also be an user setting or differently calculated. Idea is that if an overhang does not turn valid in double the amount of layers a slope of support angle + // would take to travel xy_distance, nothing reasonable will come from it. The 2*z_distance_delta is only a catch for when the support angle is very high. } cross_fill_provider = generateCrossFillProvider(mesh, support_tree_branch_distance, config.support_line_width); @@ -79,9 +89,9 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage coord_t dtt_when_tips_can_merge = 1; - if(config.branch_radius * config.diameter_angle_scale_factor < 2 * config.maximum_move_distance_slow) + if (config.branch_radius * config.diameter_angle_scale_factor < 2 * config.maximum_move_distance_slow) { - while((2 * config.maximum_move_distance_slow * dtt_when_tips_can_merge - config.support_line_width) < config.getRadius(dtt_when_tips_can_merge)) + while ((2 * config.maximum_move_distance_slow * dtt_when_tips_can_merge - config.support_line_width) < config.getRadius(dtt_when_tips_can_merge)) { dtt_when_tips_can_merge++; } @@ -97,8 +107,10 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage std::vector TreeSupportTipGenerator::convertLinesToInternal(Polygons polylines, LayerIndex layer_idx) { // NOTE: The volumes below (on which '.inside(p, true)' is called each time below) are the same each time. The values being calculated here are strictly local as well. - // So they could in theory be pre-calculated here (outside of the loop). However, when I refatored it to be that way, it seemed to cause deadlocks each time for some settings. - // NOTE2: When refactoring ensure that avoidance to buildplate is only requested when support_rest_preference == RestPreference::BUILDPLATE as otherwise it has not been precalculated (causing long delays while it is calculated when requested here). + // So they could in theory be pre-calculated here (outside of the loop). However, when I refatored it to be that way, it seemed to cause deadlocks each time for some + // settings. + // NOTE2: When refactoring ensure that avoidance to buildplate is only requested when support_rest_preference == RestPreference::BUILDPLATE as otherwise it has not been + // precalculated (causing long delays while it is calculated when requested here). std::vector result; // Also checks if the position is valid, if it is NOT, it deletes that point @@ -107,23 +119,26 @@ std::vector TreeSupportTipGenerator::c LineInformation res_line; for (const Point& p : line) { - if (config.support_rest_preference == RestPreference::BUILDPLATE && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST_SAFE, false, !xy_overrides).inside(p, true)) + if (config.support_rest_preference == RestPreference::BUILDPLATE + && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST_SAFE, false, ! xy_overrides).inside(p, true)) { res_line.emplace_back(p, LineStatus::TO_BP_SAFE); } - else if (config.support_rest_preference == RestPreference::BUILDPLATE && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST, false, !xy_overrides).inside(p, true)) + else if ( + config.support_rest_preference == RestPreference::BUILDPLATE + && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST, false, ! xy_overrides).inside(p, true)) { res_line.emplace_back(p, LineStatus::TO_BP); } - else if (config.support_rests_on_model && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST_SAFE, true, !xy_overrides).inside(p, true)) + else if (config.support_rests_on_model && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST_SAFE, true, ! xy_overrides).inside(p, true)) { res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS_SAFE); } - else if (config.support_rests_on_model && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST, true, !xy_overrides).inside(p, true)) + else if (config.support_rests_on_model && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::FAST, true, ! xy_overrides).inside(p, true)) { res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS); } - else if (config.support_rests_on_model && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::COLLISION, true, !xy_overrides).inside(p, true)) + else if (config.support_rests_on_model && ! volumes_.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::COLLISION, true, ! xy_overrides).inside(p, true)) { res_line.emplace_back(p, LineStatus::TO_MODEL); } @@ -133,7 +148,7 @@ std::vector TreeSupportTipGenerator::c res_line.clear(); } } - if (!res_line.empty()) + if (! res_line.empty()) { result.emplace_back(res_line); res_line.clear(); @@ -160,10 +175,17 @@ Polygons TreeSupportTipGenerator::convertInternalToLines(std::vector)> TreeSupportTipGenerator::getEvaluatePointForNextLayerFunction(size_t current_layer) { - std::function)> evaluatePoint = - [=](std::pair p) + std::function)> evaluatePoint = [=](std::pair p) { - if (config.support_rest_preference != RestPreference::GRACEFUL && ! volumes_.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, false, !xy_overrides).inside(p.first, true)) + if (config.support_rest_preference != RestPreference::GRACEFUL + && ! volumes_ + .getAvoidance( + config.getRadius(0), + current_layer - 1, + p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, + false, + ! xy_overrides) + .inside(p.first, true)) { return true; } @@ -171,11 +193,18 @@ std::function)> TreeS { if (p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE) { - return ! volumes_.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, true, !xy_overrides).inside(p.first, true); + return ! volumes_ + .getAvoidance( + config.getRadius(0), + current_layer - 1, + p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, + true, + ! xy_overrides) + .inside(p.first, true); } else { - return ! volumes_.getAvoidance(config.getRadius(0), current_layer - 1, AvoidanceType::COLLISION, true, !xy_overrides).inside(p.first, true); + return ! volumes_.getAvoidance(config.getRadius(0), current_layer - 1, AvoidanceType::COLLISION, true, ! xy_overrides).inside(p.first, true); } } return false; @@ -183,7 +212,9 @@ std::function)> TreeS return evaluatePoint; } -std::pair, std::vector> TreeSupportTipGenerator::splitLines(std::vector lines, std::function)> evaluatePoint) +std::pair, std::vector> TreeSupportTipGenerator::splitLines( + std::vector lines, + std::function)> evaluatePoint) { // Assumes all Points on the current line are valid. @@ -205,7 +236,7 @@ std::pair, std::vector, std::vector>>, std::vector>>>(keep, set_free); + return std::pair< + std::vector>>, + std::vector>>>(keep, set_free); } -Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& input, coord_t distance, size_t min_points, bool enforce_distance) const +Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& input, coord_t distance, size_t min_points, bool enforce_distance) const { Polygons result; for (auto part : input) @@ -242,9 +275,9 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& if (part.front() == part.back()) { size_t optimal_start_index = 0; - // If the polyline was a polygon, there is a high chance it was an overhang. Overhangs that are <60 degree tend to be very thin areas, so lets get the beginning and end of them and ensure that they are supported. - // The first point of the line will always be supported, so rotate the order of points in this polyline that one of the two corresponding points that are furthest from each other is in the beginning. - // The other will be manually added (optimal_end_index) + // If the polyline was a polygon, there is a high chance it was an overhang. Overhangs that are <60 degree tend to be very thin areas, so lets get the beginning and + // end of them and ensure that they are supported. The first point of the line will always be supported, so rotate the order of points in this polyline that one of + // the two corresponding points that are furthest from each other is in the beginning. The other will be manually added (optimal_end_index) coord_t max_dist2_between_vertices = 0; for (auto [idx, p_a] : part | ranges::views::enumerate | ranges::views::drop_last(1)) { @@ -270,13 +303,14 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& line.add(part[0]); bool should_add_endpoint = min_points > 1 || vSize2(part[0] - part[optimal_end_index]) > (current_distance * current_distance); - bool added_endpoint = !should_add_endpoint; // If no endpoint should be added all endpoints are already added. + bool added_endpoint = ! should_add_endpoint; // If no endpoint should be added all endpoints are already added. size_t current_index = 0; GivenDistPoint next_point; coord_t next_distance = current_distance; // Get points so that at least min_points are added and they each are current_distance away from each other. If that is impossible, decrease current_distance a bit. - // (Regarding the while-loop) The input are lines, that means that the line from the last to the first vertex does not have to exist, so exclude all points that are on this line! + // (Regarding the while-loop) The input are lines, that means that the line from the last to the first vertex does not have to exist, so exclude all points that are + // on this line! while (PolygonUtils::getNextPointWithDistance(current_point, next_distance, part, current_index, 0, next_point) && next_point.pos < coord_t(part.size()) - 1) { if (! added_endpoint && next_point.pos >= optimal_end_index) @@ -292,14 +326,14 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& // So this ensures that the points are actually a certain distance from each other. // This assurance is only made on a per polygon basis, as different but close polygon may not be able to use support below the other polygon. coord_t min_distance_to_existing_point_sqd = std::numeric_limits::max(); - if(enforce_distance) + if (enforce_distance) { for (Point p : line) { min_distance_to_existing_point_sqd = std::min(min_distance_to_existing_point_sqd, vSize2(p - next_point.location)); } } - if (!enforce_distance || min_distance_to_existing_point_sqd >= (current_distance * current_distance)) + if (! enforce_distance || min_distance_to_existing_point_sqd >= (current_distance * current_distance)) { // viable point was found. Add to possible result. line.add(next_point.location); @@ -312,7 +346,9 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& if (current_point == next_point.location) { // In case a fixpoint is encountered, better aggressively overcompensate so the code does not become stuck here... - spdlog::warn("Tree Support: Encountered a fixpoint in getNextPointWithDistance. This is expected to happen if the distance (currently {}) is smaller than 100", next_distance); + spdlog::warn( + "Tree Support: Encountered a fixpoint in getNextPointWithDistance. This is expected to happen if the distance (currently {}) is smaller than 100", + next_distance); if (next_distance > 2 * current_distance) { // This case should never happen, but better safe than sorry. @@ -344,7 +380,6 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& SierpinskiFillProvider* TreeSupportTipGenerator::generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const { - if (config.support_pattern == EFillMethod::CROSS || config.support_pattern == EFillMethod::CROSS_3D) { AABB3D aabb; @@ -374,200 +409,231 @@ SierpinskiFillProvider* TreeSupportTipGenerator::generateCrossFillProvider(const return nullptr; } -void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof ) +void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof) { - std::mutex critical; - //this is ugly, but as far as i can see there is not way to ensure a parallel_for loop is calculated for each iteration up to a certain point before it continues; - cura::parallel_for - ( - 1, - mesh.overhang_areas.size() - z_distance_delta, - [&](const LayerIndex layer_idx) + // this is ugly, but as far as i can see there is not way to ensure a parallel_for loop is calculated for each iteration up to a certain point before it continues; + cura::parallel_for( + 1, + mesh.overhang_areas.size() - z_distance_delta, + [&](const LayerIndex layer_idx) + { + if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() || result.size() < layer_idx) { - if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() || result.size()= 1 && !remaining_overhang.empty(); lag_ctr++) + Polygons relevant_forbidden = volumes_.getCollision(roof ? 0 : config.getRadius(0), layer_idx, ! xy_overrides); + // ^^^ Take the least restrictive avoidance possible + + // Technically this also makes support blocker smaller, which is wrong as they do not have a xy_distance, but it should be good enough. + Polygons model_outline = volumes_.getCollision(0, layer_idx, ! xy_overrides).offset(-config.xy_min_distance, ClipperLib::jtRound); + + Polygons overhang_regular = TreeSupportUtils::safeOffsetInc( + mesh.overhang_areas[layer_idx + z_distance_delta], + roof ? roof_outset : support_outset, + relevant_forbidden, + config.min_radius * 1.75 + config.xy_min_distance, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + Polygons remaining_overhang = mesh.overhang_areas[layer_idx + z_distance_delta] + .offset(roof ? roof_outset : support_outset) + .difference(overhang_regular) + .intersection(relevant_forbidden) + .difference(model_outline); + for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && layer_idx - coord_t(lag_ctr) >= 1 && ! remaining_overhang.empty(); lag_ctr++) + { { - { - std::lock_guard critical_section_storage(critical); - result[layer_idx-lag_ctr].add(remaining_overhang); - } - - Polygons relevant_forbidden_below = - volumes_.getCollision(roof ? 0 : config.getRadius(0), layer_idx - lag_ctr, ! xy_overrides).offset(EPSILON); - remaining_overhang=remaining_overhang.intersection(relevant_forbidden_below).unionPolygons().difference(model_outline); + std::lock_guard critical_section_storage(critical); + result[layer_idx - lag_ctr].add(remaining_overhang); } - } - ); - cura::parallel_for - ( - 0, - result.size(), - [&](const LayerIndex layer_idx) - { - result[layer_idx]=result[layer_idx].unionPolygons(); + Polygons relevant_forbidden_below = volumes_.getCollision(roof ? 0 : config.getRadius(0), layer_idx - lag_ctr, ! xy_overrides).offset(EPSILON); + remaining_overhang = remaining_overhang.intersection(relevant_forbidden_below).unionPolygons().difference(model_outline); } - ); - - + }); + cura::parallel_for( + 0, + result.size(), + [&](const LayerIndex layer_idx) + { + result[layer_idx] = result[layer_idx].unionPolygons(); + }); } void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& mesh) { - std::vector potential_support_roofs(mesh.overhang_areas.size(),Polygons()); + std::vector potential_support_roofs(mesh.overhang_areas.size(), Polygons()); std::mutex critical_potential_support_roofs; - std::vector dropped_overhangs(mesh.overhang_areas.size(),Polygons()); + std::vector dropped_overhangs(mesh.overhang_areas.size(), Polygons()); if (xy_overrides) { dropOverhangAreas(mesh, dropped_overhangs, true); } - cura::parallel_for - ( - 0, - mesh.overhang_areas.size() - z_distance_delta, - [&](const LayerIndex layer_idx) + cura::parallel_for( + 0, + mesh.overhang_areas.size() - z_distance_delta, + [&](const LayerIndex layer_idx) + { + if (mesh.overhang_areas[layer_idx + z_distance_delta].empty()) { - if (mesh.overhang_areas[layer_idx + z_distance_delta].empty()) + return; // This is a continue if imagined in a loop context. + } + + // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with + // -radius. This is intentional here, as support roof is still valid if only a part of the tip may reach it. + Polygons forbidden_here = volumes_ + .getAvoidance( + config.getRadius(0), + layer_idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + ! xy_overrides) + .offset(-config.getRadius(0), ClipperLib::jtRound); + + // todo Since arachnea the assumption that an area smaller then line_width is not printed is no longer true all such safeOffset should have config.support_line_width + // replaced with another setting. It should still work in most cases, but it should be possible to create a situation where a overhang outset lags though a wall. I will + // take a look at this later. + Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( + mesh.full_overhang_areas[layer_idx + z_distance_delta].unionPolygons(dropped_overhangs[layer_idx]), + roof_outset, + forbidden_here, + config.support_line_width, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + + for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) + { + const Polygons forbidden_next = volumes_ + .getAvoidance( + config.getRadius(0), + layer_idx - (dtt_roof + 1), + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + ! xy_overrides) + .offset(-config.getRadius(0), ClipperLib::jtRound); + + full_overhang_area = full_overhang_area.difference(forbidden_next); + + if (force_minimum_roof_area) { - return; // This is a continue if imagined in a loop context. + full_overhang_area.removeSmallAreas(minimum_roof_area); } - // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with -radius. - // This is intentional here, as support roof is still valid if only a part of the tip may reach it. - Polygons forbidden_here = - volumes_.getAvoidance(config.getRadius(0), layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides).offset(-config.getRadius(0),ClipperLib::jtRound); - - // todo Since arachnea the assumption that an area smaller then line_width is not printed is no longer true all such safeOffset should have config.support_line_width replaced with another setting. It should still work in most cases, but it should be possible to create a situation where a overhang outset lags though a wall. - // I will take a look at this later. - Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc(mesh.full_overhang_areas[layer_idx + z_distance_delta].unionPolygons(dropped_overhangs[layer_idx]),roof_outset,forbidden_here, config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - - for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) + if (full_overhang_area.area() > EPSILON) { - const Polygons forbidden_next = - volumes_.getAvoidance(config.getRadius(0), layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides).offset(-config.getRadius(0),ClipperLib::jtRound); - - full_overhang_area = full_overhang_area.difference(forbidden_next); - - if(force_minimum_roof_area) - { - full_overhang_area.removeSmallAreas(minimum_roof_area); - } - - if (full_overhang_area.area()>EPSILON) - { - std::lock_guard critical_section_potential_support_roofs(critical_potential_support_roofs); - potential_support_roofs[layer_idx-dtt_roof].add((full_overhang_area)); - } - else - { - break; - } + std::lock_guard critical_section_potential_support_roofs(critical_potential_support_roofs); + potential_support_roofs[layer_idx - dtt_roof].add((full_overhang_area)); + } + else + { + break; } - } - ); + }); - cura::parallel_for - ( + cura::parallel_for( 0, potential_support_roofs.size(), [&](const LayerIndex layer_idx) { - // Now, because the avoidance/collision was subtracted above, the overhang parts that are of xy distance were removed, so to merge areas that should have been one offset by xy_min_distance and then undo it. - // In a perfect world the offset here would be of a mode that makes sure that area.offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance) = area if there is only one polygon in said area. - // I have not encountered issues with using the default mitered here. Could be that i just have not encountered an issue with it yet though. - potential_support_roofs[layer_idx]=potential_support_roofs[layer_idx].unionPolygons().offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance).unionPolygons(potential_support_roofs[layer_idx]); - - } - ); + // Now, because the avoidance/collision was subtracted above, the overhang parts that are of xy distance were removed, so to merge areas that should have been one + // offset by xy_min_distance and then undo it. In a perfect world the offset here would be of a mode that makes sure that + // area.offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance) = area if there is only one polygon in said area. I have not encountered issues + // with using the default mitered here. Could be that i just have not encountered an issue with it yet though. + potential_support_roofs[layer_idx] = potential_support_roofs[layer_idx] + .unionPolygons() + .offset(config.xy_min_distance) + .unionPolygons() + .offset(-config.xy_min_distance) + .unionPolygons(potential_support_roofs[layer_idx]); + }); - std::vector additional_support_roofs(mesh.overhang_areas.size(),Polygons()); + std::vector additional_support_roofs(mesh.overhang_areas.size(), Polygons()); - cura::parallel_for - ( - 0, - potential_support_roofs.size(), - [&](const LayerIndex layer_idx) + cura::parallel_for( + 0, + potential_support_roofs.size(), + [&](const LayerIndex layer_idx) + { + if (! potential_support_roofs[layer_idx].empty()) { - if (!potential_support_roofs[layer_idx].empty()) + // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with + // -radius. This is intentional here, as support roof is still valid if only a part of the tip may reach it. + Polygons forbidden_here = volumes_ + .getAvoidance( + config.getRadius(0), + layer_idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + ! xy_overrides) + .offset(-(config.getRadius(0)), ClipperLib::jtRound); + + if (! force_minimum_roof_area) { - // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with -radius. - // This is intentional here, as support roof is still valid if only a part of the tip may reach it. - Polygons forbidden_here = - volumes_.getAvoidance(config.getRadius(0), layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides).offset(-(config.getRadius(0)),ClipperLib::jtRound); + Polygons fuzzy_area = Polygons(); - if (!force_minimum_roof_area) + // the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof further up/down. + // This prevents the situation where a roof gets removed even tough its area would contribute to a (better) printable roof area further down. + for (const LayerIndex layer_offset : ranges::views::iota( + -LayerIndex{ std::min(layer_idx, LayerIndex{ support_roof_layers }) }, + LayerIndex{ std::min(LayerIndex{ potential_support_roofs.size() - layer_idx }, LayerIndex{ support_roof_layers + 1 }) })) { - Polygons fuzzy_area = Polygons(); - - // the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof further up/down. - // This prevents the situation where a roof gets removed even tough its area would contribute to a (better) printable roof area further down. - for (const LayerIndex layer_offset : ranges::views::iota(-LayerIndex { std::min(layer_idx, LayerIndex { support_roof_layers }) }, LayerIndex { std::min(LayerIndex { potential_support_roofs.size() - layer_idx }, LayerIndex{ support_roof_layers + 1} ) })) - { - fuzzy_area.add(support_roof_drawn[layer_idx+layer_offset]); - fuzzy_area.add(potential_support_roofs[layer_idx+layer_offset]); - } - fuzzy_area=fuzzy_area.unionPolygons(); - fuzzy_area.removeSmallAreas(std::max(minimum_roof_area,tip_roof_size)); + fuzzy_area.add(support_roof_drawn[layer_idx + layer_offset]); + fuzzy_area.add(potential_support_roofs[layer_idx + layer_offset]); + } + fuzzy_area = fuzzy_area.unionPolygons(); + fuzzy_area.removeSmallAreas(std::max(minimum_roof_area, tip_roof_size)); - for (Polygons potential_roof:potential_support_roofs[layer_idx].difference(forbidden_here).splitIntoParts()) + for (Polygons potential_roof : potential_support_roofs[layer_idx].difference(forbidden_here).splitIntoParts()) + { + if (! potential_roof.intersection(fuzzy_area).empty()) { - - if (!potential_roof.intersection(fuzzy_area).empty()) - { - additional_support_roofs[layer_idx].add(potential_roof); - } - + additional_support_roofs[layer_idx].add(potential_roof); } } - else - { - Polygons valid_roof = potential_support_roofs[layer_idx].difference(forbidden_here); - valid_roof.removeSmallAreas(std::max(minimum_roof_area,tip_roof_size)); - additional_support_roofs[layer_idx].add(valid_roof); - } + } + else + { + Polygons valid_roof = potential_support_roofs[layer_idx].difference(forbidden_here); + valid_roof.removeSmallAreas(std::max(minimum_roof_area, tip_roof_size)); + additional_support_roofs[layer_idx].add(valid_roof); } } - ); - - cura::parallel_for - ( - 0, - additional_support_roofs.size(), - [&](const LayerIndex layer_idx) - { - support_roof_drawn[layer_idx] = support_roof_drawn[layer_idx].unionPolygons(additional_support_roofs[layer_idx]); - } - ); + }); + cura::parallel_for( + 0, + additional_support_roofs.size(), + [&](const LayerIndex layer_idx) + { + support_roof_drawn[layer_idx] = support_roof_drawn[layer_idx].unionPolygons(additional_support_roofs[layer_idx]); + }); } -void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation, std::vector additional_ovalization_targets) +void TreeSupportTipGenerator::addPointAsInfluenceArea( + std::vector>& move_bounds, + std::pair p, + size_t dtt, + LayerIndex insert_layer, + size_t dont_move_until, + bool roof, + bool skip_ovalisation, + std::vector additional_ovalization_targets) { const bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; const bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; const bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; - if (! config.support_rests_on_model && !to_bp) + if (! config.support_rests_on_model && ! to_bp) { spdlog::warn("Tried to add an invalid support point"); return; @@ -585,23 +651,20 @@ void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vectorarea = new Polygons(area); for (Point p : additional_ovalization_targets) @@ -615,9 +678,17 @@ void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, size_t dont_move_until, bool connect_points) +void TreeSupportTipGenerator::addLinesAsInfluenceAreas( + std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + bool supports_roof, + size_t dont_move_until, + bool connect_points) { - // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a certain roof area will actually have lines. + // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a + // certain roof area will actually have lines. size_t dtt_roof_tip = 0; if (config.support_roof_wall_count == 0) { @@ -631,11 +702,12 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector, std::vector> split = - splitLines(lines, getEvaluatePointForNextLayerFunction(insert_layer_idx - dtt_roof_tip)); // Keep all lines that are still valid on the next layer. + std::pair, std::vector> split + = splitLines(lines, getEvaluatePointForNextLayerFunction(insert_layer_idx - dtt_roof_tip)); // Keep all lines that are still valid on the next layer. for (LineInformation line : split.second) // Add all points that would not be valid. { @@ -683,8 +755,9 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector additional_ovalization_targets; @@ -699,305 +772,380 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, - dtt_roof_tip != 0 || supports_roof, disable_ovalization, - additional_ovalization_targets - ); + addPointAsInfluenceArea( + move_bounds, + point_data, + 0, + insert_layer_idx - dtt_roof_tip, + dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, + dtt_roof_tip != 0 || supports_roof, + disable_ovalization, + additional_ovalization_targets); } } } -void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector>& move_bounds,SliceDataStorage& storage, std::vector& additional_support_areas) +void TreeSupportTipGenerator::removeUselessAddedPoints( + std::vector>& move_bounds, + SliceDataStorage& storage, + std::vector& additional_support_areas) { - - cura::parallel_for - ( - 0, - move_bounds.size(), - [&](const LayerIndex layer_idx) + cura::parallel_for( + 0, + move_bounds.size(), + [&](const LayerIndex layer_idx) + { + if (layer_idx + 1 < storage.support.supportLayers.size()) { + std::vector to_be_removed; + Polygons roof_on_layer_above = use_fake_roof ? support_roof_drawn[layer_idx + 1] + : storage.support.supportLayers[layer_idx + 1].support_roof.unionPolygons(additional_support_areas[layer_idx + 1]); + Polygons roof_on_layer + = use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof.unionPolygons(additional_support_areas[layer_idx]); - if(layer_idx+1 < storage.support.supportLayers.size()) + for (TreeSupportElement* elem : move_bounds[layer_idx]) { - std::vector to_be_removed; - Polygons roof_on_layer_above = use_fake_roof ? support_roof_drawn[layer_idx+1] : storage.support.supportLayers[layer_idx+1].support_roof.unionPolygons(additional_support_areas[layer_idx+1]); - Polygons roof_on_layer = use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof.unionPolygons(additional_support_areas[layer_idx]); - - for (TreeSupportElement* elem : move_bounds[layer_idx]) + if (roof_on_layer.inside(elem->result_on_layer)) // Remove branches that start inside of support interface { - if (roof_on_layer.inside(elem->result_on_layer)) // Remove branches that start inside of support interface + to_be_removed.emplace_back(elem); + } + else if (elem->supports_roof) + { + Point from = elem->result_on_layer; + PolygonUtils::moveInside(roof_on_layer_above, from); + // Remove branches should have interface above them, but dont. Should never happen. + if (roof_on_layer_above.empty() + || (! roof_on_layer_above.inside(elem->result_on_layer) + && vSize2(from - elem->result_on_layer) > config.getRadius(0) * config.getRadius(0) + FUDGE_LENGTH * FUDGE_LENGTH)) { to_be_removed.emplace_back(elem); - } - else if(elem->supports_roof) - { - Point from = elem->result_on_layer; - PolygonUtils::moveInside(roof_on_layer_above,from); - // Remove branches should have interface above them, but dont. Should never happen. - if (roof_on_layer_above.empty() || - (!roof_on_layer_above.inside(elem->result_on_layer) && vSize2(from-elem->result_on_layer)>config.getRadius(0)*config.getRadius(0) + FUDGE_LENGTH * FUDGE_LENGTH)) - { - to_be_removed.emplace_back(elem); - spdlog::warn("Removing already placed tip that should have roof above it?"); - } + spdlog::warn("Removing already placed tip that should have roof above it?"); } } + } - for (auto elem : to_be_removed) - { - move_bounds[layer_idx].erase(elem); - delete elem->area; - delete elem; - } - + for (auto elem : to_be_removed) + { + move_bounds[layer_idx].erase(elem); + delete elem->area; + delete elem; } } - ); + }); } -void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas) +void TreeSupportTipGenerator::generateTips( + SliceDataStorage& storage, + const SliceMeshStorage& mesh, + std::vector>& move_bounds, + std::vector& additional_support_areas, + std::vector& placed_support_lines_support_areas) { std::vector> new_tips(move_bounds.size()); - const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ? config.min_radius / 2 : sqrt(square(config.min_radius) - square(config.min_radius - config.support_line_width / 2)); - // ^^^ As r*r=x*x+y*y (circle equation): If a circle with center at (0,0) the top most point is at (0,r) as in y=r. This calculates how far one has to move on the x-axis so that y=r-support_line_width/2. - // In other words how far does one need to move on the x-axis to be support_line_width/2 away from the circle line. As a circle is round this length is identical for every axis as long as the 90� angle between both remains. + const coord_t circle_length_to_half_linewidth_change + = config.min_radius < config.support_line_width ? config.min_radius / 2 : sqrt(square(config.min_radius) - square(config.min_radius - config.support_line_width / 2)); + // ^^^ As r*r=x*x+y*y (circle equation): If a circle with center at (0,0) the top most point is at (0,r) as in y=r. This calculates how far one has to move on the x-axis so + // that y=r-support_line_width/2. + // In other words how far does one need to move on the x-axis to be support_line_width/2 away from the circle line. As a circle is round this length is identical for every + // axis as long as the 90� angle between both remains. const coord_t extra_outset = std::max(coord_t(0), config.min_radius - config.support_line_width / 2) + (xy_overrides ? 0 : config.support_line_width / 2); - // ^^^ Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all. + // ^^^ Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is + // better than not supporting it at all. if (support_roof_layers) { calculateRoofAreas(mesh); } - cura::parallel_for - ( - 1, - mesh.overhang_areas.size() - z_distance_delta, - [&](const LayerIndex layer_idx) + cura::parallel_for( + 1, + mesh.overhang_areas.size() - z_distance_delta, + [&](const LayerIndex layer_idx) + { + if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() && (layer_idx + 1 >= support_roof_drawn.size() || support_roof_drawn[layer_idx + 1].empty())) { - if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() && (layer_idx+1 >= support_roof_drawn.size() || support_roof_drawn[layer_idx+1].empty())) - { - return; // This is a continue if imagined in a loop context. - } - - Polygons relevant_forbidden = - volumes_.getAvoidance(config.getRadius(0), layer_idx, (only_gracious || !config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION , config.support_rests_on_model, ! xy_overrides); - // ^^^ Take the least restrictive avoidance possible - relevant_forbidden = relevant_forbidden.offset(EPSILON).unionPolygons(); // Prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. - - std::function generateLines = - [&](const Polygons& area, bool roof, LayerIndex layer_idx) - { - - coord_t upper_line_distance = support_supporting_branch_distance; - coord_t line_distance = std::max(roof ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance ); - + return; // This is a continue if imagined in a loop context. + } - return TreeSupportUtils::generateSupportInfillLines(area, config, roof && !use_fake_roof, layer_idx, line_distance , cross_fill_provider, roof && !use_fake_roof, line_distance == upper_line_distance); - }; + Polygons relevant_forbidden = volumes_.getAvoidance( + config.getRadius(0), + layer_idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + ! xy_overrides); + // ^^^ Take the least restrictive avoidance possible + relevant_forbidden + = relevant_forbidden.offset(EPSILON) + .unionPolygons(); // Prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. + + std::function generateLines = [&](const Polygons& area, bool roof, LayerIndex layer_idx) + { + coord_t upper_line_distance = support_supporting_branch_distance; + coord_t line_distance = std::max(roof ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance); + + + return TreeSupportUtils::generateSupportInfillLines( + area, + config, + roof && ! use_fake_roof, + layer_idx, + line_distance, + cross_fill_provider, + roof && ! use_fake_roof, + line_distance == upper_line_distance); + }; - std::vector> overhang_processing; - // ^^^ Every overhang has saved if a roof should be generated for it. - // This can NOT be done in the for loop as an area may NOT have a roof even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and it would not have a roof if the overhang is offset by support roof horizontal expansion instead. - // (At least this is the current behavior of the regular support) + std::vector> overhang_processing; + // ^^^ Every overhang has saved if a roof should be generated for it. + // This can NOT be done in the for loop as an area may NOT have a roof even if it is larger than the minimum_roof_area when it is only larger because of the support + // horizontal expansion and it would not have a roof if the overhang is offset by support roof horizontal expansion instead. (At least this is the current behavior + // of the regular support) - Polygons core_overhang=mesh.overhang_areas[layer_idx + z_distance_delta]; + Polygons core_overhang = mesh.overhang_areas[layer_idx + z_distance_delta]; - if (support_roof_layers && layer_idx+1 < support_roof_drawn.size()) + if (support_roof_layers && layer_idx + 1 < support_roof_drawn.size()) + { + core_overhang = core_overhang.difference(support_roof_drawn[layer_idx]); + for (Polygons roof_part : support_roof_drawn[layer_idx + 1] + .difference(support_roof_drawn[layer_idx]) + .splitIntoParts(true)) // If there is a roof, the roof will be one layer above the tips. { - core_overhang = core_overhang.difference(support_roof_drawn[layer_idx]); - for (Polygons roof_part : support_roof_drawn[layer_idx+1].difference(support_roof_drawn[layer_idx]).splitIntoParts(true)) //If there is a roof, the roof will be one layer above the tips. - { - //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors introduced since then. But this does not fully prevent some rounding errors either way, so just handle the error later. - overhang_processing.emplace_back(roof_part, true); - } + //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors + //introduced since then. But this does not fully prevent some rounding errors either way, so just handle the error later. + overhang_processing.emplace_back(roof_part, true); } + } - Polygons overhang_regular = - TreeSupportUtils::safeOffsetInc(core_overhang, support_outset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier); - Polygons remaining_overhang = - core_overhang.offset(support_outset).difference(overhang_regular.offset(config.support_line_width * 0.5)).intersection(relevant_forbidden); - - - // Offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang - coord_t extra_total_offset_acc = 0; - - // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. - while (extra_total_offset_acc + config.support_line_width / 8 < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. - { - coord_t offset_current_step = - extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ? - std::min(config.support_line_width / 8, extra_outset - extra_total_offset_acc) : - std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); - extra_total_offset_acc += offset_current_step; - Polygons overhang_offset = TreeSupportUtils::safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(support_roof_drawn[layer_idx].offset(1.5 * extra_total_offset_acc))).unionPolygons(); //overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. - Polygons next_overhang = TreeSupportUtils::safeOffsetInc(remaining_overhang, extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); - } + Polygons overhang_regular = TreeSupportUtils::safeOffsetInc( + core_overhang, + support_outset, + relevant_forbidden, + config.min_radius * 1.75 + config.xy_min_distance, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + Polygons remaining_overhang + = core_overhang.offset(support_outset).difference(overhang_regular.offset(config.support_line_width * 0.5)).intersection(relevant_forbidden); + + + // Offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang + coord_t extra_total_offset_acc = 0; + + // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. + while (extra_total_offset_acc + config.support_line_width / 8 + < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. + { + coord_t offset_current_step = extra_total_offset_acc + 2 * config.support_line_width > config.min_radius + ? std::min(config.support_line_width / 8, extra_outset - extra_total_offset_acc) + : std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); + extra_total_offset_acc += offset_current_step; + Polygons overhang_offset = TreeSupportUtils::safeOffsetInc( + overhang_regular, + 1.5 * extra_total_offset_acc, + volumes_.getCollision(0, layer_idx, true), + config.xy_min_distance + config.support_line_width, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(support_roof_drawn[layer_idx].offset(1.5 * extra_total_offset_acc))) + .unionPolygons(); // overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. + Polygons next_overhang = TreeSupportUtils::safeOffsetInc( + remaining_overhang, + extra_total_offset_acc, + volumes_.getCollision(0, layer_idx, true), + config.xy_min_distance + config.support_line_width, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); + } - // If the xy distance overrides the z distance, some support needs to be inserted further down. - //=> Analyze which support points do not fit on this layer and check if they will fit a few layers down - // (while adding them an infinite amount of layers down would technically be closer the setting description, it would not produce reasonable results. ) - if (xy_overrides) + // If the xy distance overrides the z distance, some support needs to be inserted further down. + //=> Analyze which support points do not fit on this layer and check if they will fit a few layers down + // (while adding them an infinite amount of layers down would technically be closer the setting description, it would not produce reasonable results. ) + if (xy_overrides) + { + for (Polygons& remaining_overhang_part : remaining_overhang.splitIntoParts(false)) { - - for (Polygons& remaining_overhang_part:remaining_overhang.splitIntoParts(false)) + std::vector overhang_lines; + Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), config.min_radius, 1, false); + // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. + // Technically this violates branch distance, but not only is this the only reasonable choice, + // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. + // Also it is assumed that the area that is valid a layer below is to small for support roof. + if (polylines.pointCount() <= 3) { + // Add the outer wall to ensure it is correct supported instead. + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(remaining_overhang_part), connect_length, 3, true); + } - std::vector overhang_lines; - Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), config.min_radius, 1, false); - // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. - // Technically this violates branch distance, but not only is this the only reasonable choice, - // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. - // Also it is assumed that the area that is valid a layer below is to small for support roof. - if (polylines.pointCount() <= 3) + for (auto line : polylines) + { + LineInformation res_line; + for (Point p : line) { - // Add the outer wall to ensure it is correct supported instead. - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(remaining_overhang_part), connect_length, 3, true); + res_line.emplace_back(p, LineStatus::INVALID); } + overhang_lines.emplace_back(res_line); + } - for (auto line : polylines) + for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && ! overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) + { + // get least restricted avoidance for layer_idx-lag_ctr + Polygons relevant_forbidden_below = volumes_.getAvoidance( + config.getRadius(0), + layer_idx - lag_ctr, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + ! xy_overrides); + // It is not required to offset the forbidden area here as the points won't change: + // If points here are not inside the forbidden area neither will they be later when placing these points, as these are the same points. + std::function)> evaluatePoint = [&](std::pair p) { - LineInformation res_line; - for (Point p : line) - { - res_line.emplace_back(p, LineStatus::INVALID); - } - overhang_lines.emplace_back(res_line); - } + return relevant_forbidden_below.inside(p.first, true); + }; - for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) + if (support_roof_layers) { - // get least restricted avoidance for layer_idx-lag_ctr - Polygons relevant_forbidden_below = - volumes_.getAvoidance(config.getRadius(0), layer_idx - lag_ctr, (only_gracious||!config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); - // It is not required to offset the forbidden area here as the points won't change: - // If points here are not inside the forbidden area neither will they be later when placing these points, as these are the same points. - std::function)> evaluatePoint = - [&](std::pair p) { return relevant_forbidden_below.inside(p.first, true); }; - - if (support_roof_layers) + // Remove all points that are for some reason part of a roof area, as the point is already supported by roof + std::function)> evaluatePartOfRoof = [&](std::pair p) { - //Remove all points that are for some reason part of a roof area, as the point is already supported by roof - std::function)> evaluatePartOfRoof = - [&](std::pair p) { return support_roof_drawn[layer_idx-lag_ctr].inside(p.first, true); }; - - overhang_lines = splitLines(overhang_lines, evaluatePartOfRoof).second; - } - std::pair, std::vector> split = splitLines(overhang_lines, evaluatePoint); // Keep all lines that are invalid. - overhang_lines = split.first; - std::vector fresh_valid_points = convertLinesToInternal(convertInternalToLines(split.second), layer_idx - lag_ctr); - // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. + return support_roof_drawn[layer_idx - lag_ctr].inside(p.first, true); + }; - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, support_roof_layers, false); + overhang_lines = splitLines(overhang_lines, evaluatePartOfRoof).second; } + std::pair, std::vector> split + = splitLines(overhang_lines, evaluatePoint); // Keep all lines that are invalid. + overhang_lines = split.first; + std::vector fresh_valid_points = convertLinesToInternal(convertInternalToLines(split.second), layer_idx - lag_ctr); + // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. + + addLinesAsInfluenceAreas( + new_tips, + fresh_valid_points, + (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, + layer_idx - lag_ctr, + false, + support_roof_layers, + false); } } + } - overhang_regular.removeSmallAreas(minimum_support_area); + overhang_regular.removeSmallAreas(minimum_support_area); - for (Polygons support_part : overhang_regular.splitIntoParts(true)) + for (Polygons support_part : overhang_regular.splitIntoParts(true)) + { + overhang_processing.emplace_back(support_part, false); + } + + for (std::pair overhang_pair : overhang_processing) + { + const bool roof_allowed_for_this_part = overhang_pair.second; + Polygons overhang_outset = overhang_pair.first; + const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); + std::vector overhang_lines; + + bool only_lines = true; + + // The tip positions are determined here. + // todo can cause inconsistent support density if a line exactly aligns with the model + Polygons polylines = ensureMaximumDistancePolyline( + generateLines(overhang_outset, roof_allowed_for_this_part, layer_idx + roof_allowed_for_this_part), + ! roof_allowed_for_this_part ? config.min_radius * 2 + : use_fake_roof ? support_supporting_branch_distance + : connect_length, + 1, + false); + + + // support_line_width to form a line here as otherwise most will be unsupported. + // Technically this violates branch distance, but not only is this the only reasonable choice, + // but it ensures consistent behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour. + // This is not done when a roof is above as the roof will support the model and the trees only need to support the roof + + if (polylines.pointCount() <= min_support_points) { - overhang_processing.emplace_back(support_part, false); + only_lines = false; + // Add the outer wall (of the overhang) to ensure it is correct supported instead. + // Try placing the support points in a way that they fully support the outer wall, instead of just the with half of the support line width. + Polygons reduced_overhang_outset = overhang_outset.offset(-config.support_line_width / 2.2); + // ^^^ It's assumed that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area + // generated from them will support the overhang. + // (If this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60� so there is a fallback, as some support is + // better than none.) + if (! reduced_overhang_outset.empty() + && overhang_outset.difference(reduced_overhang_outset.offset(std::max(config.support_line_width, connect_length))).area() < 1) + { + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(reduced_overhang_outset), connect_length, min_support_points, true); + } + else + { + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(overhang_outset), connect_length, min_support_points, true); + } } - for (std::pair overhang_pair : overhang_processing) + if (roof_allowed_for_this_part) // Some roof may only be supported by a part of a tip { - const bool roof_allowed_for_this_part = overhang_pair.second; - Polygons overhang_outset = overhang_pair.first; - const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); - std::vector overhang_lines; - - bool only_lines = true; - - // The tip positions are determined here. - // todo can cause inconsistent support density if a line exactly aligns with the model - Polygons polylines = - ensureMaximumDistancePolyline - ( - generateLines(overhang_outset, roof_allowed_for_this_part, layer_idx + roof_allowed_for_this_part), !roof_allowed_for_this_part ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length , 1, false - ); - + polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, config.getRadius(0) + FUDGE_LENGTH / 2); + } - // support_line_width to form a line here as otherwise most will be unsupported. - // Technically this violates branch distance, but not only is this the only reasonable choice, - // but it ensures consistent behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour. - // This is not done when a roof is above as the roof will support the model and the trees only need to support the roof + overhang_lines = convertLinesToInternal(polylines, layer_idx); - if (polylines.pointCount() <= min_support_points) - { - only_lines = false; - // Add the outer wall (of the overhang) to ensure it is correct supported instead. - // Try placing the support points in a way that they fully support the outer wall, instead of just the with half of the support line width. - Polygons reduced_overhang_outset = overhang_outset.offset(-config.support_line_width / 2.2); - // ^^^ It's assumed that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them will support the overhang. - // (If this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60� so there is a fallback, as some support is better than none.) - if (! reduced_overhang_outset.empty() && overhang_outset.difference(reduced_overhang_outset.offset(std::max(config.support_line_width, connect_length))).area() < 1) - { - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(reduced_overhang_outset), connect_length, min_support_points, true); - } - else - { - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(overhang_outset), connect_length, min_support_points, true); - } - } + if (overhang_lines.empty()) // some error handling and logging + { + Polygons enlarged_overhang_outset = overhang_outset.offset(config.getRadius(0) + FUDGE_LENGTH / 2, ClipperLib::jtRound).difference(relevant_forbidden); + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(enlarged_overhang_outset), connect_length, min_support_points, true); + overhang_lines = convertLinesToInternal(polylines, layer_idx); - if (roof_allowed_for_this_part) //Some roof may only be supported by a part of a tip + if (! overhang_lines.empty()) { - polylines = TreeSupportUtils::movePointsOutside(polylines,relevant_forbidden,config.getRadius(0)+FUDGE_LENGTH/2); + spdlog::debug("Compensated for overhang area that had no valid tips. Now has a tip."); } - - overhang_lines = convertLinesToInternal(polylines, layer_idx ); - - if(overhang_lines.empty()) //some error handling and logging + else { - Polygons enlarged_overhang_outset = overhang_outset.offset(config.getRadius(0)+FUDGE_LENGTH/2,ClipperLib::jtRound).difference(relevant_forbidden); - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(enlarged_overhang_outset), connect_length, min_support_points, true); - overhang_lines = convertLinesToInternal(polylines, layer_idx ); - - if(!overhang_lines.empty()) - { - spdlog::debug("Compensated for overhang area that had no valid tips. Now has a tip."); - } - else - { - spdlog::warn("Overhang area has no valid tips! Was roof: {} On Layer: {}", roof_allowed_for_this_part,layer_idx); - } + spdlog::warn("Overhang area has no valid tips! Was roof: {} On Layer: {}", roof_allowed_for_this_part, layer_idx); } - - size_t dont_move_for_layers = support_roof_layers ? (force_tip_to_roof ? support_roof_layers : (roof_allowed_for_this_part ? 0 : support_roof_layers)) : 0; - addLinesAsInfluenceAreas(new_tips, overhang_lines, force_tip_to_roof ? support_roof_layers : 0, layer_idx, roof_allowed_for_this_part, dont_move_for_layers, only_lines); } + + size_t dont_move_for_layers = support_roof_layers ? (force_tip_to_roof ? support_roof_layers : (roof_allowed_for_this_part ? 0 : support_roof_layers)) : 0; + addLinesAsInfluenceAreas( + new_tips, + overhang_lines, + force_tip_to_roof ? support_roof_layers : 0, + layer_idx, + roof_allowed_for_this_part, + dont_move_for_layers, + only_lines); } - ); + }); - cura::parallel_for - ( + cura::parallel_for( 0, support_roof_drawn.size(), [&](const LayerIndex layer_idx) { // Sometimes roofs could be empty as the pattern does not generate lines if the area is narrow enough. - // If there is a roof could have zero lines in its area (as it has no wall), rand a support area would very likely be printed (because there are walls for the support areas), replace non printable roofs with support + // If there is a roof could have zero lines in its area (as it has no wall), rand a support area would very likely be printed (because there are walls for the support + // areas), replace non printable roofs with support if (! use_fake_roof && config.support_wall_count > 0 && config.support_roof_wall_count == 0) { for (auto roof_area : support_roof_drawn[layer_idx].unionPolygons(roof_tips_drawn[layer_idx]).splitIntoParts()) { - // technically there is no guarantee that a drawn roof tip has lines, as it could be unioned with another roof area that has, but this has to be enough hopefully. - if (layer_idx < additional_support_areas.size() && TreeSupportUtils::generateSupportInfillLines(roof_area, config, true, layer_idx, support_roof_line_distance, cross_fill_provider, false).empty()) + // technically there is no guarantee that a drawn roof tip has lines, as it could be unioned with another roof area that has, but this has to be enough + // hopefully. + if (layer_idx < additional_support_areas.size() + && TreeSupportUtils::generateSupportInfillLines(roof_area, config, true, layer_idx, support_roof_line_distance, cross_fill_provider, false).empty()) { additional_support_areas[layer_idx].add(roof_area); } @@ -1018,8 +1166,15 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice { storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, 0, support_roof_line_distance); } - placed_support_lines_support_areas[layer_idx].add( - TreeSupportUtils::generateSupportInfillLines(support_roof_drawn[layer_idx], config, false, layer_idx, support_roof_line_distance, cross_fill_provider, false).offsetPolyLine(config.support_line_width / 2)); + placed_support_lines_support_areas[layer_idx].add(TreeSupportUtils::generateSupportInfillLines( + support_roof_drawn[layer_idx], + config, + false, + layer_idx, + support_roof_line_distance, + cross_fill_provider, + false) + .offsetPolyLine(config.support_line_width / 2)); } else { @@ -1029,7 +1184,7 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice } }); - removeUselessAddedPoints(new_tips ,storage , additional_support_areas); + removeUselessAddedPoints(new_tips, storage, additional_support_areas); for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { @@ -1038,6 +1193,4 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice } - -}// namespace cura - +} // namespace cura diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 6d06e29751..9b0b04a4b1 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -3,18 +3,13 @@ #ifdef ARCUS -#include //The socket to communicate to. -#include //To sleep while waiting for the connection. -#include //To map settings to their extruder numbers for limit_to_extruder. - -#include +#include "communication/ArcusCommunication.h" #include "Application.h" //To get and set the current slice command. #include "ExtruderTrain.h" #include "FffProcessor.h" //To start a slice. #include "PrintFeature.h" #include "Slice.h" //To process slices. -#include "communication/ArcusCommunication.h" #include "communication/ArcusCommunicationPrivate.h" //Our PIMPL. #include "communication/Listener.h" //To listen to the Arcus socket. #include "communication/SliceDataStruct.h" //To store sliced layer data. @@ -22,6 +17,12 @@ #include "settings/types/Velocity.h" //To send to layer view how fast stuff is printing. #include "utils/polygon.h" +#include //The socket to communicate to. +#include + +#include //To sleep while waiting for the connection. +#include //To map settings to their extruder numbers for limit_to_extruder. + namespace cura { @@ -47,7 +48,8 @@ class ArcusCommunication::PathCompiler std::vector line_widths; //!< Line widths for the line segments stored, the size of this vector is N. std::vector line_thicknesses; //!< Line thicknesses for the line segments stored, the size of this vector is N. std::vector line_velocities; //!< Line feedrates for the line segments stored, the size of this vector is N. - std::vector points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is the dimensionality of the point. + std::vector points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is + //!< the dimensionality of the point. Point last_point; @@ -281,7 +283,9 @@ class ArcusCommunication::PathCompiler } }; -ArcusCommunication::ArcusCommunication() : private_data(new Private), path_compiler(new PathCompiler(*private_data)) +ArcusCommunication::ArcusCommunication() + : private_data(new Private) + , path_compiler(new PathCompiler(*private_data)) { } @@ -422,7 +426,12 @@ void ArcusCommunication::sendOptimizedLayerData() data.slice_data.clear(); } -void ArcusCommunication::sendPolygon(const PrintFeatureType& type, const ConstPolygonRef& polygon, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) +void ArcusCommunication::sendPolygon( + const PrintFeatureType& type, + const ConstPolygonRef& polygon, + const coord_t& line_width, + const coord_t& line_thickness, + const Velocity& velocity) { path_compiler->sendPolygon(type, polygon, line_width, line_thickness, velocity); } diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index b4f800db6e..0fd047e76e 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1,12 +1,7 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include -#include -#include - -#include +#include "gcodeExport.h" #include "Application.h" //To send layer view data. #include "ExtruderTrain.h" @@ -15,11 +10,17 @@ #include "Slice.h" #include "WipeScriptConfig.h" #include "communication/Communication.h" //To send layer view data. -#include "gcodeExport.h" #include "settings/types/LayerIndex.h" #include "utils/Date.h" #include "utils/string.h" // MMtoStream, PrecisionedDouble +#include + +#include +#include +#include +#include + namespace cura { @@ -35,7 +36,11 @@ 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, MM2INT(20)) + , layer_nr(0) + , relative_extrusion(false) { *output_stream << std::fixed; @@ -85,7 +90,8 @@ void GCodeExport::preSetup(const size_t start_extruder) const ExtruderTrain& train = scene.extruders[extruder_nr]; setFilamentDiameter(extruder_nr, train.settings.get("material_diameter")); - extruder_attr[extruder_nr].last_retraction_prime_speed = train.settings.get("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured... + extruder_attr[extruder_nr].last_retraction_prime_speed + = train.settings.get("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured... extruder_attr[extruder_nr].fan_number = train.settings.get("machine_extruder_cooling_fan_number"); } @@ -160,7 +166,11 @@ const std::string GCodeExport::flavorToString(const EGCodeFlavor& flavor) const } } -std::string GCodeExport::getFileHeader(const std::vector& extruder_is_used, const Duration* print_time, const std::vector& filament_used, const std::vector& mat_ids) +std::string GCodeExport::getFileHeader( + const std::vector& extruder_is_used, + const Duration* print_time, + const std::vector& filament_used, + const std::vector& mat_ids) { std::ostringstream prefix; @@ -387,8 +397,10 @@ double GCodeExport::getCurrentExtrudedVolume() const const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings; if (! extruder_settings.get("machine_firmware_retract")) { // no E values are changed to perform a retraction - extrusion_amount -= extruder_attr[current_extruder].retraction_e_amount_at_e_start; // subtract the increment in E which was used for the first unretraction instead of extrusion - extrusion_amount += extruder_attr[current_extruder].retraction_e_amount_current; // add the decrement in E which the filament is behind on extrusion due to the last retraction + extrusion_amount + -= extruder_attr[current_extruder].retraction_e_amount_at_e_start; // subtract the increment in E which was used for the first unretraction instead of extrusion + extrusion_amount + += extruder_attr[current_extruder].retraction_e_amount_current; // add the decrement in E which the filament is behind on extrusion due to the last retraction } if (is_volumetric) { @@ -616,7 +628,8 @@ bool GCodeExport::initializeExtruderTrains(const SliceDataStorage& storage, cons bool should_prime_extruder = true; const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (Application::getInstance().communication->isSequential()) // If we must output the g-code sequentially, we must already place the g-code header here even if we don't know the exact time/material usages yet. + if (Application::getInstance().communication->isSequential()) // If we must output the g-code sequentially, we must already place the g-code header here even if we don't know + // the exact time/material usages yet. { std::string prefix = getFileHeader(storage.getExtrudersUsed()); writeCode(prefix.c_str()); @@ -719,7 +732,9 @@ void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage { const Temperature bed_temp = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); if (scene.current_mesh_group == scene.mesh_groups.begin() // Always write bed temperature for first mesh group. - || bed_temp != (scene.current_mesh_group - 1)->settings.get("material_bed_temperature")) // Don't write bed temperature if identical to temperature of previous group. + || bed_temp + != (scene.current_mesh_group - 1) + ->settings.get("material_bed_temperature")) // Don't write bed temperature if identical to temperature of previous group. { if (bed_temp != 0) { @@ -874,14 +889,18 @@ void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velo if (! extruder_attr[current_extruder].retraction_e_amount_current) { *output_stream << "M103" << new_line; - extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically + extruder_attr[current_extruder].retraction_e_amount_current + = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically } } *output_stream << "G1 X" << MMtoStream{ gcode_pos.X } << " Y" << MMtoStream{ gcode_pos.Y } << " Z" << MMtoStream{ z }; *output_stream << " F" << PrecisionedDouble{ 1, fspeed } << new_line; 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(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(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) @@ -907,7 +926,14 @@ void GCodeExport::writeTravel(const coord_t x, const coord_t y, const coord_t z, writeFXYZE(speed, x, y, z, current_e_value, travel_move_type); } -void GCodeExport::writeExtrusion(const coord_t x, const coord_t y, const coord_t z, const Velocity& speed, const double extrusion_mm3_per_mm, const PrintFeatureType& feature, const bool update_extrusion_offset) +void GCodeExport::writeExtrusion( + const coord_t x, + const coord_t y, + const coord_t z, + const Velocity& speed, + const double extrusion_mm3_per_mm, + const PrintFeatureType& feature, + const bool update_extrusion_offset) { if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z) { @@ -1021,33 +1047,45 @@ 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 } - << new_line; + *output_stream << "G1 F" << PrecisionedDouble{ 1, extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " + << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{ 5, output_e } << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; } - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0, PrintFeatureType::MoveRetraction); + estimateCalculator.plan( + TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), + 25.0, + PrintFeatureType::MoveRetraction); } else { current_e_value += extruder_attr[current_extruder].retraction_e_amount_current; const double output_e = (relative_extrusion) ? extruder_attr[current_extruder].retraction_e_amount_current + prime_volume_e : current_e_value; - *output_stream << "G1 F" << PrecisionedDouble{ 1, extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{ 5, output_e } << new_line; + *output_stream << "G1 F" << PrecisionedDouble{ 1, extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " + << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{ 5, output_e } << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; - estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction); + estimateCalculator.plan( + TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), + currentSpeed, + PrintFeatureType::MoveRetraction); } } else if (prime_volume != 0.0) { const double output_e = (relative_extrusion) ? prime_volume_e : current_e_value; - *output_stream << "G1 F" << PrecisionedDouble{ 1, extruder_attr[current_extruder].last_retraction_prime_speed * 60 } << " " << extruder_attr[current_extruder].extruderCharacter; + *output_stream << "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(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), + currentSpeed, + PrintFeatureType::NoneType); } extruder_attr[current_extruder].prime_volume = 0.0; if (getCurrentExtrudedVolume() > 10000.0 && flavor != EGCodeFlavor::BFB - && flavor != EGCodeFlavor::MAKERBOT) // According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. + && flavor != EGCodeFlavor::MAKERBOT) // According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it + // every 10m, just to be sure. { resetExtrusionValue(); } @@ -1121,9 +1159,10 @@ 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)), - 25.0, - PrintFeatureType::MoveRetraction); // TODO: hardcoded values! + estimateCalculator.plan( + TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), + 25.0, + PrintFeatureType::MoveRetraction); // TODO: hardcoded values! } else { @@ -1132,7 +1171,10 @@ 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(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), + currentSpeed, + PrintFeatureType::MoveRetraction); extr_attr.last_retraction_prime_speed = config.primeSpeed; } @@ -1286,7 +1328,10 @@ void GCodeExport::writePrimeTrain(const Velocity& travel_speed) // ideally the prime position would be respected whether we do a blob or not, // but the frontend currently doesn't support a value function of an extruder setting depending on an fdmprinter setting, // which is needed to automatically ignore the prime position for the printer when blob is disabled - Point3 prime_pos(extruder_settings.get("extruder_prime_pos_x"), extruder_settings.get("extruder_prime_pos_y"), extruder_settings.get("extruder_prime_pos_z")); + Point3 prime_pos( + extruder_settings.get("extruder_prime_pos_x"), + extruder_settings.get("extruder_prime_pos_y"), + extruder_settings.get("extruder_prime_pos_z")); if (! extruder_settings.get("extruder_prime_pos_abs")) { // currentPosition.z can be already z hopped diff --git a/src/infill/SubDivCube.cpp b/src/infill/SubDivCube.cpp index 890fed2865..87478fa204 100644 --- a/src/infill/SubDivCube.cpp +++ b/src/infill/SubDivCube.cpp @@ -3,17 +3,17 @@ #include "infill/SubDivCube.h" -#include - -#include "sliceDataStorage.h" #include "settings/types/Angle.h" //For the infill angle. +#include "sliceDataStorage.h" #include "utils/math.h" #include "utils/polygonUtils.h" -#define ONE_OVER_SQRT_2 0.7071067811865475244008443621048490392848359376884740 //1 / sqrt(2) -#define ONE_OVER_SQRT_3 0.577350269189625764509148780501957455647601751270126876018 //1 / sqrt(3) -#define ONE_OVER_SQRT_6 0.408248290463863016366214012450981898660991246776111688072 //1 / sqrt(6) -#define SQRT_TWO_THIRD 0.816496580927726032732428024901963797321982493552223376144 //sqrt(2 / 3) +#include + +#define ONE_OVER_SQRT_2 0.7071067811865475244008443621048490392848359376884740 // 1 / sqrt(2) +#define ONE_OVER_SQRT_3 0.577350269189625764509148780501957455647601751270126876018 // 1 / sqrt(3) +#define ONE_OVER_SQRT_6 0.408248290463863016366214012450981898660991246776111688072 // 1 / sqrt(6) +#define SQRT_TWO_THIRD 0.816496580927726032732428024901963797321982493552223376144 // sqrt(2 / 3) namespace cura { @@ -40,9 +40,10 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh, const Point& infill_or // if infill_angles is not empty use the first value, otherwise use 0 const std::vector infill_angles = mesh.settings.get>("infill_angles"); - const AngleDegrees infill_angle = (!infill_angles.empty()) ? infill_angles[0] : AngleDegrees(0); + const AngleDegrees infill_angle = (! infill_angles.empty()) ? infill_angles[0] : AngleDegrees(0); - const coord_t furthest_dist_from_origin = std::sqrt(square(mesh.settings.get("machine_height")) + square(mesh.settings.get("machine_depth") / 2) + square(mesh.settings.get("machine_width") / 2)); + const coord_t furthest_dist_from_origin = std::sqrt( + square(mesh.settings.get("machine_height")) + square(mesh.settings.get("machine_depth") / 2) + square(mesh.settings.get("machine_width") / 2)); const coord_t max_side_length = furthest_dist_from_origin * 2; size_t curr_recursion_depth = 0; @@ -75,9 +76,15 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh, const Point& infill_or // / .O. \ | . // /.~' '~.\ O---->X . // X """"""""""" Y . - tilt.matrix[0] = -ONE_OVER_SQRT_2; tilt.matrix[1] = ONE_OVER_SQRT_2; tilt.matrix[2] = 0; - tilt.matrix[3] = -ONE_OVER_SQRT_6; tilt.matrix[4] = -ONE_OVER_SQRT_6; tilt.matrix[5] = SQRT_TWO_THIRD ; - tilt.matrix[6] = ONE_OVER_SQRT_3; tilt.matrix[7] = ONE_OVER_SQRT_3; tilt.matrix[8] = ONE_OVER_SQRT_3; + tilt.matrix[0] = -ONE_OVER_SQRT_2; + tilt.matrix[1] = ONE_OVER_SQRT_2; + tilt.matrix[2] = 0; + tilt.matrix[3] = -ONE_OVER_SQRT_6; + tilt.matrix[4] = -ONE_OVER_SQRT_6; + tilt.matrix[5] = SQRT_TWO_THIRD; + tilt.matrix[6] = ONE_OVER_SQRT_3; + tilt.matrix[7] = ONE_OVER_SQRT_3; + tilt.matrix[8] = ONE_OVER_SQRT_3; infill_rotation_matrix = PointMatrix(infill_angle); Point3Matrix infill_angle_mat(infill_rotation_matrix); @@ -89,7 +96,7 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh, const Point& infill_or void SubDivCube::generateSubdivisionLines(const coord_t z, Polygons& result) { - if (cube_properties_per_recursion_step.empty()) //Infill is set to 0%. + if (cube_properties_per_recursion_step.empty()) // Infill is set to 0%. { return; } @@ -126,7 +133,7 @@ void SubDivCube::generateSubdivisionLines(const coord_t z, Polygons (&directiona relative_b.Y = relative_a.Y; rotatePointInitial(relative_a); rotatePointInitial(relative_b); - for (int dir_idx = 0; dir_idx < 3; dir_idx++)//!< draw the line, then rotate 120 degrees. + for (int dir_idx = 0; dir_idx < 3; dir_idx++) //!< draw the line, then rotate 120 degrees. { a.X = center.x + relative_a.X; a.Y = center.y + relative_a.Y; @@ -158,7 +165,7 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, size_t depth) { return; } - if (depth >= cube_properties_per_recursion_step.size()) //Depth is out of bounds of what we pre-computed. + if (depth >= cube_properties_per_recursion_step.size()) // Depth is out of bounds of what we pre-computed. { return; } @@ -191,17 +198,17 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, size_t depth) bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, coord_t radius) { coord_t distance2 = 0; - coord_t sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer + coord_t sphere_slice_radius2; //!< squared radius of bounding sphere slice on target layer bool inside_somewhere = false; bool outside_somewhere = false; int inside; - Ratio part_dist;//what percentage of the radius the target layer is away from the center along the z axis. 0 - 1 + Ratio part_dist; // what percentage of the radius the target layer is away from the center along the z axis. 0 - 1 const coord_t layer_height = mesh.settings.get("layer_height"); int bottom_layer = (center.z - radius) / layer_height; int top_layer = (center.z + radius) / layer_height; for (int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain. { - part_dist = Ratio { static_cast(test_layer * layer_height - center.z) } / radius; + part_dist = Ratio{ static_cast(test_layer * layer_height - center.z) } / radius; sphere_slice_radius2 = radius * radius * (1.0 - (part_dist * part_dist)); Point loc(center.x, center.y); @@ -259,7 +266,7 @@ void SubDivCube::rotatePointInitial(Point& target) void SubDivCube::rotatePoint120(Point& target) { - //constexpr double sqrt_three_fourths = sqrt(3.0 / 4.0); //TODO: Reactivate once MacOS is upgraded to a more modern compiler. + // constexpr double sqrt_three_fourths = sqrt(3.0 / 4.0); //TODO: Reactivate once MacOS is upgraded to a more modern compiler. #define sqrt_three_fourths 0.86602540378443864676372317 const coord_t x = -0.5 * target.X - sqrt_three_fourths * target.Y; target.Y = -0.5 * target.Y + sqrt_three_fourths * target.X; @@ -289,4 +296,4 @@ void SubDivCube::addLineAndCombine(Polygons& group, Point from, Point to) group.addLine(from, to); } -}//namespace cura +} // namespace cura diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 2b6bcbea5a..1197cade97 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -1,14 +1,7 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include -#include // regex parsing for temp flow graph -#include // ostringstream -#include -#include //Parsing strings (stod, stoul). - -#include +#include "settings/Settings.h" #include "Application.h" //To get the extruders. #include "BeadingStrategy/BeadingStrategyFactory.h" @@ -16,7 +9,6 @@ #include "Slice.h" #include "settings/EnumSettings.h" #include "settings/FlowTempGraph.h" -#include "settings/Settings.h" #include "settings/types/Angle.h" #include "settings/types/Duration.h" //For duration and time settings. #include "settings/types/LayerIndex.h" //For layer index settings. @@ -24,8 +16,17 @@ #include "settings/types/Temperature.h" //For temperature settings. #include "settings/types/Velocity.h" //For velocity settings. #include "utils/FMatrix4x3.h" -#include "utils/string.h" //For Escaped. #include "utils/polygon.h" +#include "utils/string.h" //For Escaped. + +#include + +#include +#include +#include // regex parsing for temp flow graph +#include // ostringstream +#include +#include //Parsing strings (stod, stoul). namespace cura { @@ -112,7 +113,8 @@ ExtruderTrain& Settings::get(const std::string& key) const return Application::getInstance().current_slice->scene.extruders[extruder_nr]; } -template<> std::vector Settings::get>(const std::string& key) const +template<> +std::vector Settings::get>(const std::string& key) const { int extruder_nr = std::atoi(get(key).c_str()); std::vector ret; @@ -133,7 +135,8 @@ template<> std::vector Settings::get template<> LayerIndex Settings::get(const std::string& key) const { - return std::atoi(get(key).c_str()) - 1; // For the user we display layer numbers starting from 1, but we start counting from 0. Still it may be negative for Raft layers. + return std::atoi(get(key).c_str()) + - 1; // For the user we display layer numbers starting from 1, but we start counting from 0. Still it may be negative for Raft layers. } template<> @@ -246,14 +249,15 @@ FlowTempGraph Settings::get(const std::string& key) const return result; } -template<> Polygons Settings::get(const std::string& key) const +template<> +Polygons Settings::get(const std::string& key) const { std::string value_string = get(key); Polygons result; if (value_string.empty()) { - return result; //Empty at this point. + return result; // Empty at this point. } /* We're looking to match one or more floating point values separated by * commas and surrounded by square brackets. Note that because the QML @@ -266,22 +270,22 @@ template<> Polygons Settings::get(const std::string& key) const if (std::regex_search(value_string, polygons_match, polygons_regex) && polygons_match.size() > 1) { std::string polygons_string = polygons_match.str(1); - + std::regex polygon_regex(R"(\[((\[[^\[\]]*\]\s*,?\s*)*)\]\s*,?)"); // matches with a list of lists (a list of 2D vertices) std::smatch polygon_match; - - std::regex_token_iterator rend; //Default constructor gets the end-of-sequence iterator. + + std::regex_token_iterator rend; // Default constructor gets the end-of-sequence iterator. std::regex_token_iterator polygon_match_iter(polygons_string.begin(), polygons_string.end(), polygon_regex, 0); while (polygon_match_iter != rend) { std::string polygon_str = *polygon_match_iter++; - + result.emplace_back(); PolygonRef poly = result.back(); std::regex point2D_regex(R"(\[([^,\[]*),([^,\]]*)\])"); // matches to a list of exactly two things - const int submatches[] = {1, 2}; // Match first number and second number of a pair. + const int submatches[] = { 1, 2 }; // Match first number and second number of a pair. std::regex_token_iterator match_iter(polygon_str.begin(), polygon_str.end(), point2D_regex, submatches); while (match_iter != rend) { @@ -522,7 +526,8 @@ EZSeamType Settings::get(const std::string& key) const { return EZSeamType::RANDOM; } - else if (value == "back") // It's called 'back' internally because originally this was intended to allow the user to put the seam in the back of the object where it's less visible. + else if (value == "back") // It's called 'back' internally because originally this was intended to allow the user to put the seam in the back of the object where it's less + // visible. { return EZSeamType::USER_SPECIFIED; } diff --git a/src/skin.cpp b/src/skin.cpp index 1c0e8da708..c29fec0b63 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -1,20 +1,21 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include // std::ceil +#include "skin.h" #include "Application.h" //To get settings. -#include "Slice.h" #include "ExtruderTrain.h" -#include "skin.h" +#include "Slice.h" +#include "WallToolPaths.h" #include "infill.h" -#include "sliceDataStorage.h" #include "settings/EnumSettings.h" //For EFillMethod. #include "settings/types/Angle.h" //For the infill support angle. #include "settings/types/Ratio.h" +#include "sliceDataStorage.h" #include "utils/math.h" #include "utils/polygonUtils.h" -#include "WallToolPaths.h" + +#include // std::ceil #define MIN_AREA_SIZE (0.4 * 0.4) @@ -33,18 +34,18 @@ coord_t SkinInfillAreaComputation::getSkinLineWidth(const SliceMeshStorage& mesh } SkinInfillAreaComputation::SkinInfillAreaComputation(const LayerIndex& layer_nr, SliceMeshStorage& mesh, bool process_infill) -: layer_nr(layer_nr) -, mesh(mesh) -, bottom_layer_count(mesh.settings.get("bottom_layers")) -, initial_bottom_layer_count(mesh.settings.get("initial_bottom_layers")) -, top_layer_count(mesh.settings.get("top_layers")) -, skin_line_width(getSkinLineWidth(mesh, layer_nr)) -, no_small_gaps_heuristic(mesh.settings.get("skin_no_small_gaps_heuristic")) -, process_infill(process_infill) -, top_skin_preshrink(mesh.settings.get("top_skin_preshrink")) -, bottom_skin_preshrink(mesh.settings.get("bottom_skin_preshrink")) -, top_skin_expand_distance(mesh.settings.get("top_skin_expand_distance")) -, bottom_skin_expand_distance(mesh.settings.get("bottom_skin_expand_distance")) + : layer_nr(layer_nr) + , mesh(mesh) + , bottom_layer_count(mesh.settings.get("bottom_layers")) + , initial_bottom_layer_count(mesh.settings.get("initial_bottom_layers")) + , top_layer_count(mesh.settings.get("top_layers")) + , skin_line_width(getSkinLineWidth(mesh, layer_nr)) + , no_small_gaps_heuristic(mesh.settings.get("skin_no_small_gaps_heuristic")) + , process_infill(process_infill) + , top_skin_preshrink(mesh.settings.get("top_skin_preshrink")) + , bottom_skin_preshrink(mesh.settings.get("bottom_skin_preshrink")) + , top_skin_expand_distance(mesh.settings.get("top_skin_expand_distance")) + , bottom_skin_expand_distance(mesh.settings.get("bottom_skin_expand_distance")) { } @@ -105,12 +106,12 @@ void SkinInfillAreaComputation::generateSkinAndInfillAreas() { SliceLayer& layer = mesh.layers[layer_nr]; - if (!process_infill && bottom_layer_count == 0 && top_layer_count == 0) + if (! process_infill && bottom_layer_count == 0 && top_layer_count == 0) { return; } - for(SliceLayerPart& part : layer.parts) + for (SliceLayerPart& part : layer.parts) { generateSkinAndInfillAreas(part); } @@ -124,7 +125,7 @@ void SkinInfillAreaComputation::generateSkinAndInfillAreas() */ void SkinInfillAreaComputation::generateSkinAndInfillAreas(SliceLayerPart& part) { - //Make a copy of the outline which we later intersect and union with the resized skins to ensure the resized skin isn't too large or removed completely. + // Make a copy of the outline which we later intersect and union with the resized skins to ensure the resized skin isn't too large or removed completely. Polygons top_skin; if (top_layer_count > 0) { @@ -141,7 +142,7 @@ void SkinInfillAreaComputation::generateSkinAndInfillAreas(SliceLayerPart& part) applySkinExpansion(part.inner_area, top_skin, bottom_skin); - //Now combine the resized top skin and bottom skin. + // Now combine the resized top skin and bottom skin. Polygons skin = top_skin.unionPolygons(bottom_skin); skin.removeSmallAreas(MIN_AREA_SIZE); @@ -175,9 +176,9 @@ void SkinInfillAreaComputation::calculateBottomSkin(const SliceLayerPart& part, { return; // don't subtract anything form the downskin } - LayerIndex bottom_check_start_layer_idx { std::max(LayerIndex { 0 }, LayerIndex { layer_nr - bottom_layer_count }) }; + LayerIndex bottom_check_start_layer_idx{ std::max(LayerIndex{ 0 }, LayerIndex{ layer_nr - bottom_layer_count }) }; Polygons not_air = getOutlineOnLayer(part, bottom_check_start_layer_idx); - if (!no_small_gaps_heuristic) + if (! no_small_gaps_heuristic) { for (int downskin_layer_nr = bottom_check_start_layer_idx + 1; downskin_layer_nr < layer_nr; downskin_layer_nr++) { @@ -194,15 +195,15 @@ void SkinInfillAreaComputation::calculateBottomSkin(const SliceLayerPart& part, void SkinInfillAreaComputation::calculateTopSkin(const SliceLayerPart& part, Polygons& upskin) { - if(layer_nr > LayerIndex(mesh.layers.size()) - top_layer_count || top_layer_count <= 0) + if (layer_nr > LayerIndex(mesh.layers.size()) - top_layer_count || top_layer_count <= 0) { - //If we're in the very top layers (less than top_layer_count from the top of the mesh) everything will be top skin anyway, so no need to generate infill. Just take the original inner contour. - //If top_layer_count is 0, no need to calculate anything either. + // If we're in the very top layers (less than top_layer_count from the top of the mesh) everything will be top skin anyway, so no need to generate infill. Just take the + // original inner contour. If top_layer_count is 0, no need to calculate anything either. return; } Polygons not_air = getOutlineOnLayer(part, layer_nr + top_layer_count); - if (!no_small_gaps_heuristic) + if (! no_small_gaps_heuristic) { for (int upskin_layer_nr = layer_nr + 1; upskin_layer_nr < layer_nr + top_layer_count; upskin_layer_nr++) { @@ -230,17 +231,17 @@ void SkinInfillAreaComputation::applySkinExpansion(const Polygons& original_outl const coord_t min_width = mesh.settings.get("min_skin_width_for_expansion") / 2; // Expand some areas of the skin for Skin Expand Distance. - if(min_width > 0) + if (min_width > 0) { // This performs an opening operation by first insetting by the minimum width, then offsetting with the same width. // The expansion is only applied to that opened shape. - if(bottom_skin_expand_distance != 0) + if (bottom_skin_expand_distance != 0) { const Polygons expanded = downskin.offset(-min_width).offset(min_width + bottom_skin_expand_distance); // And then re-joined with the original part that was not offset, to retain parts smaller than min_width. downskin = downskin.unionPolygons(expanded); } - if(top_skin_expand_distance != 0) + if (top_skin_expand_distance != 0) { const Polygons expanded = upskin.offset(-min_width).offset(min_width + top_skin_expand_distance); upskin = upskin.unionPolygons(expanded); @@ -248,11 +249,11 @@ void SkinInfillAreaComputation::applySkinExpansion(const Polygons& original_outl } else // No need to pay attention to minimum width. Just expand. { - if(bottom_skin_expand_distance != 0) + if (bottom_skin_expand_distance != 0) { downskin = downskin.offset(bottom_skin_expand_distance); } - if(top_skin_expand_distance != 0) + if (top_skin_expand_distance != 0) { upskin = upskin.offset(top_skin_expand_distance); } @@ -276,22 +277,24 @@ void SkinInfillAreaComputation::applySkinExpansion(const Polygons& original_outl // as the final polygon is limited (intersected) with the original polygon. constexpr double MITER_LIMIT = 10000000.0; // Remove thin pieces of support for Skin Removal Width. - if(bottom_skin_preshrink > 0 || (min_width == 0 && bottom_skin_expand_distance != 0)) + if (bottom_skin_preshrink > 0 || (min_width == 0 && bottom_skin_expand_distance != 0)) { - downskin = downskin.offset(-bottom_skin_preshrink / 2, ClipperLib::jtMiter, MITER_LIMIT).offset(bottom_skin_preshrink / 2, ClipperLib::jtMiter, MITER_LIMIT).intersection(downskin); - should_bottom_be_clipped = true; // Rounding errors can lead to propagation of errors. This could mean that skin goes beyond the original outline + downskin = downskin.offset(-bottom_skin_preshrink / 2, ClipperLib::jtMiter, MITER_LIMIT) + .offset(bottom_skin_preshrink / 2, ClipperLib::jtMiter, MITER_LIMIT) + .intersection(downskin); + should_bottom_be_clipped = true; // Rounding errors can lead to propagation of errors. This could mean that skin goes beyond the original outline } - if(top_skin_preshrink > 0 || (min_width == 0 && top_skin_expand_distance != 0)) + if (top_skin_preshrink > 0 || (min_width == 0 && top_skin_expand_distance != 0)) { upskin = upskin.offset(-top_skin_preshrink / 2, ClipperLib::jtMiter, MITER_LIMIT).offset(top_skin_preshrink / 2, ClipperLib::jtMiter, MITER_LIMIT); - should_top_be_clipped = true; // Rounding errors can lead to propagation of errors. This could mean that skin goes beyond the original outline + should_top_be_clipped = true; // Rounding errors can lead to propagation of errors. This could mean that skin goes beyond the original outline } - if(should_bottom_be_clipped) + if (should_bottom_be_clipped) { downskin = downskin.intersection(original_outline); } - if(should_top_be_clipped) + if (should_top_be_clipped) { upskin = upskin.intersection(original_outline); } @@ -305,7 +308,7 @@ void SkinInfillAreaComputation::applySkinExpansion(const Polygons& original_outl */ void SkinInfillAreaComputation::generateInfill(SliceLayerPart& part, const Polygons& skin) { - part.infill_area = part.inner_area.difference(skin); //Generate infill everywhere where there wasn't any skin. + part.infill_area = part.inner_area.difference(skin); // Generate infill everywhere where there wasn't any skin. part.infill_area.removeSmallAreas(MIN_AREA_SIZE); } @@ -317,7 +320,7 @@ void SkinInfillAreaComputation::generateInfill(SliceLayerPart& part, const Polyg */ void SkinInfillAreaComputation::generateRoofingFillAndSkinFill(SliceLayerPart& part) { - for(SkinPart& skin_part : part.skin_parts) + for (SkinPart& skin_part : part.skin_parts) { const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); const coord_t skin_overlap = mesh.settings.get("skin_overlap_mm"); @@ -345,7 +348,7 @@ Polygons SkinInfillAreaComputation::generateFilledAreaAbove(SliceLayerPart& part const size_t wall_idx = std::min(size_t(2), mesh.settings.get("wall_line_count")); Polygons filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); - if (!no_small_gaps_heuristic) + if (! no_small_gaps_heuristic) { for (int layer_nr_above = layer_nr + 1; layer_nr_above < layer_nr + roofing_layer_count; layer_nr_above++) { @@ -363,7 +366,7 @@ Polygons SkinInfillAreaComputation::generateFilledAreaAbove(SliceLayerPart& part // set air_below to the skin area for the current layer that has air below it Polygons air_below = getOutlineOnLayer(part, layer_nr).difference(getOutlineOnLayer(part, layer_nr - 1)); - if (!air_below.empty()) + if (! air_below.empty()) { // add the polygons that have air below to the no air above polygons filled_area_above = filled_area_above.unionPolygons(air_below); @@ -378,34 +381,34 @@ Polygons SkinInfillAreaComputation::generateFilledAreaAbove(SliceLayerPart& part * * this function may only read the skin and infill from the *current* layer. */ - Polygons SkinInfillAreaComputation::generateFilledAreaBelow(SliceLayerPart& part, size_t flooring_layer_count) +Polygons SkinInfillAreaComputation::generateFilledAreaBelow(SliceLayerPart& part, size_t flooring_layer_count) +{ + if (layer_nr < flooring_layer_count) { - if (layer_nr < flooring_layer_count) - { - return {}; - } - constexpr size_t min_wall_line_count = 2; - const int lowest_flooring_layer = layer_nr - flooring_layer_count; - Polygons filled_area_below = getOutlineOnLayer(part, lowest_flooring_layer); + return {}; + } + constexpr size_t min_wall_line_count = 2; + const int lowest_flooring_layer = layer_nr - flooring_layer_count; + Polygons filled_area_below = getOutlineOnLayer(part, lowest_flooring_layer); - if (!no_small_gaps_heuristic) + if (! no_small_gaps_heuristic) + { + const int next_lowest_flooring_layer = lowest_flooring_layer + 1; + for (int layer_nr_below = next_lowest_flooring_layer; layer_nr_below < layer_nr; layer_nr_below++) { - const int next_lowest_flooring_layer = lowest_flooring_layer + 1; - for (int layer_nr_below = next_lowest_flooring_layer; layer_nr_below < layer_nr; layer_nr_below++) - { - Polygons outlines_below = getOutlineOnLayer(part, layer_nr_below); - filled_area_below = filled_area_below.intersection(outlines_below); - } + Polygons outlines_below = getOutlineOnLayer(part, layer_nr_below); + filled_area_below = filled_area_below.intersection(outlines_below); } - return filled_area_below; } + return filled_area_below; +} void SkinInfillAreaComputation::generateInfillSupport(SliceMeshStorage& mesh) { const coord_t layer_height = mesh.settings.get("layer_height"); const AngleRadians support_angle = mesh.settings.get("infill_support_angle"); - const double tan_angle = tan(support_angle) - 0.01; //The X/Y component of the support angle. 0.01 to make 90 degrees work too. - const coord_t max_dist_from_lower_layer = tan_angle * layer_height; //Maximum horizontal distance that can be bridged. + const double tan_angle = tan(support_angle) - 0.01; // The X/Y component of the support angle. 0.01 to make 90 degrees work too. + const coord_t max_dist_from_lower_layer = tan_angle * layer_height; // Maximum horizontal distance that can be bridged. for (int layer_idx = mesh.layers.size() - 2; layer_idx >= 0; layer_idx--) { @@ -443,15 +446,17 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) { // no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density] float layer_skip_count = 8; // skip every so many layers as to ignore small gaps in the model making computation more easy - if (!mesh.settings.get("skin_no_small_gaps_heuristic")) + if (! mesh.settings.get("skin_no_small_gaps_heuristic")) { layer_skip_count = 1; } const coord_t gradual_infill_step_height = mesh.settings.get("gradual_infill_step_height"); - const size_t gradual_infill_step_layer_count = round_divide(gradual_infill_step_height, mesh.settings.get("layer_height")); // The difference in layer count between consecutive density infill areas + const size_t gradual_infill_step_layer_count + = round_divide(gradual_infill_step_height, mesh.settings.get("layer_height")); // The difference in layer count between consecutive density infill areas // make gradual_infill_step_height divisible by layer_skip_count - float n_skip_steps_per_gradual_step = std::max(1.0f, std::ceil(gradual_infill_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_infill_step_layer_count + float n_skip_steps_per_gradual_step + = std::max(1.0f, std::ceil(gradual_infill_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_infill_step_layer_count layer_skip_count = gradual_infill_step_layer_count / n_skip_steps_per_gradual_step; const size_t max_infill_steps = mesh.settings.get("gradual_infill_steps"); @@ -469,7 +474,15 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) { assert((part.infill_area_per_combine_per_density.empty() && "infill_area_per_combine_per_density is supposed to be uninitialized")); - const Polygons& infill_area = Infill::generateWallToolPaths(part.infill_wall_toolpaths, part.getOwnInfillArea(), infill_wall_count, infill_wall_width, infill_overlap, mesh.settings, layer_idx, SectionType::SKIN); + const Polygons& infill_area = Infill::generateWallToolPaths( + part.infill_wall_toolpaths, + part.getOwnInfillArea(), + infill_wall_count, + infill_wall_width, + infill_overlap, + mesh.settings, + layer_idx, + SectionType::SKIN); if (infill_area.empty() || layer_idx < min_layer || layer_idx > max_layer) { // initialize infill_area_per_combine_per_density empty @@ -495,7 +508,7 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) Polygons relevent_upper_polygons; for (const SliceLayerPart& upper_layer_part : upper_layer.parts) { - if (!upper_layer_part.boundaryBox.hit(part.boundaryBox)) + if (! upper_layer_part.boundaryBox.hit(part.boundaryBox)) { continue; } @@ -517,21 +530,26 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) std::vector& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back(); infill_area_per_combine_current_density.push_back(infill_area); part.infill_area_own = std::nullopt; // clear infill_area_own, it's not needed any more. - assert(!part.infill_area_per_combine_per_density.empty() && "infill_area_per_combine_per_density is now initialized"); + assert(! part.infill_area_per_combine_per_density.empty() && "infill_area_per_combine_per_density is now initialized"); } } } void SkinInfillAreaComputation::combineInfillLayers(SliceMeshStorage& mesh) { - if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast(mesh.settings.get("top_layers")) || mesh.settings.get("infill_line_distance") == 0) //No infill is even generated. + if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast(mesh.settings.get("top_layers")) + || mesh.settings.get("infill_line_distance") == 0) // No infill is even generated. { return; } const coord_t layer_height = mesh.settings.get("layer_height"); - const size_t amount = std::max(uint64_t(1), round_divide(mesh.settings.get("infill_sparse_thickness"), std::max(layer_height, coord_t(1)))); //How many infill layers to combine to obtain the requested sparse thickness. - if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid. + const size_t amount = std::max( + uint64_t(1), + round_divide( + mesh.settings.get("infill_sparse_thickness"), + std::max(layer_height, coord_t(1)))); // How many infill layers to combine to obtain the requested sparse thickness. + if (amount <= 1) // If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid. { return; } @@ -542,15 +560,15 @@ void SkinInfillAreaComputation::combineInfillLayers(SliceMeshStorage& mesh) other. */ size_t bottom_most_layers = mesh.settings.get("initial_bottom_layers"); LayerIndex min_layer = static_cast(bottom_most_layers + amount) - 1; - min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine. + min_layer -= min_layer % amount; // Round upwards to the nearest layer divisible by infill_sparse_combine. LayerIndex max_layer = static_cast(mesh.layers.size()) - 1 - mesh.settings.get("top_layers"); - max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine. - for(LayerIndex layer_idx = min_layer; layer_idx <= max_layer; layer_idx += amount) //Skip every few layers, but extrude more. + max_layer -= max_layer % amount; // Round downwards to the nearest layer divisible by infill_sparse_combine. + for (LayerIndex layer_idx = min_layer; layer_idx <= max_layer; layer_idx += amount) // Skip every few layers, but extrude more. { SliceLayer* layer = &mesh.layers[layer_idx]; - for(size_t combine_count_here = 1; combine_count_here < amount; combine_count_here++) + for (size_t combine_count_here = 1; combine_count_here < amount; combine_count_here++) { - if(layer_idx < static_cast(combine_count_here)) + if (layer_idx < static_cast(combine_count_here)) { break; } @@ -571,10 +589,10 @@ void SkinInfillAreaComputation::combineInfillLayers(SliceMeshStorage& mesh) { if (part.boundaryBox.hit(lower_layer_part.boundaryBox)) { - Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.infill_area).offset(-200).offset(200); result.add(intersection); // add area to be thickened - infill_area_per_combine[combine_count_here - 1] = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here + infill_area_per_combine[combine_count_here - 1] + = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here unsigned int max_lower_density_idx = density_idx; // Generally: remove only from *same density* areas on layer below // If there are no same density areas, then it's ok to print them anyway @@ -588,10 +606,13 @@ void SkinInfillAreaComputation::combineInfillLayers(SliceMeshStorage& mesh) // of the lower layer with the same or higher density index max_lower_density_idx = lower_layer_part.infill_area_per_combine_per_density.size() - 1; } - for (size_t lower_density_idx = density_idx; lower_density_idx <= max_lower_density_idx && lower_density_idx < lower_layer_part.infill_area_per_combine_per_density.size(); lower_density_idx++) + for (size_t lower_density_idx = density_idx; + lower_density_idx <= max_lower_density_idx && lower_density_idx < lower_layer_part.infill_area_per_combine_per_density.size(); + lower_density_idx++) { std::vector& lower_infill_area_per_combine = lower_layer_part.infill_area_per_combine_per_density[lower_density_idx]; - lower_infill_area_per_combine[0] = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (single thickness) layer + lower_infill_area_per_combine[0] + = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (single thickness) layer } } } @@ -610,9 +631,10 @@ void SkinInfillAreaComputation::combineInfillLayers(SliceMeshStorage& mesh) * this function may only read/write the skin and infill from the *current* layer. */ -void SkinInfillAreaComputation::generateTopAndBottomMostSkinFill(SliceLayerPart &part) { - - for (SkinPart& skin_part : part.skin_parts) { +void SkinInfillAreaComputation::generateTopAndBottomMostSkinFill(SliceLayerPart& part) +{ + for (SkinPart& skin_part : part.skin_parts) + { Polygons filled_area_above = generateFilledAreaAbove(part, 1); skin_part.top_most_surface_fill = skin_part.outline.difference(filled_area_above); @@ -622,4 +644,4 @@ void SkinInfillAreaComputation::generateTopAndBottomMostSkinFill(SliceLayerPart } -}//namespace cura +} // namespace cura diff --git a/src/support.cpp b/src/support.cpp index 77b53d61d1..e20aaac53c 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1,20 +1,7 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include // sqrt, round -#include -#include // ifstream.good() -#include // pair - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "support.h" #include "Application.h" //To get settings. #include "BoostInterface.hpp" @@ -31,13 +18,27 @@ #include "settings/types/Ratio.h" #include "sliceDataStorage.h" #include "slicer.h" -#include "support.h" #include "utils/Simplify.h" #include "utils/ThreadPool.h" #include "utils/VoronoiUtils.h" #include "utils/math.h" #include "utils/views/get.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // sqrt, round +#include +#include // ifstream.good() +#include // pair + namespace cura { @@ -53,7 +54,8 @@ bool AreaSupport::handleSupportModifierMesh(SliceDataStorage& storage, const Set SUPPORT_DROP_DOWN, SUPPORT_VANILLA }; - ModifierType modifier_type = (mesh_settings.get("anti_overhang_mesh")) ? ANTI_OVERHANG : ((mesh_settings.get("support_mesh_drop_down")) ? SUPPORT_DROP_DOWN : SUPPORT_VANILLA); + ModifierType modifier_type + = (mesh_settings.get("anti_overhang_mesh")) ? ANTI_OVERHANG : ((mesh_settings.get("support_mesh_drop_down")) ? SUPPORT_DROP_DOWN : SUPPORT_VANILLA); for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++) { SupportLayer& support_layer = storage.support.supportLayers[layer_nr]; @@ -75,7 +77,10 @@ bool AreaSupport::handleSupportModifierMesh(SliceDataStorage& storage, const Set } -void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts(SliceDataStorage& storage, const std::vector& global_support_areas_per_layer, unsigned int total_layer_count) +void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( + SliceDataStorage& storage, + const std::vector& global_support_areas_per_layer, + unsigned int total_layer_count) { if (total_layer_count == 0) { @@ -192,11 +197,13 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) const coord_t wall_width = infill_extruder.settings.get("support_line_width"); // no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density] - double layer_skip_count { 8.0 }; // skip every so many layers as to ignore small gaps in the model making computation more easy - size_t gradual_support_step_layer_count = round_divide(gradual_support_step_height, mesh_group_settings.get("layer_height")); // The difference in layer count between consecutive density infill areas. + double layer_skip_count{ 8.0 }; // skip every so many layers as to ignore small gaps in the model making computation more easy + size_t gradual_support_step_layer_count + = round_divide(gradual_support_step_height, mesh_group_settings.get("layer_height")); // The difference in layer count between consecutive density infill areas. // make gradual_support_step_height divisable by layer_skip_count - const auto n_skip_steps_per_gradual_step = std::max(1.0, std::ceil(gradual_support_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_support_step_layer_count + const auto n_skip_steps_per_gradual_step + = std::max(1.0, std::ceil(gradual_support_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_support_step_layer_count layer_skip_count = gradual_support_step_layer_count / n_skip_steps_per_gradual_step; LayerIndex min_layer = 0; @@ -222,15 +229,23 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) continue; } // NOTE: This both generates the walls _and_ returns the _actual_ infill area (the one _without_ walls) for use in the rest of the method. - const Polygons infill_area = Infill::generateWallToolPaths(support_infill_part.wall_toolpaths, original_area, support_infill_part.inset_count_to_generate, wall_width, 0, infill_extruder.settings, layer_nr, SectionType::SUPPORT); + const Polygons infill_area = Infill::generateWallToolPaths( + support_infill_part.wall_toolpaths, + original_area, + support_infill_part.inset_count_to_generate, + wall_width, + 0, + infill_extruder.settings, + layer_nr, + SectionType::SUPPORT); const AABB& this_part_boundary_box = support_infill_part.outline_boundary_box; // calculate density areas for this island Polygons less_dense_support = infill_area; // one step less dense with each density_step for (unsigned int density_step = 0; density_step < max_density_steps; ++density_step) { - LayerIndex min_layer { layer_nr + density_step * gradual_support_step_layer_count + static_cast(layer_skip_count) }; - LayerIndex max_layer { layer_nr + (density_step + 1) * gradual_support_step_layer_count }; + LayerIndex min_layer{ layer_nr + density_step * gradual_support_step_layer_count + static_cast(layer_skip_count) }; + LayerIndex max_layer{ layer_nr + (density_step + 1) * gradual_support_step_layer_count }; for (float upper_layer_idx = min_layer; upper_layer_idx <= max_layer; upper_layer_idx += layer_skip_count) { @@ -311,7 +326,8 @@ void AreaSupport::combineSupportInfillLayers(SliceDataStorage& storage) const coord_t layer_height = mesh_group_settings.get("layer_height"); // How many support infill layers to combine to obtain the requested sparse thickness. const ExtruderTrain& infill_extruder = mesh_group_settings.get("support_infill_extruder_nr"); - const size_t combine_layers_amount = std::max(uint64_t(1), round_divide(infill_extruder.settings.get("support_infill_sparse_thickness"), std::max(layer_height, coord_t(1)))); + const size_t combine_layers_amount + = std::max(uint64_t(1), round_divide(infill_extruder.settings.get("support_infill_sparse_thickness"), std::max(layer_height, coord_t(1)))); if (combine_layers_amount <= 1) { return; @@ -373,7 +389,8 @@ void AreaSupport::combineSupportInfillLayers(SliceDataStorage& storage) } result.add(intersection); // add area to be thickened - infill_area_per_combine[combine_count_here - 1] = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here + infill_area_per_combine[combine_count_here - 1] + = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here unsigned int max_lower_density_idx = density_idx; // Generally: remove only from *same density* areas on layer below @@ -388,10 +405,13 @@ void AreaSupport::combineSupportInfillLayers(SliceDataStorage& storage) // of the lower layer with the same or higher density index max_lower_density_idx = lower_layer_part.infill_area_per_combine_per_density.size() - 1; } - for (unsigned int lower_density_idx = density_idx; lower_density_idx <= max_lower_density_idx && lower_density_idx < lower_layer_part.infill_area_per_combine_per_density.size(); lower_density_idx++) + for (unsigned int lower_density_idx = density_idx; + lower_density_idx <= max_lower_density_idx && lower_density_idx < lower_layer_part.infill_area_per_combine_per_density.size(); + lower_density_idx++) { std::vector& lower_infill_area_per_combine = lower_layer_part.infill_area_per_combine_per_density[lower_density_idx]; - lower_infill_area_per_combine[0] = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (single thickness) layer + lower_infill_area_per_combine[0] + = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (single thickness) layer } } @@ -497,7 +517,7 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp const std::vector is_extruder_used = storage.getExtrudersUsed(); for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { - if (! is_extruder_used[extruder_nr]) //Unused extruders and the primary adhesion extruder don't generate an extra skirt line. + if (! is_extruder_used[extruder_nr]) // Unused extruders and the primary adhesion extruder don't generate an extra skirt line. { continue; } @@ -517,13 +537,15 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp for (ExtruderTrain* skirt_brim_extruder_p : skirt_brim_extruders) { ExtruderTrain& skirt_brim_extruder = *skirt_brim_extruder_p; - adhesion_size = std::max(adhesion_size, coord_t( - skirt_brim_extruder.settings.get(adhesion_width_str) - + skirt_brim_extruder.settings.get("skirt_brim_line_width") - * (skirt_brim_extruder.settings.get(adhesion_line_count_str) - 1) // - 1 because the line is also included in extra_skirt_line_width - * skirt_brim_extruder.settings.get("initial_layer_line_width_factor") - + extra_skirt_line_width)); - } + adhesion_size = std::max( + adhesion_size, + coord_t( + skirt_brim_extruder.settings.get(adhesion_width_str) + + skirt_brim_extruder.settings.get("skirt_brim_line_width") + * (skirt_brim_extruder.settings.get(adhesion_line_count_str) - 1) // - 1 because the line is also included in extra_skirt_line_width + * skirt_brim_extruder.settings.get("initial_layer_line_width_factor") + + extra_skirt_line_width)); + } break; case EPlatformAdhesion::RAFT: { @@ -563,11 +585,10 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp const coord_t min_even_wall_line_width = infill_settings.get("min_even_wall_line_width"); auto half_min_feature_width = min_even_wall_line_width + 10; - joined = joined - .offset(-half_min_feature_width) - .offset(join_distance + half_min_feature_width, ClipperLib::jtRound) - .offset(-join_distance, ClipperLib::jtRound) - .unionPolygons(joined); + joined = joined.offset(-half_min_feature_width) + .offset(join_distance + half_min_feature_width, ClipperLib::jtRound) + .offset(-join_distance, ClipperLib::jtRound) + .unionPolygons(joined); } const Simplify simplify(infill_settings); @@ -634,7 +655,8 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) Settings* bottom_settings = &storage.meshes[mesh_idx].settings; if (mesh.settings.get("support_mesh")) { - if ((mesh.settings.get("support_mesh_drop_down") && support_meshes_drop_down_handled) || (! mesh.settings.get("support_mesh_drop_down") && support_meshes_handled)) + if ((mesh.settings.get("support_mesh_drop_down") && support_meshes_drop_down_handled) + || (! mesh.settings.get("support_mesh_drop_down") && support_meshes_handled)) { // handle all support_mesh and support_mesh_drop_down areas only once continue; } @@ -727,8 +749,11 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) std::ifstream cross_fs(cross_subdisivion_spec_image_file.c_str()); if (cross_subdisivion_spec_image_file != "" && cross_fs.good()) { - storage.support.cross_fill_provider = - new SierpinskiFillProvider(aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width"), cross_subdisivion_spec_image_file); + storage.support.cross_fill_provider = new SierpinskiFillProvider( + aabb, + infill_extruder.settings.get("support_line_distance"), + infill_extruder.settings.get("support_line_width"), + cross_subdisivion_spec_image_file); } else { @@ -736,7 +761,8 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) { spdlog::error("Cannot find density image: {}.", cross_subdisivion_spec_image_file); } - storage.support.cross_fill_provider = new SierpinskiFillProvider(aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width")); + storage.support.cross_fill_provider + = new SierpinskiFillProvider(aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width")); } } } @@ -780,22 +806,23 @@ void AreaSupport::generateOverhangAreasForMesh(SliceDataStorage& storage, SliceM } // Generate the actual areas and store them in the mesh. - cura::parallel_for(1, - storage.print_layer_count, - [&](const size_t layer_idx) - { - std::pair basic_and_full_overhang = computeBasicAndFullOverhang(storage, mesh, layer_idx); - mesh.overhang_areas[layer_idx] = basic_and_full_overhang.first; // Store the results. - mesh.full_overhang_areas[layer_idx] = basic_and_full_overhang.second; - scripta::log("support_basic_overhang_area", basic_and_full_overhang.first, SectionType::SUPPORT, layer_idx); - scripta::log("support_full_overhang_area", basic_and_full_overhang.second, SectionType::SUPPORT, layer_idx); - }); + cura::parallel_for( + 1, + storage.print_layer_count, + [&](const size_t layer_idx) + { + std::pair basic_and_full_overhang = computeBasicAndFullOverhang(storage, mesh, layer_idx); + mesh.overhang_areas[layer_idx] = basic_and_full_overhang.first; // Store the results. + mesh.full_overhang_areas[layer_idx] = basic_and_full_overhang.second; + scripta::log("support_basic_overhang_area", basic_and_full_overhang.first, SectionType::SUPPORT, layer_idx); + scripta::log("support_full_overhang_area", basic_and_full_overhang.second, SectionType::SUPPORT, layer_idx); + }); } Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& storage, const Settings& infill_settings, const LayerIndex layer_idx) { const auto& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const Simplify simplify { mesh_group_settings }; + const Simplify simplify{ mesh_group_settings }; const auto layer_thickness = mesh_group_settings.get("layer_height"); const auto support_distance_top = static_cast(mesh_group_settings.get("support_top_distance")); const auto support_distance_bot = static_cast(mesh_group_settings.get("support_bottom_distance")); @@ -807,9 +834,7 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st constexpr coord_t close_dist = snap_radius + 5; // needs to be larger than the snap radius! constexpr coord_t search_radius = 0; - auto layer_current = simplify.polygon(storage.layers[layer_idx].getOutlines() - .offset(-close_dist) - .offset(close_dist)); + auto layer_current = simplify.polygon(storage.layers[layer_idx].getOutlines().offset(-close_dist).offset(close_dist)); // sparse grid for storing the offset distances at each point. For each point there can be multiple offset // values as multiple may be calculated when multiple layers are used for z-smoothing of the offsets. @@ -818,7 +843,7 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st // equal to commutative_offset / n. using point_pair_t = std::pair; using grid_t = SparsePointGridInclusive; - grid_t offset_dist_at_point { snap_radius }; + grid_t offset_dist_at_point{ snap_radius }; // Collection of the various areas we used to calculate the areas for. This is a combination // - the support distance (this is the support top distance for overhang areas, and support @@ -829,46 +854,26 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st // here either the slope or overhang area is stored std::vector> z_distances_layer_deltas; - constexpr LayerIndex layer_index_offset { 1 }; + constexpr LayerIndex layer_index_offset{ 1 }; - const LayerIndex layer_idx_below { std::max(LayerIndex{ layer_idx - layer_index_offset }, LayerIndex { 0 }) }; + const LayerIndex layer_idx_below{ std::max(LayerIndex{ layer_idx - layer_index_offset }, LayerIndex{ 0 }) }; if (layer_idx_below != layer_idx) { - auto layer_below = simplify.polygon(storage.layers[layer_idx_below].getOutlines() - .offset(-close_dist) - .offset(close_dist)); - - z_distances_layer_deltas.emplace_back( - support_distance_top, - static_cast(layer_index_offset * layer_thickness), - layer_current.difference(layer_below) - ); - - z_distances_layer_deltas.emplace_back( - support_distance_bot, - static_cast(layer_index_offset * layer_thickness), - layer_below.difference(layer_current) - ); + auto layer_below = simplify.polygon(storage.layers[layer_idx_below].getOutlines().offset(-close_dist).offset(close_dist)); + + z_distances_layer_deltas.emplace_back(support_distance_top, static_cast(layer_index_offset * layer_thickness), layer_current.difference(layer_below)); + + z_distances_layer_deltas.emplace_back(support_distance_bot, static_cast(layer_index_offset * layer_thickness), layer_below.difference(layer_current)); } - const LayerIndex layer_idx_above { std::min(LayerIndex{ layer_idx + layer_index_offset }, LayerIndex { storage.layers.size() - 1 }) }; + const LayerIndex layer_idx_above{ std::min(LayerIndex{ layer_idx + layer_index_offset }, LayerIndex{ storage.layers.size() - 1 }) }; if (layer_idx_above != layer_idx) { - auto layer_above = simplify.polygon(storage.layers[layer_idx_below].getOutlines() - .offset(-close_dist) - .offset(close_dist)); - - z_distances_layer_deltas.emplace_back( - support_distance_bot, - static_cast(layer_index_offset * layer_thickness), - layer_current.difference(layer_above) - ); - - z_distances_layer_deltas.emplace_back( - support_distance_top, - static_cast(layer_index_offset * layer_thickness), - layer_above.difference(layer_current) - ); + auto layer_above = simplify.polygon(storage.layers[layer_idx_below].getOutlines().offset(-close_dist).offset(close_dist)); + + z_distances_layer_deltas.emplace_back(support_distance_bot, static_cast(layer_index_offset * layer_thickness), layer_current.difference(layer_above)); + + z_distances_layer_deltas.emplace_back(support_distance_top, static_cast(layer_index_offset * layer_thickness), layer_above.difference(layer_current)); } for (auto& [support_distance, delta_z, layer_delta_] : z_distances_layer_deltas) @@ -885,14 +890,14 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st } // grid for storing the "slope" (wall overhang area at that specific point in the polygon) - grid_t slope_at_point { snap_radius }; + grid_t slope_at_point{ snap_radius }; // construct a voronoi diagram. The slope is calculated based // on the edge length from the boundary to the center edge(s) std::vector segments; - for (auto [poly_idx, poly]: layer_delta | ranges::views::enumerate) + for (auto [poly_idx, poly] : layer_delta | ranges::views::enumerate) { - for (auto [point_idx, _p]: poly | ranges::views::enumerate) + for (auto [point_idx, _p] : poly | ranges::views::enumerate) { segments.emplace_back(&layer_delta, poly_idx, point_idx); } @@ -901,7 +906,7 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st boost::polygon::voronoi_diagram vonoroi_diagram; boost::polygon::construct_voronoi(segments.begin(), segments.end(), &vonoroi_diagram); - for (const auto& edge: vonoroi_diagram.edges()) + for (const auto& edge : vonoroi_diagram.edges()) { if (edge.is_infinite()) { @@ -931,8 +936,8 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st auto slope = dist_to_boundary / delta_z; auto nearby_vals = slope_at_point.getNearbyVals(p0, search_radius); - auto n = ranges::accumulate(nearby_vals | views::get( &point_pair_t::first ), 0); - auto cumulative_slope = ranges::accumulate(nearby_vals | views::get( &point_pair_t::second ), 0.); + auto n = ranges::accumulate(nearby_vals | views::get(&point_pair_t::first), 0); + auto cumulative_slope = ranges::accumulate(nearby_vals | views::get(&point_pair_t::second), 0.); n += 1; cumulative_slope += slope; @@ -941,13 +946,13 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st slope_at_point.insert(p0, { n, cumulative_slope }); } - for (const auto& poly: layer_current) + for (const auto& poly : layer_current) { - for (const auto& point: poly) + for (const auto& point : poly) { auto nearby_vals = slope_at_point.getNearbyVals(point, search_radius); - auto n = ranges::accumulate(nearby_vals | views::get( &point_pair_t::first ), 0); - auto cumulative_slope = ranges::accumulate(nearby_vals | views::get( &point_pair_t::second ), 0.); + auto n = ranges::accumulate(nearby_vals | views::get(&point_pair_t::first), 0); + auto cumulative_slope = ranges::accumulate(nearby_vals | views::get(&point_pair_t::second), 0.); if (n != 0) { @@ -960,26 +965,26 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st auto nearby_vals_offset_dist = offset_dist_at_point.getNearbyVals(point, search_radius); // update and insert cumulative varying xy distance in one go - offset_dist_at_point.insert(point, { - ranges::accumulate(nearby_vals_offset_dist | views::get( &point_pair_t::first ), 0) + 1, - ranges::accumulate(nearby_vals_offset_dist | views::get( &point_pair_t::second ), 0.) + xy_distance_varying - }); + offset_dist_at_point.insert( + point, + { ranges::accumulate(nearby_vals_offset_dist | views::get(&point_pair_t::first), 0) + 1, + ranges::accumulate(nearby_vals_offset_dist | views::get(&point_pair_t::second), 0.) + xy_distance_varying }); } } } } std::vector varying_offsets; - for (const auto& poly: layer_current) + for (const auto& poly : layer_current) { for (const auto& point : poly) { auto nearby_vals = offset_dist_at_point.getNearbyVals(point, search_radius); - auto n = ranges::accumulate(nearby_vals | views::get( &point_pair_t::first ), 0); - auto cumulative_offset_dist = ranges::accumulate(nearby_vals | views::get( &point_pair_t::second ), 0.); + auto n = ranges::accumulate(nearby_vals | views::get(&point_pair_t::first), 0); + auto cumulative_offset_dist = ranges::accumulate(nearby_vals | views::get(&point_pair_t::second), 0.); - double offset_dist {}; + double offset_dist{}; if (n == 0) { // if there are no offset dists generated for a vertex $p$ this must mean that vertex $p$ was not @@ -1005,7 +1010,8 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st // close operation to smooth the x/y disallowed area boundary. With varying xy distances we see some jumps in the boundary. // As the x/y disallowed areas "cut in" to support the xy-disallowed area may propagate through the support area. If the // x/y disallowed area is not smoothed boost has trouble generating a voronoi diagram. - .offset(smooth_dist).offset(-smooth_dist); + .offset(smooth_dist) + .offset(-smooth_dist); scripta::log("support_varying_xy_disallowed_areas", varying_xy_disallowed_areas, SectionType::SUPPORT, layer_idx); return varying_xy_disallowed_areas; } @@ -1016,23 +1022,26 @@ Polygons AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& st * - find overhang by looking at the difference between two consecutive layers * - join with support areas from layer above * - subtract current layer - * - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more stability) + * - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more + * stability) * - perform inset using X/Y-distance and bottom Z distance * * for support buildplate only: purge all support not connected to build plate */ -void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, - const Settings& infill_settings, - const Settings& roof_settings, - const Settings& bottom_settings, - const size_t mesh_idx, - const size_t layer_count, - std::vector& support_areas) +void AreaSupport::generateSupportAreasForMesh( + SliceDataStorage& storage, + const Settings& infill_settings, + const Settings& roof_settings, + const Settings& bottom_settings, + const size_t mesh_idx, + const size_t layer_count, + std::vector& support_areas) { SliceMeshStorage& mesh = storage.meshes[mesh_idx]; const ESupportStructure support_structure = mesh.settings.get("support_structure"); - const bool is_support_mesh_place_holder = mesh.settings.get("support_mesh"); // whether this mesh has empty SliceMeshStorage and this function is now called to only generate support for all support meshes + const bool is_support_mesh_place_holder + = mesh.settings.get("support_mesh"); // whether this mesh has empty SliceMeshStorage and this function is now called to only generate support for all support meshes if ((! mesh.settings.get("support_enable") || support_structure != ESupportStructure::NORMAL) && ! is_support_mesh_place_holder) { return; @@ -1062,7 +1071,8 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, // simplified processing for bottom layer - just ensure support clears part by XY distance const coord_t xy_distance = infill_settings.get("support_xy_distance"); const coord_t xy_distance_overhang = infill_settings.get("support_xy_distance_overhang"); - const bool use_xy_distance_overhang = infill_settings.get("support_xy_overrides_z") == SupportDistPriority::Z_OVERRIDES_XY; // whether to use a different xy distance at overhangs + const bool use_xy_distance_overhang + = infill_settings.get("support_xy_overrides_z") == SupportDistPriority::Z_OVERRIDES_XY; // whether to use a different xy distance at overhangs const AngleRadians angle = ((mesh.settings.get("support_roof_enable")) ? roof_settings : infill_settings).get("support_angle"); const double tan_angle = tan(angle) - 0.01; // the XY-component of the supportAngle constexpr bool no_support = false; @@ -1077,48 +1087,53 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, // The maximum width of an odd wall = 2 * minimum even wall width. auto half_min_feature_width = min_even_wall_line_width + 10; - cura::parallel_for(1, - layer_count, - [&](const size_t layer_idx) - { - const Polygons outlines = storage.getLayerOutlines(layer_idx, no_support, no_prime_tower); - - // Build sloped areas. We need this for the stair-stepping later on. - // Specifically, sloped areass are used in 'moveUpFromModel' to prevent a stair step happening over an area where there isn't a slope. - // This part here only concerns the slope between two layers. This will be post-processed later on (see the other parallel loop below). - 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) - // 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)) - // 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); - // The sloped areas are now ready to be post-processed. - scripta::log("support_sloped_areas", sloped_areas_per_layer[layer_idx], SectionType::SUPPORT, layer_idx, - scripta::CellVDI{"sloped_area_detection_width", sloped_area_detection_width }); - - if (! is_support_mesh_place_holder) - { // don't compute overhang for support meshes - if (use_xy_distance_overhang) // Z overrides XY distance. - { - // we also want to use the min XY distance when the support is resting on a sloped surface so we calculate the area of the - // layer below that protrudes beyond the current layer's area and combine it with the current layer's overhang disallowed area - - Polygons minimum_xy_disallowed_areas = xy_disallowed_per_layer[layer_idx].offset(xy_distance_overhang); - Polygons varying_xy_disallowed_areas = generateVaryingXYDisallowedArea(mesh, infill_settings, layer_idx); - xy_disallowed_per_layer[layer_idx] = minimum_xy_disallowed_areas.unionPolygons(varying_xy_disallowed_areas); - scripta::log("support_xy_disallowed_areas", xy_disallowed_per_layer[layer_idx], SectionType::SUPPORT, layer_idx); - } - } - if (is_support_mesh_place_holder || ! use_xy_distance_overhang) - { - xy_disallowed_per_layer[layer_idx] = outlines.offset(xy_distance); - } - }); + cura::parallel_for( + 1, + layer_count, + [&](const size_t layer_idx) + { + const Polygons outlines = storage.getLayerOutlines(layer_idx, no_support, no_prime_tower); + + // Build sloped areas. We need this for the stair-stepping later on. + // Specifically, sloped areass are used in 'moveUpFromModel' to prevent a stair step happening over an area where there isn't a slope. + // This part here only concerns the slope between two layers. This will be post-processed later on (see the other parallel loop below). + 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) + // 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)) + // 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); + // The sloped areas are now ready to be post-processed. + scripta::log( + "support_sloped_areas", + sloped_areas_per_layer[layer_idx], + SectionType::SUPPORT, + layer_idx, + scripta::CellVDI{ "sloped_area_detection_width", sloped_area_detection_width }); + + if (! is_support_mesh_place_holder) + { // don't compute overhang for support meshes + if (use_xy_distance_overhang) // Z overrides XY distance. + { + // we also want to use the min XY distance when the support is resting on a sloped surface so we calculate the area of the + // layer below that protrudes beyond the current layer's area and combine it with the current layer's overhang disallowed area + + Polygons minimum_xy_disallowed_areas = xy_disallowed_per_layer[layer_idx].offset(xy_distance_overhang); + Polygons varying_xy_disallowed_areas = generateVaryingXYDisallowedArea(mesh, infill_settings, layer_idx); + xy_disallowed_per_layer[layer_idx] = minimum_xy_disallowed_areas.unionPolygons(varying_xy_disallowed_areas); + scripta::log("support_xy_disallowed_areas", xy_disallowed_per_layer[layer_idx], SectionType::SUPPORT, layer_idx); + } + } + if (is_support_mesh_place_holder || ! use_xy_distance_overhang) + { + xy_disallowed_per_layer[layer_idx] = outlines.offset(xy_distance); + } + }); std::vector tower_roofs; Polygons stair_removal; // polygons to subtract from support because of stair-stepping @@ -1155,7 +1170,8 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, const coord_t z_distance_bottom = ((mesh.settings.get("support_bottom_enable")) ? bottom_settings : infill_settings).get("support_bottom_distance"); const size_t bottom_empty_layer_count = round_up_divide(z_distance_bottom, layer_thickness); // number of empty layers between support and model const coord_t bottom_stair_step_height = std::max(static_cast(0), mesh.settings.get("support_bottom_stair_step_height")); - const size_t bottom_stair_step_layer_count = bottom_stair_step_height / layer_thickness + 1; // the difference in layers between two stair steps. One is normal support (not stair-like) + const size_t bottom_stair_step_layer_count + = bottom_stair_step_height / layer_thickness + 1; // the difference in layers between two stair steps. One is normal support (not stair-like) // Post-process the sloped areas's. (Skip if no stair-stepping anyway.) // The idea here is to 'add up' all the sloped 'areas' so they form actual areas per each stair-step height. @@ -1219,7 +1235,8 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, { // join with support from layer up const Polygons empty; const Polygons* layer_above = (layer_idx < support_areas.size()) ? &support_areas[layer_idx + 1] : ∅ - const Polygons model_mesh_on_layer = (layer_idx > 0) && ! is_support_mesh_nondrop_place_holder ? storage.getLayerOutlines(layer_idx, no_support, no_prime_tower) : empty; + const Polygons model_mesh_on_layer + = (layer_idx > 0) && ! is_support_mesh_nondrop_place_holder ? storage.getLayerOutlines(layer_idx, no_support, no_prime_tower) : empty; if (is_support_mesh_nondrop_place_holder) { layer_above = ∅ @@ -1233,9 +1250,7 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, { for (PolygonsPart poly : layer_this.splitIntoParts()) { - const auto polygon_part = poly.difference(xy_disallowed_per_layer[layer_idx]) - .offset(-half_min_feature_width) - .offset(half_min_feature_width); + const auto polygon_part = poly.difference(xy_disallowed_per_layer[layer_idx]).offset(-half_min_feature_width).offset(half_min_feature_width); const int64_t part_area = polygon_part.area(); if (part_area == 0 || part_area > max_tower_supported_diameter * max_tower_supported_diameter) @@ -1259,7 +1274,15 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, } // Move up from model, handle stair-stepping. - moveUpFromModel(storage, stair_removal, sloped_areas_per_layer[layer_idx], layer_this, layer_idx, bottom_empty_layer_count, bottom_stair_step_layer_count, bottom_stair_step_width); + moveUpFromModel( + storage, + stair_removal, + sloped_areas_per_layer[layer_idx], + layer_this, + layer_idx, + bottom_empty_layer_count, + bottom_stair_step_layer_count, + bottom_stair_step_width); support_areas[layer_idx] = layer_this; Progress::messageProgress(Progress::Stage::SUPPORT, layer_count * (mesh_idx + 1) - layer_idx, layer_count * storage.meshes.size()); @@ -1335,24 +1358,26 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, // this is performed after the main support generation loop above, because it affects the joining of polygons // if this would be performed in the main loop then some support would not have been generated under the overhangs and consequently no support is generated for that, // meaning almost no support would be generated in some cases which definitely need support. - const int max_checking_layer_idx = std::max(0, std::min(static_cast(storage.support.supportLayers.size()), static_cast(layer_count - (layer_z_distance_top - 1)))); - - cura::parallel_for(0, - max_checking_layer_idx, - [&](const size_t layer_idx) - { - constexpr bool no_support = false; - constexpr bool no_prime_tower = false; - support_areas[layer_idx] = support_areas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layer_z_distance_top - 1, no_support, no_prime_tower)); - }); + const int max_checking_layer_idx + = std::max(0, std::min(static_cast(storage.support.supportLayers.size()), static_cast(layer_count - (layer_z_distance_top - 1)))); + + cura::parallel_for( + 0, + max_checking_layer_idx, + [&](const size_t layer_idx) + { + constexpr bool no_support = false; + constexpr bool no_prime_tower = false; + support_areas[layer_idx] = support_areas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layer_z_distance_top - 1, no_support, no_prime_tower)); + }); } // Procedure to remove floating support - for (size_t layer_idx = 1; layer_idx < layer_count - 1; layer_idx ++) + for (size_t layer_idx = 1; layer_idx < layer_count - 1; layer_idx++) { Polygons& layer_this = support_areas[layer_idx]; - if (!layer_this.empty()) + if (! layer_this.empty()) { Polygons& layer_below = support_areas[layer_idx - 1]; Polygons& layer_above = support_areas[layer_idx + 1]; @@ -1373,14 +1398,15 @@ void AreaSupport::generateSupportAreasForMesh(SliceDataStorage& storage, storage.support.generated = true; } -void AreaSupport::moveUpFromModel(const SliceDataStorage& storage, - Polygons& stair_removal, - Polygons& sloped_areas, - Polygons& support_areas, - const size_t layer_idx, - const size_t bottom_empty_layer_count, - const size_t bottom_stair_step_layer_count, - const coord_t support_bottom_stair_step_width) +void AreaSupport::moveUpFromModel( + const SliceDataStorage& storage, + Polygons& stair_removal, + Polygons& sloped_areas, + Polygons& support_areas, + const size_t layer_idx, + const size_t bottom_empty_layer_count, + const size_t bottom_stair_step_layer_count, + const coord_t support_bottom_stair_step_width) { // The idea behind support bottom stairs: // @@ -1416,7 +1442,8 @@ void AreaSupport::moveUpFromModel(const SliceDataStorage& storage, // support // ############################################################################ ┐ // AAAAAxxxxxxxxxxxxx │ - // CCCCCCCCCCCCCCCCCC########################################################## │ >>>>>>>>> result only applies stair step to first layer(s) of what woud normally be the stair step + // CCCCCCCCCCCCCCCCCC########################################################## │ >>>>>>>>> result only applies stair step to first layer(s) of what woud + // normally be the stair step // ^^--..__ │ // ^^--..__####################################################### ├> stair step height // ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ ^^--..__ │ @@ -1486,8 +1513,8 @@ std::pair AreaSupport::computeBasicAndFullOverhang(const Sli constexpr bool no_support = false; constexpr bool no_prime_tower = false; - constexpr double smooth_height = 0.4; //mm - const LayerIndex layers_below { static_cast(std::round(smooth_height / mesh.settings.get("layer_height"))) }; + constexpr double smooth_height = 0.4; // mm + const LayerIndex layers_below{ static_cast(std::round(smooth_height / mesh.settings.get("layer_height"))) }; const coord_t layer_height = mesh.settings.get("layer_height"); const AngleRadians support_angle = mesh.settings.get("support_angle"); @@ -1498,23 +1525,17 @@ std::pair AreaSupport::computeBasicAndFullOverhang(const Sli // To avoids generating support for textures on vertical surfaces, a moving average // is taken over smooth_height. The smooth_height is currently an educated guess // that we might want to expose to the frontend in the future. - Polygons outlines_below = - storage.getLayerOutlines(layer_idx - 1, no_support, no_prime_tower) - .offset(max_dist_from_lower_layer); - for (int layer_idx_offset = 2; layer_idx - layer_idx_offset >= 0 && layer_idx_offset <= layers_below; layer_idx_offset ++) + Polygons outlines_below = storage.getLayerOutlines(layer_idx - 1, no_support, no_prime_tower).offset(max_dist_from_lower_layer); + for (int layer_idx_offset = 2; layer_idx - layer_idx_offset >= 0 && layer_idx_offset <= layers_below; layer_idx_offset++) { - auto outlines_below_ = - storage.getLayerOutlines(layer_idx - layer_idx_offset, no_support, no_prime_tower) - .offset(max_dist_from_lower_layer * layer_idx_offset); + auto outlines_below_ = storage.getLayerOutlines(layer_idx - layer_idx_offset, no_support, no_prime_tower).offset(max_dist_from_lower_layer * layer_idx_offset); outlines_below = outlines_below.unionPolygons(outlines_below_); } - Polygons basic_overhang = - outlines - .difference(outlines_below); + Polygons basic_overhang = outlines.difference(outlines_below); const SupportLayer& support_layer = storage.support.supportLayers[layer_idx]; - if (!support_layer.anti_overhang.empty()) + if (! support_layer.anti_overhang.empty()) { // Merge anti overhang into one polygon, otherwise overlapping polygons // will create opposite effect. @@ -1523,10 +1544,9 @@ std::pair AreaSupport::computeBasicAndFullOverhang(const Sli basic_overhang = basic_overhang.difference(merged_polygons); } - Polygons overhang_extended = - basic_overhang - // +0.1mm for easier joining with support from layer above - .offset(max_dist_from_lower_layer * layers_below + MM2INT(0.1)); + Polygons overhang_extended = basic_overhang + // +0.1mm for easier joining with support from layer above + .offset(max_dist_from_lower_layer * layers_below + MM2INT(0.1)); Polygons full_overhang = overhang_extended.intersection(outlines); return std::make_pair(basic_overhang, full_overhang); @@ -1570,7 +1590,14 @@ void AreaSupport::detectOverhangPoints(const SliceDataStorage& storage, SliceMes } } -void AreaSupport::handleTowers(const Settings& settings, const Polygons& xy_disallowed_area, Polygons& supportLayer_this, std::vector& tower_roofs, std::vector>& overhang_points, LayerIndex layer_idx, size_t layer_count) +void AreaSupport::handleTowers( + const Settings& settings, + const Polygons& xy_disallowed_area, + Polygons& supportLayer_this, + std::vector& tower_roofs, + std::vector>& overhang_points, + LayerIndex layer_idx, + size_t layer_count) { LayerIndex layer_overhang_point = layer_idx + 1; // Start tower 1 layer below overhang point. if (layer_overhang_point >= static_cast(layer_count) - 1) @@ -1621,7 +1648,12 @@ void AreaSupport::handleTowers(const Settings& settings, const Polygons& xy_disa tower_roof_expansion_distance = layer_thickness / tan_tower_roof_angle; } - for (Polygons& tower_roof: tower_roofs | ranges::views::filter([](const auto& poly){ return ! poly.empty(); })) + for (Polygons& tower_roof : tower_roofs + | ranges::views::filter( + [](const auto& poly) + { + return ! poly.empty(); + })) { supportLayer_this = supportLayer_this.unionPolygons(tower_roof); @@ -1721,12 +1753,15 @@ void AreaSupport::generateSupportBottom(SliceDataStorage& storage, const SliceMe return; } const coord_t z_distance_bottom = round_up_divide(mesh.settings.get("support_bottom_distance"), layer_height); // Number of layers between support bottom and model. - const size_t skip_layer_count = std::max(uint64_t(1), round_divide(mesh.settings.get("support_interface_skip_height"), layer_height)); // Resolution of generating support bottoms above model. + const size_t skip_layer_count + = std::max(uint64_t(1), round_divide(mesh.settings.get("support_interface_skip_height"), layer_height)); // Resolution of generating support bottoms above model. const coord_t bottom_line_width = mesh_group_settings.get("support_bottom_extruder_nr").settings.get("support_bottom_line_width"); const coord_t bottom_outline_offset = mesh_group_settings.get("support_bottom_extruder_nr").settings.get("support_bottom_offset"); const size_t scan_count = std::max(size_t(1), (bottom_layer_count - 1) / skip_layer_count); // How many measurements to take to generate bottom areas. - const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(scan_count)); // How many layers to skip between measurements. Using float for better spread, but this is later rounded. + const float z_skip = std::max( + 1.0f, + float(bottom_layer_count - 1) / float(scan_count)); // How many layers to skip between measurements. Using float for better spread, but this is later rounded. const double minimum_bottom_area = mesh.settings.get("minimum_bottom_area"); std::vector& support_layers = storage.support.supportLayers; @@ -1755,18 +1790,23 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh return; } const coord_t z_distance_top = round_up_divide(mesh.settings.get("support_top_distance"), layer_height); // Number of layers between support roof and model. - const size_t skip_layer_count = std::max(uint64_t(1), round_divide(mesh.settings.get("support_interface_skip_height"), layer_height)); // Resolution of generating support roof below model. + const size_t skip_layer_count + = std::max(uint64_t(1), round_divide(mesh.settings.get("support_interface_skip_height"), layer_height)); // Resolution of generating support roof below model. const coord_t roof_line_width = mesh_group_settings.get("support_roof_extruder_nr").settings.get("support_roof_line_width"); const coord_t roof_outline_offset = mesh_group_settings.get("support_roof_extruder_nr").settings.get("support_roof_offset"); const size_t scan_count = std::max(size_t(1), (roof_layer_count - 1) / skip_layer_count); // How many measurements to take to generate roof areas. - const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(scan_count)); // How many layers to skip between measurements. Using float for better spread, but this is later rounded. + const float z_skip = std::max( + 1.0f, + float(roof_layer_count - 1) / float(scan_count)); // How many layers to skip between measurements. Using float for better spread, but this is later rounded. const double minimum_roof_area = mesh.settings.get("minimum_roof_area"); std::vector& support_layers = storage.support.supportLayers; for (LayerIndex layer_idx = 0; layer_idx < static_cast(support_layers.size() - z_distance_top); layer_idx++) { - const LayerIndex top_layer_idx_above { std::min(LayerIndex { support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) }; // Maximum layer of the model that generates support roof. + const LayerIndex top_layer_idx_above{ + std::min(LayerIndex{ support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) + }; // Maximum layer of the model that generates support roof. Polygons mesh_outlines; for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip) { @@ -1779,10 +1819,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh } // Remove support in between the support roof and the model. Subtracts the roof polygons from the support polygons on the layers above it. - for (auto [layer_idx, support_layer] : support_layers - | ranges::views::enumerate - | ranges::views::drop(1) - | ranges::views::drop_last(z_distance_top)) + for (auto [layer_idx, support_layer] : support_layers | ranges::views::enumerate | ranges::views::drop(1) | ranges::views::drop_last(z_distance_top)) { Polygons roof = support_layer.support_roof; @@ -1800,7 +1837,13 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh } } -void AreaSupport::generateSupportInterfaceLayer(Polygons& support_areas, const Polygons colliding_mesh_outlines, const coord_t safety_offset, const coord_t outline_offset, const double minimum_interface_area, Polygons& interface_polygons) +void AreaSupport::generateSupportInterfaceLayer( + Polygons& support_areas, + const Polygons colliding_mesh_outlines, + const coord_t safety_offset, + const coord_t outline_offset, + const double minimum_interface_area, + Polygons& interface_polygons) { Polygons model = colliding_mesh_outlines.unionPolygons(); interface_polygons = support_areas.offset(safety_offset / 2).intersection(model); diff --git a/src/timeEstimate.cpp b/src/timeEstimate.cpp index 4066ff8329..3e2345a92f 100644 --- a/src/timeEstimate.cpp +++ b/src/timeEstimate.cpp @@ -1,14 +1,15 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include "timeEstimate.h" + +#include "settings/Settings.h" +#include "utils/math.h" + +#include #include #include #include -#include - -#include "timeEstimate.h" -#include "utils/math.h" -#include "settings/Settings.h" namespace cura { @@ -59,7 +60,7 @@ void TimeEstimateCalculator::reset() blocks.clear(); } -// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the +// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the // acceleration within the allotted distance. static inline Velocity maxAllowableSpeed(const Acceleration& acceleration, const Velocity& target_velocity, double distance) { @@ -76,7 +77,7 @@ static inline float estimateAccelerationDistance(const Velocity& initial_rate, c return (square(target_rate) - square(initial_rate)) / (2.0 * acceleration); } -// This function gives you the point at which you must start braking (at the rate of -acceleration) if +// This function gives you the point at which you must start braking (at the rate of -acceleration) if // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after // a total travel of distance. This can be used to compute the intersection point between acceleration and // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) @@ -122,13 +123,13 @@ static inline double intersectionDistance(const Velocity& initial_rate, const Ve static inline double accelerationTimeFromDistance(const Velocity& initial_feedrate, const Velocity& distance, const Acceleration& acceleration) { double discriminant = square(initial_feedrate) - 2 * acceleration * -distance; - //If discriminant is negative, we're moving in the wrong direction. - //Making the discriminant 0 then gives the extremum of the parabola instead of the intersection. + // If discriminant is negative, we're moving in the wrong direction. + // Making the discriminant 0 then gives the extremum of the parabola instead of the intersection. discriminant = std::max(0.0, discriminant); return (-initial_feedrate + sqrt(discriminant)) / acceleration; } -void TimeEstimateCalculator::calculateTrapezoidForBlock(Block *block, const Ratio entry_factor, const Ratio exit_factor) +void TimeEstimateCalculator::calculateTrapezoidForBlock(Block* block, const Ratio entry_factor, const Ratio exit_factor) { const Velocity initial_feedrate = block->nominal_feedrate * entry_factor; const Velocity final_feedrate = block->nominal_feedrate * exit_factor; @@ -137,7 +138,7 @@ void TimeEstimateCalculator::calculateTrapezoidForBlock(Block *block, const Rati const double decelerate_distance = estimateAccelerationDistance(block->nominal_feedrate, final_feedrate, -block->acceleration); // Calculate the size of Plateau of Nominal Rate. - double plateau_distance = block->distance-accelerate_distance - decelerate_distance; + double plateau_distance = block->distance - accelerate_distance - decelerate_distance; // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will // have to use intersection_distance() to calculate when to abort acceleration and start braking @@ -146,7 +147,7 @@ void TimeEstimateCalculator::calculateTrapezoidForBlock(Block *block, const Rati { accelerate_distance = intersectionDistance(initial_feedrate, final_feedrate, block->acceleration, block->distance); accelerate_distance = std::max(accelerate_distance, 0.0); // Check limits due to numerical round-off - accelerate_distance = std::min(accelerate_distance, block->distance);//(We can cast here to unsigned, because the above line ensures that we are above zero) + accelerate_distance = std::min(accelerate_distance, block->distance); //(We can cast here to unsigned, because the above line ensures that we are above zero) plateau_distance = 0; } @@ -163,8 +164,8 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu block.feature = feature; - //block.maxTravel = 0; //Done by memset. - for(size_t n = 0; n < NUM_AXIS; n++) + // block.maxTravel = 0; //Done by memset. + for (size_t n = 0; n < NUM_AXIS; n++) { block.delta[n] = newPos[n] - currentPosition[n]; block.absDelta[n] = std::abs(block.delta[n]); @@ -184,11 +185,11 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu block.distance = block.absDelta[3]; } block.nominal_feedrate = feedrate; - + Position current_feedrate; Position current_abs_feedrate; Ratio feedrate_factor = 1.0; - for(size_t n = 0; n < NUM_AXIS; n++) + for (size_t n = 0; n < NUM_AXIS; n++) { current_feedrate[n] = (block.delta[n] * feedrate) / block.distance; current_abs_feedrate[n] = std::abs(current_feedrate[n]); @@ -197,32 +198,32 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu feedrate_factor = std::min(feedrate_factor, Ratio(max_feedrate[n] / current_abs_feedrate[n])); } } - //TODO: XY_FREQUENCY_LIMIT - + // TODO: XY_FREQUENCY_LIMIT + if (feedrate_factor < 1.0) { - for(size_t n = 0; n < NUM_AXIS; n++) + for (size_t n = 0; n < NUM_AXIS; n++) { current_feedrate[n] *= feedrate_factor; current_abs_feedrate[n] *= feedrate_factor; } block.nominal_feedrate *= feedrate_factor; } - + block.acceleration = acceleration; - for(size_t n = 0; n < NUM_AXIS; n++) + for (size_t n = 0; n < NUM_AXIS; n++) { if (block.acceleration * (block.absDelta[n] / block.distance) > max_acceleration[n]) { block.acceleration = max_acceleration[n]; } } - - Velocity vmax_junction { max_xy_jerk / 2.0 }; - Ratio vmax_junction_factor { 1.0 }; + + Velocity vmax_junction{ max_xy_jerk / 2.0 }; + Ratio vmax_junction_factor{ 1.0 }; if (current_abs_feedrate[Z_AXIS] > max_z_jerk / 2.0) { - vmax_junction = std::min(vmax_junction, Velocity { max_z_jerk / 2.0 }); + vmax_junction = std::min(vmax_junction, Velocity{ max_z_jerk / 2.0 }); } if (current_abs_feedrate[E_AXIS] > max_e_jerk / 2.0) { @@ -230,7 +231,7 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu } vmax_junction = std::min(vmax_junction, block.nominal_feedrate); const Velocity safe_speed = vmax_junction; - + if ((blocks.size() > 0) && (previous_nominal_feedrate > 0.0001)) { const Velocity xy_jerk = sqrt(square(current_feedrate[X_AXIS] - previous_feedrate[X_AXIS]) + square(current_feedrate[Y_AXIS] - previous_feedrate[Y_AXIS])); @@ -249,7 +250,7 @@ void TimeEstimateCalculator::plan(Position newPos, Velocity feedrate, PrintFeatu { vmax_junction_factor = std::min(vmax_junction_factor, Ratio(max_e_jerk / e_jerk)); } - vmax_junction = std::min(previous_nominal_feedrate, Velocity { vmax_junction * vmax_junction_factor }); // Limit speed to max previous speed + vmax_junction = std::min(previous_nominal_feedrate, Velocity{ vmax_junction * vmax_junction_factor }); // Limit speed to max previous speed } block.max_entry_speed = vmax_junction; @@ -274,10 +275,10 @@ std::vector TimeEstimateCalculator::calculate() reversePass(); forwardPass(); recalculateTrapezoids(); - + std::vector totals(static_cast(PrintFeatureType::NumPrintFeatureTypes), 0.0); totals[static_cast(PrintFeatureType::NoneType)] = extra_time; // Extra time (pause for minimum layer time, etc) is marked as NoneType - for(unsigned int n = 0; n < blocks.size(); n++) + for (unsigned int n = 0; n < blocks.size(); n++) { const Block& block = blocks[n]; const double plateau_distance = block.decelerate_after - block.accelerate_until; @@ -289,10 +290,10 @@ std::vector TimeEstimateCalculator::calculate() return totals; } -void TimeEstimateCalculator::plannerReversePassKernel(Block *previous, Block *current, Block *next) +void TimeEstimateCalculator::plannerReversePassKernel(Block* previous, Block* current, Block* next) { (void)previous; - if (!current || !next) + if (! current || ! next) { return; } @@ -304,7 +305,7 @@ void TimeEstimateCalculator::plannerReversePassKernel(Block *previous, Block *cu { // If nominal length true, max junction speed is guaranteed to be reached. Only compute // for max allowable speed if block is decelerating and nominal length is false. - if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) + if ((! current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) { current->entry_speed = std::min(current->max_entry_speed, maxAllowableSpeed(-current->acceleration, next->entry_speed, current->distance)); } @@ -318,20 +319,20 @@ void TimeEstimateCalculator::plannerReversePassKernel(Block *previous, Block *cu void TimeEstimateCalculator::reversePass() { - Block* block[3] = {nullptr, nullptr, nullptr}; - for(size_t n = blocks.size() - 1; int(n) >= 0; n--) + Block* block[3] = { nullptr, nullptr, nullptr }; + for (size_t n = blocks.size() - 1; int(n) >= 0; n--) { - block[2]= block[1]; - block[1]= block[0]; + block[2] = block[1]; + block[1] = block[0]; block[0] = &blocks[n]; plannerReversePassKernel(block[0], block[1], block[2]); } } -void TimeEstimateCalculator::plannerForwardPassKernel(Block *previous, Block *current, Block *next) +void TimeEstimateCalculator::plannerForwardPassKernel(Block* previous, Block* current, Block* next) { (void)next; - if (!previous) + if (! previous) { return; } @@ -340,7 +341,7 @@ void TimeEstimateCalculator::plannerForwardPassKernel(Block *previous, Block *cu // full speed change within the block, we need to adjust the entry speed accordingly. Entry // speeds have already been reset, maximized, and reverse planned by reverse planner. // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. - if (!previous->nominal_length_flag) + if (! previous->nominal_length_flag) { if (previous->entry_speed < current->entry_speed) { @@ -358,11 +359,11 @@ void TimeEstimateCalculator::plannerForwardPassKernel(Block *previous, Block *cu void TimeEstimateCalculator::forwardPass() { - Block* block[3] = {nullptr, nullptr, nullptr}; - for(size_t n = 0; n < blocks.size(); n++) + Block* block[3] = { nullptr, nullptr, nullptr }; + for (size_t n = 0; n < blocks.size(); n++) { - block[0]= block[1]; - block[1]= block[2]; + block[0] = block[1]; + block[1] = block[2]; block[2] = &blocks[n]; plannerForwardPassKernel(block[0], block[1], block[2]); } @@ -371,10 +372,10 @@ void TimeEstimateCalculator::forwardPass() void TimeEstimateCalculator::recalculateTrapezoids() { - Block *current; - Block *next = nullptr; + Block* current; + Block* next = nullptr; - for(unsigned int n=0; n Date: Wed, 9 Aug 2023 01:05:41 +0200 Subject: [PATCH 333/656] Fixed UT and missing includes. Contributes to CURA-10446 --- include/support.h | 9 +++-- tests/ExtruderPlanTest.cpp | 52 ++++++++++++++-------------- tests/GCodeExportTest.cpp | 45 ++++++++++++------------ tests/TimeEstimateCalculatorTest.cpp | 6 ++-- tests/settings/SettingsTest.cpp | 14 ++++---- 5 files changed, 65 insertions(+), 61 deletions(-) diff --git a/include/support.h b/include/support.h index ce4c9e4ca3..793ce6156b 100644 --- a/include/support.h +++ b/include/support.h @@ -1,9 +1,14 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef SUPPORT_H #define SUPPORT_H +#include "utils/polygon.h" + +#include +#include + namespace cura { diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index 965829ac3e..190381ada3 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "LayerPlan.h" //Code under test. #include @@ -69,15 +69,15 @@ class ExtruderPlanTestPathCollection GCodePathConfig travel_config; ExtruderPlanTestPathCollection() - : extrusion_config(PrintFeatureType::OuterWall, 400, 100, 1.0_r, GCodePathConfig::SpeedDerivatives(50, 1000, 10)) - , travel_config(PrintFeatureType::MoveCombing, 0, 100, 0.0_r, GCodePathConfig::SpeedDerivatives(120, 5000, 30)) + : extrusion_config(PrintFeatureType::OuterWall, 400, 100, 1.0_r, GCodePathConfig::SpeedDerivatives(50.0, 1000.0, 10.0)) + , travel_config(PrintFeatureType::MoveCombing, 0, 100, 0.0_r, GCodePathConfig::SpeedDerivatives(120.0, 5000.0, 30.0)) { const SliceMeshStorage* mesh = nullptr; constexpr Ratio flow_1 = 1.0_r; constexpr Ratio width_1 = 1.0_r; constexpr bool no_spiralize = false; constexpr Ratio speed_1 = 1.0_r; - square.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::PolyLines, flow_1, no_spiralize, speed_1) }); + square.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::PolyLines, flow_1, width_1, no_spiralize, speed_1) }); square.back().points = { Point(0, 0), @@ -89,11 +89,11 @@ class ExtruderPlanTestPathCollection lines.assign ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1) + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1) }); lines[0].points = { Point(0, 0), Point(1000, 0) }; lines[1].points = { Point(1000, 0), Point(1000, 400) }; @@ -106,11 +106,11 @@ class ExtruderPlanTestPathCollection constexpr Ratio flow_04 = 0.4_r; decreasing_flow.assign ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_12, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_08, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_04, no_spiralize, speed_1) + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_12, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_08, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_04, width_1, no_spiralize, speed_1) }); decreasing_flow[0].points = { Point(0, 0), Point(1000, 0) }; decreasing_flow[1].points = { Point(1000, 0), Point(1000, 400) }; @@ -123,11 +123,11 @@ class ExtruderPlanTestPathCollection constexpr Ratio speed_04 = 0.4_r; decreasing_speed.assign ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_12), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_08), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_04) + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_12), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_08), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_04) }); decreasing_speed[0].points = { Point(0, 0), Point(1000, 0) }; decreasing_speed[1].points = { Point(1000, 0), Point(1000, 400) }; @@ -137,12 +137,12 @@ class ExtruderPlanTestPathCollection variable_width.assign ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.8_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.6_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.4_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.2_r, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.0_r, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.8_r, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.6_r, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.4_r, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.2_r, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.0_r, width_1, no_spiralize, speed_1), }); variable_width[0].points = { Point(0, 0), Point(1000, 0) }; variable_width[1].points = { Point(1000, 0), Point(2000, 0) }; diff --git a/tests/GCodeExportTest.cpp b/tests/GCodeExportTest.cpp index a4d3b7e7b1..134a48b503 100644 --- a/tests/GCodeExportTest.cpp +++ b/tests/GCodeExportTest.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "gcodeExport.h" // The unit under test. #include "Application.h" // To set up a slice with settings. @@ -7,7 +7,6 @@ #include "Slice.h" // To set up a slice with settings. #include "WipeScriptConfig.h" // For wipe script tests. #include "arcus/MockCommunication.h" // To prevent calls to any missing Communication class. -#include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" #include "utils/Date.h" // To check the Griffin header. #include @@ -52,10 +51,10 @@ class GCodeExportTest : public testing::Test gcode.current_extruder = 0; gcode.current_fan_speed = -1; gcode.total_print_times = std::vector(static_cast(PrintFeatureType::NumPrintFeatureTypes), 0.0); - gcode.currentSpeed = 1; - gcode.current_print_acceleration = -1; - gcode.current_travel_acceleration = -1; - gcode.current_jerk = -1; + gcode.currentSpeed = 1.0; + gcode.current_print_acceleration = -1.0; + gcode.current_travel_acceleration = -1.0; + gcode.current_jerk = -1.0; gcode.is_z_hopped = 0; gcode.setFlavor(EGCodeFlavor::MARLIN); gcode.bed_temperature = 0; @@ -210,10 +209,10 @@ class GriffinHeaderTest : public testing::TestWithParam gcode.current_extruder = 0; gcode.current_fan_speed = -1; gcode.total_print_times = std::vector(static_cast(PrintFeatureType::NumPrintFeatureTypes), 0.0); - gcode.currentSpeed = 1; - gcode.current_print_acceleration = -1; - gcode.current_travel_acceleration = -1; - gcode.current_jerk = -1; + gcode.currentSpeed = 1.0; + gcode.current_print_acceleration = -1.0; + gcode.current_travel_acceleration = -1.0; + gcode.current_jerk = -1.0; gcode.is_z_hopped = 0; gcode.setFlavor(EGCodeFlavor::MARLIN); gcode.initial_bed_temp = 0; @@ -493,7 +492,7 @@ TEST_F(GCodeExportTest, WriteZHopStartCustomSpeed) 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; - constexpr Velocity speed = 4; // 240 mm/min. + constexpr Velocity speed { 4.0 }; // 240 mm/min. gcode.writeZhopStart(hop_height, speed); EXPECT_EQ(std::string("G1 F240 Z5\n"), output.str()) << "Custom provided speed should be used."; } @@ -521,7 +520,7 @@ TEST_F(GCodeExportTest, WriteZHopEndCustomSpeed) 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; - constexpr Velocity speed = 4; // 240 mm/min. + constexpr Velocity speed { 4.0 }; // 240 mm/min. gcode.writeZhopEnd(speed); EXPECT_EQ(std::string("G1 F240 Z2\n"), output.str()) << "Custom provided speed should be used."; } @@ -539,7 +538,7 @@ TEST_F(GCodeExportTest, insertWipeScriptSingleMove) config.brush_pos_x = 2000; config.repeat_count = 1; config.move_distance = 500; - config.move_speed = 10; + config.move_speed = 10.0; config.pause = 0; EXPECT_CALL(*mock_communication, sendLineTo(testing::_, testing::_, testing::_, testing::_, testing::_)).Times(3); @@ -571,7 +570,7 @@ TEST_F(GCodeExportTest, insertWipeScriptMultipleMoves) config.brush_pos_x = 2000; config.repeat_count = 4; config.move_distance = 500; - config.move_speed = 10; + config.move_speed = 10.0; config.pause = 0; EXPECT_CALL(*mock_communication, sendLineTo(testing::_, testing::_, testing::_, testing::_, testing::_)).Times(6); @@ -609,7 +608,7 @@ TEST_F(GCodeExportTest, insertWipeScriptOptionalDelay) config.brush_pos_x = 2000; config.repeat_count = 1; config.move_distance = 500; - config.move_speed = 10; + config.move_speed = 10.0; config.pause = 1.5; // 1.5 sec = 1500 ms. EXPECT_CALL(*mock_communication, sendLineTo(testing::_, testing::_, testing::_, testing::_, testing::_)).Times(3); @@ -637,7 +636,7 @@ TEST_F(GCodeExportTest, insertWipeScriptRetractionEnable) gcode.current_extruder = 0; gcode.extruder_attr[0].filament_area = 10.0; gcode.relative_extrusion = false; - gcode.currentSpeed = 1; + gcode.currentSpeed = 1.0; Application::getInstance().current_slice->scene.current_mesh_group->settings.add("layer_height", "0.2"); Application::getInstance().current_slice->scene.extruders.emplace_back(0, &Application::getInstance().current_slice->scene.current_mesh_group->settings); Application::getInstance().current_slice->scene.extruders.back().settings.add("machine_firmware_retract", "false"); @@ -645,8 +644,8 @@ TEST_F(GCodeExportTest, insertWipeScriptRetractionEnable) WipeScriptConfig config; config.retraction_enable = true; config.retraction_config.distance = 1; - config.retraction_config.speed = 2; // 120 mm/min. - config.retraction_config.primeSpeed = 3; // 180 mm/min. + config.retraction_config.speed = 2.0; // 120 mm/min. + config.retraction_config.primeSpeed = 3.0; // 180 mm/min. config.retraction_config.prime_volume = gcode.extruder_attr[0].filament_area * 4; // 4mm in linear dimensions config.retraction_config.retraction_count_max = 100; // Practically no limit. config.retraction_config.retraction_extrusion_window = 1; @@ -655,7 +654,7 @@ TEST_F(GCodeExportTest, insertWipeScriptRetractionEnable) config.brush_pos_x = 2000; config.repeat_count = 1; config.move_distance = 500; - config.move_speed = 10; + config.move_speed = 10.0; config.pause = 0; EXPECT_CALL(*mock_communication, sendLineTo(testing::_, testing::_, testing::_, testing::_, testing::_)).Times(3); @@ -680,18 +679,18 @@ TEST_F(GCodeExportTest, insertWipeScriptHopEnable) gcode.currentPosition = Point3(1000, 1000, 1000); gcode.current_layer_z = 1000; gcode.use_extruder_offset_to_offset_coords = false; - gcode.currentSpeed = 1; + gcode.currentSpeed = 1.0; Application::getInstance().current_slice->scene.current_mesh_group->settings.add("layer_height", "0.2"); WipeScriptConfig config; config.retraction_enable = false; config.hop_enable = true; - config.hop_speed = 2; // 120 mm/min. + config.hop_speed = 2.0; // 120 mm/min. config.hop_amount = 300; config.brush_pos_x = 2000; config.repeat_count = 1; config.move_distance = 500; - config.move_speed = 10; + config.move_speed = 10.0; config.pause = 0; EXPECT_CALL(*mock_communication, sendLineTo(testing::_, testing::_, testing::_, testing::_, testing::_)).Times(3); diff --git a/tests/TimeEstimateCalculatorTest.cpp b/tests/TimeEstimateCalculatorTest.cpp index 68a88f3b75..e5ce2a6b1d 100644 --- a/tests/TimeEstimateCalculatorTest.cpp +++ b/tests/TimeEstimateCalculatorTest.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "PrintFeature.h" //We get time estimates per print feature. #include "settings/Settings.h" //To set firmware settings. @@ -144,7 +144,7 @@ TEST_F(TimeEstimateCalculatorTest, MoveToCurrentLocation) Duration estimate = std::accumulate(result.begin(), result.end(), Duration(0.0)); EXPECT_NEAR(Duration(0.0), estimate, EPSILON) << "setPosition should not add any time to the estimate."; - calculator.plan(position, Velocity(10), PrintFeatureType::Infill); + calculator.plan(position, Velocity { 10.0 }, PrintFeatureType::Infill); result = calculator.calculate(); estimate = std::accumulate(result.begin(), result.end(), Duration(0.0)); diff --git a/tests/settings/SettingsTest.cpp b/tests/settings/SettingsTest.cpp index a59acbda1b..e468965646 100644 --- a/tests/settings/SettingsTest.cpp +++ b/tests/settings/SettingsTest.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "settings/Settings.h" //The class under test. #include "Application.h" //To test extruder train settings. @@ -139,25 +139,25 @@ TEST_F(SettingsTest, AddSettingTemperature) TEST_F(SettingsTest, AddSettingVelocity) { settings.add("test_setting", "12.345"); - EXPECT_DOUBLE_EQ(Velocity(12.345), settings.get("test_setting")); + EXPECT_DOUBLE_EQ(Velocity { 12.345 }, settings.get("test_setting")); settings.add("test_setting", "-78"); - EXPECT_DOUBLE_EQ(Velocity(-78), settings.get("test_setting")); + EXPECT_DOUBLE_EQ(Velocity{ -78.0 }, settings.get("test_setting")); } TEST_F(SettingsTest, AddSettingRatio) { settings.add("test_setting", "1.618"); - EXPECT_DOUBLE_EQ(Ratio(0.01618), settings.get("test_setting")) << "With ratios, the input is interpreted in percentages."; + EXPECT_DOUBLE_EQ(Ratio { 0.01618 }, settings.get("test_setting")) << "With ratios, the input is interpreted in percentages."; } TEST_F(SettingsTest, AddSettingDuration) { settings.add("test_setting", "1234.5678"); - EXPECT_DOUBLE_EQ(Duration(1234.5678), settings.get("test_setting")); + EXPECT_DOUBLE_EQ(Duration { 1234.5678 }, settings.get("test_setting")); settings.add("test_setting", "-1234.5678"); - EXPECT_DOUBLE_EQ(Duration(0), settings.get("test_setting")) << "Negative duration doesn't exist, so it gets rounded to 0."; + EXPECT_DOUBLE_EQ(Duration { 0 }, settings.get("test_setting")) << "Negative duration doesn't exist, so it gets rounded to 0."; } TEST_F(SettingsTest, AddSettingFlowTempGraph) From 17cd8a06834758cf09b24779d27c1cbb7a3a530a Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 8 Aug 2023 23:06:13 +0000 Subject: [PATCH 334/656] Applied clang-format. --- include/support.h | 75 +++++++++++++++++++++------------ src/TreeSupportTipGenerator.cpp | 2 +- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/include/support.h b/include/support.h index 793ce6156b..a65f899dd4 100644 --- a/include/support.h +++ b/include/support.h @@ -6,8 +6,8 @@ #include "utils/polygon.h" -#include #include +#include namespace cura { @@ -24,7 +24,7 @@ class AreaSupport /*! * \brief Move support mesh outlines from slicer data into the support * storage. - * + * * \param[out] storage Where to store the support areas. * \param mesh_settings Where to get the settings from what kind of support * mesh it is. @@ -74,7 +74,8 @@ class AreaSupport * \param global_support_areas_per_layer the global support areas per layer * \param total_layer_count total number of layers */ - static void splitGlobalSupportAreasIntoSupportInfillParts(SliceDataStorage& storage, const std::vector& global_support_areas_per_layer, unsigned int total_layer_count); + static void + splitGlobalSupportAreasIntoSupportInfillParts(SliceDataStorage& storage, const std::vector& global_support_areas_per_layer, unsigned int total_layer_count); /*! * Generate gradual support on the already generated support areas. This must be called after generateSupportAreas(). @@ -105,11 +106,11 @@ class AreaSupport /*! * \brief Combines the support infill of multiple layers. - * + * * The support infill layers are combined while the thickness of each layer is * multiplied such that the infill should fill up again to the full height of * all combined layers. - * + * * \param storage data storage containing the input layer outline data and containing the output support storage per layer */ static void combineSupportInfillLayers(SliceDataStorage& storage); @@ -138,7 +139,7 @@ class AreaSupport * meshes with drop down and once for all support meshes without drop down. * The \p mesh_idx should then correspond to an empty \ref SliceMeshStorage * of one support mesh with the given value of support_mesh_drop_down. - * + * * \param storage Data storage containing the input layer outline data. * \param infill_settings The settings which are based on the infill of the * support. @@ -150,7 +151,14 @@ class AreaSupport * areas. * \param layer_count Total number of layers. */ - static void generateSupportAreasForMesh(SliceDataStorage& storage, const Settings& infill_settings, const Settings& roof_settings, const Settings& bottom_settings, const size_t mesh_idx, const size_t layer_count, std::vector& support_areas); + static void generateSupportAreasForMesh( + SliceDataStorage& storage, + const Settings& infill_settings, + const Settings& roof_settings, + const Settings& bottom_settings, + const size_t mesh_idx, + const size_t layer_count, + std::vector& support_areas); /*! * Generate support bottom areas for a given mesh. @@ -197,7 +205,13 @@ class AreaSupport * \param minimum_interface_area Minimum area size for resulting interface polygons. * \param[out] interface_polygons The resulting interface layer. Do not use `interface` in windows! */ - static void generateSupportInterfaceLayer(Polygons& support_areas, const Polygons mesh_outlines, const coord_t safety_offset, const coord_t outline_offset, const double minimum_interface_area, Polygons& interface_polygons); + static void generateSupportInterfaceLayer( + Polygons& support_areas, + const Polygons mesh_outlines, + const coord_t safety_offset, + const coord_t outline_offset, + const double minimum_interface_area, + Polygons& interface_polygons); /*! * \brief Join current support layer with the support of the layer above, @@ -214,20 +228,26 @@ class AreaSupport /*! * Move the support up from model (cut away polygons to ensure bottom z distance) * and apply stair step transformation. - * + * * If the bottom stairs defined only by the step height are too wide, * the top half of the step will be as wide as the stair step width * and the bottom half will follow the model. - * + * * \param storage Where to get model outlines from - * \param[in,out] stair_removal The polygons to be removed for stair stepping on the current layer (input) and for the next layer (output). Only changed every [step_height] layers. - * \param[in,out] support_areas The support areas before and after this function - * \param layer_idx The layer number of the support layer we are processing - * \param bottom_empty_layer_count The number of empty layers between the bottom of support and the top of the model on which support rests - * \param bottom_stair_step_layer_count The max height (in nr of layers) of the support bottom stairs - * \param support_bottom_stair_step_width The max width of the support bottom stairs + * \param[in,out] stair_removal The polygons to be removed for stair stepping on the current layer (input) and for the next layer (output). Only changed every [step_height] + * layers. \param[in,out] support_areas The support areas before and after this function \param layer_idx The layer number of the support layer we are processing \param + * bottom_empty_layer_count The number of empty layers between the bottom of support and the top of the model on which support rests \param bottom_stair_step_layer_count The + * max height (in nr of layers) of the support bottom stairs \param support_bottom_stair_step_width The max width of the support bottom stairs */ - static void moveUpFromModel(const SliceDataStorage& storage, Polygons& stair_removal, Polygons& sloped_areas, Polygons& support_areas, const size_t layer_idx, const size_t bottom_empty_layer_count, const size_t bottom_stair_step_layer_count, const coord_t support_bottom_stair_step_width); + static void moveUpFromModel( + const SliceDataStorage& storage, + Polygons& stair_removal, + Polygons& sloped_areas, + Polygons& support_areas, + const size_t layer_idx, + const size_t bottom_empty_layer_count, + const size_t bottom_stair_step_layer_count, + const coord_t support_bottom_stair_step_width); /*! * Joins the layer part outlines of all meshes and collects the overhang @@ -236,26 +256,26 @@ class AreaSupport * \param mesh Output mesh to store the resulting overhang points in. */ static void detectOverhangPoints(const SliceDataStorage& storage, SliceMeshStorage& mesh); - + /*! * \brief Compute the basic overhang and full overhang of a layer. * * The basic overhang consists of the parts of this layer which are too far * away from the layer below to be supported. The full overhang consists of * the basic overhang extended toward the border of the layer below. - * + * * layer 2 * layer 1 ______________| * _______| ^^^^^ basic overhang * ^^^^^^^^^^^^^^ full overhang - * + * * \param storage The slice data storage. * \param mesh The mesh for which to compute the basic overhangs. * \param layer_idx The layer for which to compute the overhang. * \return A pair of basic overhang and full overhang. */ static std::pair computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx); - + /*! * \brief Adds tower pieces to the current support layer. * @@ -277,9 +297,8 @@ class AreaSupport std::vector& tower_roofs, std::vector>& overhang_points, LayerIndex layer_idx, - size_t layer_count - ); - + size_t layer_count); + /*! * \brief Adds struts (towers against a wall) to the current layer. * \param settings The settings to use to create the wall struts. @@ -291,10 +310,10 @@ class AreaSupport /*! * Clean up the SupportInfillParts. * Remove parts which have nothing to be printed. - * + * * Remove parts which are too small for the first wall. * For parts without walls: remove if combined into upper layers. - * + * */ static void cleanup(SliceDataStorage& storage); @@ -311,6 +330,6 @@ class AreaSupport }; -}//namespace cura +} // namespace cura -#endif//SUPPORT_H +#endif // SUPPORT_H diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 1103f693f7..b0ddb5a41b 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -917,7 +917,7 @@ void TreeSupportTipGenerator::generateTips( .splitIntoParts(true)) // If there is a roof, the roof will be one layer above the tips. { //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors - //introduced since then. But this does not fully prevent some rounding errors either way, so just handle the error later. + // introduced since then. But this does not fully prevent some rounding errors either way, so just handle the error later. overhang_processing.emplace_back(roof_part, true); } } From ba85a4983b536a15e067e181de6a77ec2f950914 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 9 Aug 2023 08:10:50 +0200 Subject: [PATCH 335/656] Fixed the shadowing warnings CURA-10446 --- include/utils/types/numeric_facade.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h index 309fa0f8e2..ae6a6074ec 100644 --- a/include/utils/types/numeric_facade.h +++ b/include/utils/types/numeric_facade.h @@ -22,9 +22,9 @@ struct NumericFacade constexpr NumericFacade(const NumericFacade& other) noexcept = default; constexpr NumericFacade(NumericFacade&& other) noexcept = default; - constexpr NumericFacade(const floating_point auto value) noexcept requires floating_point : value{ static_cast(value) } {}; + constexpr NumericFacade(const floating_point auto val) noexcept requires floating_point : value{ static_cast(val) } {}; - constexpr NumericFacade(const integral auto value) noexcept requires integral : value{ static_cast(value) } {}; + constexpr NumericFacade(const integral auto val) noexcept requires integral : value{ static_cast(val) } {}; constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; From 5b7f14aa3f3f06b9330d0c6d0f91c6fc86f4be3d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 9 Aug 2023 11:12:06 +0200 Subject: [PATCH 336/656] Fix includes Don't forward declare the new numeric_facade types Contributes to CURA-10446 --- include/FffPolygonGenerator.h | 2 +- include/GCodePathConfig.h | 5 ++--- include/Preheat.h | 3 +-- include/communication/ArcusCommunicationPrivate.h | 2 +- include/communication/Communication.h | 8 ++++---- include/gcodeExport.h | 2 +- include/infill/SubDivCube.h | 8 ++++---- include/settings/PathConfigStorage.h | 6 +++--- include/support.h | 2 +- include/utils/FMatrix4x3.h | 3 ++- src/InsetOrderOptimizer.cpp | 2 +- 11 files changed, 21 insertions(+), 22 deletions(-) diff --git a/include/FffPolygonGenerator.h b/include/FffPolygonGenerator.h index 6f649d9c77..99a220443e 100644 --- a/include/FffPolygonGenerator.h +++ b/include/FffPolygonGenerator.h @@ -5,11 +5,11 @@ #define FFF_POLYGON_GENERATOR_H #include "utils/NoCopy.h" +#include "settings/types/LayerIndex.h" namespace cura { -struct LayerIndex; class MeshGroup; class ProgressStageEstimator; class SliceDataStorage; diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 89e1ca8905..c49892c149 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -5,15 +5,14 @@ #define G_CODE_PATH_CONFIG_H #include "PrintFeature.h" -#include "settings/types/Ratio.h" //For flow rate. +#include "settings/types/Ratio.h" #include "settings/types/Velocity.h" +#include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" namespace cura { -struct LayerIndex; - /*! * The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed. */ diff --git a/include/Preheat.h b/include/Preheat.h index 800ddc4413..e9b1097380 100644 --- a/include/Preheat.h +++ b/include/Preheat.h @@ -7,14 +7,13 @@ #include #include // max +#include "settings/types/Ratio.h" #include "settings/types/Duration.h" #include "settings/types/Temperature.h" namespace cura { -class Ratio; - /*! * Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature. */ diff --git a/include/communication/ArcusCommunicationPrivate.h b/include/communication/ArcusCommunicationPrivate.h index bcd304ef42..9c63aa0bce 100644 --- a/include/communication/ArcusCommunicationPrivate.h +++ b/include/communication/ArcusCommunicationPrivate.h @@ -9,11 +9,11 @@ #include "ArcusCommunication.h" //We're adding a subclass to this. #include "SliceDataStruct.h" +#include "settings/types/LayerIndex.h" namespace cura { -struct LayerIndex; class ArcusCommunication::Private { diff --git a/include/communication/Communication.h b/include/communication/Communication.h index 1853f7c8e0..bb7b018c31 100644 --- a/include/communication/Communication.h +++ b/include/communication/Communication.h @@ -4,13 +4,13 @@ #ifndef COMMUNICATION_H #define COMMUNICATION_H -#include "../utils/IntPoint.h" //For coord_t and Point. +#include "utils/IntPoint.h" +#include "settings/types/LayerIndex.h" +#include "settings/types/Velocity.h" namespace cura { //Some forward declarations to increase compilation speed. -struct LayerIndex; -struct Velocity; enum class PrintFeatureType : unsigned char; class Polygons; class ConstPolygonRef; @@ -26,7 +26,7 @@ class Communication /* * \brief Close the communication channel. */ - virtual ~Communication() {} + virtual ~Communication() = default; /* * \brief Test if there are more slices to be queued. diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 9ab087cd75..db015e6b8f 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -12,6 +12,7 @@ #include "settings/Settings.h" //For MAX_EXTRUDERS. #include "settings/types/Temperature.h" //Bed temperature. #include "settings/types/Velocity.h" +#include "settings/types/LayerIndex.h" #include "sliceDataStorage.h" #include "timeEstimate.h" #include "utils/AABB3D.h" //To track the used build volume for the Griffin header. @@ -24,7 +25,6 @@ namespace cura { -struct LayerIndex; class RetractionConfig; struct WipeScriptConfig; diff --git a/include/infill/SubDivCube.h b/include/infill/SubDivCube.h index c6962f008e..ad3fe90bab 100644 --- a/include/infill/SubDivCube.h +++ b/include/infill/SubDivCube.h @@ -4,14 +4,14 @@ #ifndef INFILL_SUBDIVCUBE_H #define INFILL_SUBDIVCUBE_H -#include "../settings/types/Ratio.h" -#include "../utils/IntPoint.h" -#include "../utils/Point3.h" +#include "settings/types/Ratio.h" +#include "settings/types/LayerIndex.h" +#include "utils/IntPoint.h" +#include "utils/Point3.h" namespace cura { -struct LayerIndex; class Polygons; class SliceMeshStorage; diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index 2480e345e9..4073cdfc21 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -6,14 +6,14 @@ #include -#include "../GCodePathConfig.h" -#include "../utils/Coord_t.h" +#include "GCodePathConfig.h" +#include "utils/Coord_t.h" +#include "settings/types/LayerIndex.h" namespace cura { class ExtruderTrain; -struct LayerIndex; class SliceDataStorage; class SliceMeshStorage; diff --git a/include/support.h b/include/support.h index a65f899dd4..e3e783494a 100644 --- a/include/support.h +++ b/include/support.h @@ -5,6 +5,7 @@ #define SUPPORT_H #include "utils/polygon.h" +#include "settings/types/LayerIndex.h" #include #include @@ -12,7 +13,6 @@ namespace cura { -struct LayerIndex; class Settings; class SliceDataStorage; class SliceMeshStorage; diff --git a/include/utils/FMatrix4x3.h b/include/utils/FMatrix4x3.h index 441d693ac5..3f324a9deb 100644 --- a/include/utils/FMatrix4x3.h +++ b/include/utils/FMatrix4x3.h @@ -4,12 +4,13 @@ #ifndef FMATRIX4X3_H #define FMATRIX4X3_H +#include "settings/types/Ratio.h" + namespace cura { class FPoint3; class Point3; -class Ratio; /*! * A 4x3 affine transformation matrix. diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 7129ec2d52..32b1ca4b21 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include From 3c2971d7cc9b9a2b8a5a587a38c40e625df4546f Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 09:15:03 +0000 Subject: [PATCH 337/656] Applied clang-format. --- include/FffPolygonGenerator.h | 72 +++++----- include/GCodePathConfig.h | 37 +++-- include/Preheat.h | 32 ++--- .../communication/ArcusCommunicationPrivate.h | 15 +- include/communication/Communication.h | 9 +- include/gcodeExport.h | 2 +- include/infill/SubDivCube.h | 19 ++- include/settings/PathConfigStorage.h | 13 +- include/support.h | 2 +- include/utils/FMatrix4x3.h | 10 +- src/InsetOrderOptimizer.cpp | 131 +++++++++--------- 11 files changed, 182 insertions(+), 160 deletions(-) diff --git a/include/FffPolygonGenerator.h b/include/FffPolygonGenerator.h index 99a220443e..e92e02c6f6 100644 --- a/include/FffPolygonGenerator.h +++ b/include/FffPolygonGenerator.h @@ -1,11 +1,11 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef FFF_POLYGON_GENERATOR_H #define FFF_POLYGON_GENERATOR_H -#include "utils/NoCopy.h" #include "settings/types/LayerIndex.h" +#include "utils/NoCopy.h" namespace cura { @@ -19,24 +19,23 @@ class TimeKeeper; /*! * Primary stage in Fused Filament Fabrication processing: Polygons are generated. * The model is sliced and each slice consists of polygons representing the outlines: the boundaries between inside and outside the object. - * After slicing, the layers are processed; for example the wall insets are generated, and the areas which are to be filled with support and infill, which are all represented by polygons. - * In this stage nothing other than areas and circular paths are generated, which are both represented by polygons. - * No infill lines or support pattern etc. is generated. - * + * After slicing, the layers are processed; for example the wall insets are generated, and the areas which are to be filled with support and infill, which are all represented by + * polygons. In this stage nothing other than areas and circular paths are generated, which are both represented by polygons. No infill lines or support pattern etc. is generated. + * * The main function of this class is FffPolygonGenerator::generateAreas(). */ class FffPolygonGenerator : public NoCopy { public: /*! - * Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc. - * + * Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc. + * * \param object The object to slice. * \param timeKeeper Object which keeps track of timings of each stage. * \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline. */ bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper); - + private: /*! * \brief Helper function to get the actual height of the draft shield. @@ -53,73 +52,78 @@ class FffPolygonGenerator : public NoCopy /*! * Slice the \p object and store the outlines in the \p storage. - * + * * \param object The object to slice. * \param timeKeeper Object which keeps track of timings of each stage. * \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline. - * + * * \return Whether the process succeeded (always true). */ bool sliceModel(MeshGroup* object, TimeKeeper& timeKeeper, SliceDataStorage& storage); /// slices the model /*! - * Processes the outline information as stored in the \p storage: generates inset perimeter polygons, support area polygons, etc. - * + * Processes the outline information as stored in the \p storage: generates inset perimeter polygons, support area polygons, etc. + * * \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage * \param timeKeeper Object which keeps track of timings of each stage. */ void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper); - + /*! * Processes the outline information as stored in the \p storage: generates inset perimeter polygons, skin and infill - * + * * \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage * \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage * \param mesh_order The order in which the meshes are processed (used for infill meshes) * \param inset_skin_progress_estimate The progress stage estimate calculator */ - void processBasicWallsSkinInfill(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate); + void processBasicWallsSkinInfill( + SliceDataStorage& storage, + const size_t mesh_order_idx, + const std::vector& mesh_order, + ProgressStageEstimator& inset_skin_progress_estimate); /*! * Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes - * + * * \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage * \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage * \param mesh_order The order in which the meshes are processed */ void processInfillMesh(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order); - + /*! * Process features which are derived from the basic walls, skin, and infill: * fuzzy skin, infill combine - * + * * \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage */ void processDerivedWallsSkinInfill(SliceMeshStorage& mesh); - + /*! * Checks whether a layer is empty or not - * + * * \param storage Input and Ouput parameter: stores all layers * \param layer_idx Index of the layer to check - * + * * \return Whether or not the layer is empty */ bool isEmptyLayer(SliceDataStorage& storage, const unsigned int layer_idx); - + /*! * \brief Remove all bottom layers which are empty. - * + * * \warning Changes \p total_layers - * + * * \param[in, out] storage Stores all layers. * \param[in, out] total_layers The total number of layers. */ void removeEmptyFirstLayers(SliceDataStorage& storage, size_t& total_layers); /*! - * Set \ref SliceDataStorage::max_print_height_per_extruder and \ref SliceDataStorage::max_print_height_order and \ref SliceDataStorage::max_print_height_second_to_last_extruder - * + * Set \ref SliceDataStorage::max_print_height_per_extruder and \ref SliceDataStorage::max_print_height_order and \ref + * SliceDataStorage::max_print_height_second_to_last_extruder + * * \param[in,out] storage Where to retrieve mesh and support etc settings from and where the print height statistics are saved. */ void computePrintHeightStatistics(SliceDataStorage& storage); @@ -146,7 +150,7 @@ class FffPolygonGenerator : public NoCopy /*! * Generate the polygons where the draft screen should be. - * + * * \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage */ void processDraftShield(SliceDataStorage& storage); @@ -159,16 +163,16 @@ class FffPolygonGenerator : public NoCopy /*! * Make the outer wall 'fuzzy' - * + * * Introduce new vertices and move existing vertices in or out by a random distance, based on the fuzzy skin settings. - * + * * This only changes the outer wall. - * + * * \param[in,out] mesh where the outer wall is retrieved and stored in. */ void processFuzzyWalls(SliceMeshStorage& mesh); }; -}//namespace cura +} // namespace cura -#endif //FFF_POLYGON_GENERATOR_H +#endif // FFF_POLYGON_GENERATOR_H diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index c49892c149..39cac0852a 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -1,16 +1,16 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef G_CODE_PATH_CONFIG_H #define G_CODE_PATH_CONFIG_H #include "PrintFeature.h" +#include "settings/types/LayerIndex.h" #include "settings/types/Ratio.h" #include "settings/types/Velocity.h" -#include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" -namespace cura +namespace cura { /*! @@ -27,20 +27,33 @@ class GCodePathConfig Velocity speed; //!< movement speed (mm/s) Acceleration acceleration; //!< acceleration of head movement (mm/s^2) Velocity jerk; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s) - SpeedDerivatives(Velocity speed, Acceleration acceleration, Velocity jerk) : speed(speed), acceleration(acceleration), jerk(jerk) {} + SpeedDerivatives(Velocity speed, Acceleration acceleration, Velocity jerk) + : speed(speed) + , acceleration(acceleration) + , jerk(jerk) + { + } }; const PrintFeatureType type; //!< name of the feature type static constexpr double FAN_SPEED_DEFAULT = -1; + private: SpeedDerivatives speed_derivatives; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called. const coord_t line_width; //!< width of the line extruded const coord_t layer_thickness; //!< current layer height in micron const Ratio flow; //!< extrusion flow modifier. - const double extrusion_mm3_per_mm;//!< current mm^3 filament moved per mm line traversed + const double extrusion_mm3_per_mm; //!< current mm^3 filament moved per mm line traversed const bool is_bridge_path; //!< whether current config is used when bridging const double fan_speed; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise public: - GCodePathConfig(const PrintFeatureType& type, const coord_t line_width, const coord_t layer_height, const Ratio& flow, const SpeedDerivatives speed_derivatives, const bool is_bridge_path = false, const double fan_speed = FAN_SPEED_DEFAULT); + GCodePathConfig( + const PrintFeatureType& type, + const coord_t line_width, + const coord_t layer_height, + const Ratio& flow, + const SpeedDerivatives speed_derivatives, + const bool is_bridge_path = false, + const double fan_speed = FAN_SPEED_DEFAULT); /*! * copy constructor @@ -49,13 +62,13 @@ class GCodePathConfig /*! * Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed. - * + * * \warning This functions should not be called with @p layer_nr > @p max_speed_layer ! - * + * * \warning Calling this function twice will smooth the speed more toward \p first_layer_config - * + * * \param first_layer_config The speed settings at layer zero - * \param layer_nr The layer number + * \param layer_nr The layer number * \param max_speed_layer The layer number for which the speed_iconic should be used. */ void smoothSpeed(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer); @@ -99,6 +112,6 @@ class GCodePathConfig }; -}//namespace cura +} // namespace cura #endif // G_CODE_PATH_CONFIG_H diff --git a/include/Preheat.h b/include/Preheat.h index e9b1097380..dba5d72c08 100644 --- a/include/Preheat.h +++ b/include/Preheat.h @@ -1,23 +1,23 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef PREHEAT_H #define PREHEAT_H -#include -#include // max - -#include "settings/types/Ratio.h" #include "settings/types/Duration.h" +#include "settings/types/Ratio.h" #include "settings/types/Temperature.h" -namespace cura +#include // max +#include + +namespace cura { /*! * Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature. */ -class Preheat +class Preheat { public: /*! @@ -43,7 +43,7 @@ class Preheat /*! * Get the optimal temperature corresponding to a given average flow, * or the initial layer temperature. - * + * * \param extruder The extruder train * \param flow The flow for which to get the optimal temperature * \param is_initial_layer Whether the initial layer temperature should be returned instead of flow-based temperature @@ -53,12 +53,12 @@ class Preheat /*! * Decide when to start warming up again after starting to cool down towards \p temp_mid. - * Two cases are considered: + * Two cases are considered: * the case where the standby temperature is reached \__/ . * and the case where it isn't \/ . - * + * * \warning it is assumed that \p temp_mid is lower than both \p temp_start and \p temp_end. If not somewhat weird results may follow. - * + * // ,temp_end // / . // ,temp_start / . @@ -77,12 +77,12 @@ class Preheat /*! * Decide when to start cooling down again after starting to warm up towards the \p temp_mid - * Two cases are considered: + * Two cases are considered: * the case where the temperature is reached /"""\ . * and the case where it isn't /\ . - * + * * \warning it is assumed that \p temp_mid is higher than both \p temp_start and \p temp_end. If not somewhat weird results may follow. - * + * // _> temp_mid // /""""""""\ . // / \ . @@ -110,6 +110,6 @@ class Preheat Duration getTimeToGoFromTempToTemp(const size_t extruder, const Temperature& temp_before, const Temperature& temp_after, const bool during_printing); }; -} // namespace cura +} // namespace cura #endif // PREHEAT_H \ No newline at end of file diff --git a/include/communication/ArcusCommunicationPrivate.h b/include/communication/ArcusCommunicationPrivate.h index 9c63aa0bce..48534deee4 100644 --- a/include/communication/ArcusCommunicationPrivate.h +++ b/include/communication/ArcusCommunicationPrivate.h @@ -1,16 +1,16 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef ARCUSCOMMUNICATIONPRIVATE_H #define ARCUSCOMMUNICATIONPRIVATE_H #ifdef ARCUS -#include //For ostringstream. - #include "ArcusCommunication.h" //We're adding a subclass to this. #include "SliceDataStruct.h" #include "settings/types/LayerIndex.h" +#include //For ostringstream. + namespace cura { @@ -18,6 +18,7 @@ namespace cura class ArcusCommunication::Private { friend class ArcusCommunicationPrivateTest; + public: Private(); @@ -73,7 +74,7 @@ class ArcusCommunication::Private const size_t millisecUntilNextTry; // How long we wait until we try to connect again. }; -} //namespace cura +} // namespace cura -#endif //ARCUS -#endif //ARCUSCOMMUNICATIONPRIVATE_H \ No newline at end of file +#endif // ARCUS +#endif // ARCUSCOMMUNICATIONPRIVATE_H \ No newline at end of file diff --git a/include/communication/Communication.h b/include/communication/Communication.h index bb7b018c31..48cc6a3133 100644 --- a/include/communication/Communication.h +++ b/include/communication/Communication.h @@ -4,13 +4,13 @@ #ifndef COMMUNICATION_H #define COMMUNICATION_H -#include "utils/IntPoint.h" #include "settings/types/LayerIndex.h" #include "settings/types/Velocity.h" +#include "utils/IntPoint.h" namespace cura { -//Some forward declarations to increase compilation speed. +// Some forward declarations to increase compilation speed. enum class PrintFeatureType : unsigned char; class Polygons; class ConstPolygonRef; @@ -180,7 +180,6 @@ class Communication virtual void sliceNext() = 0; }; -} //namespace cura - -#endif //COMMUNICATION_H +} // namespace cura +#endif // COMMUNICATION_H diff --git a/include/gcodeExport.h b/include/gcodeExport.h index db015e6b8f..c3f367d3a3 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -10,9 +10,9 @@ #endif #include "settings/EnumSettings.h" #include "settings/Settings.h" //For MAX_EXTRUDERS. +#include "settings/types/LayerIndex.h" #include "settings/types/Temperature.h" //Bed temperature. #include "settings/types/Velocity.h" -#include "settings/types/LayerIndex.h" #include "sliceDataStorage.h" #include "timeEstimate.h" #include "utils/AABB3D.h" //To track the used build volume for the Griffin header. diff --git a/include/infill/SubDivCube.h b/include/infill/SubDivCube.h index ad3fe90bab..4b1b31ef52 100644 --- a/include/infill/SubDivCube.h +++ b/include/infill/SubDivCube.h @@ -1,11 +1,11 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef INFILL_SUBDIVCUBE_H #define INFILL_SUBDIVCUBE_H -#include "settings/types/Ratio.h" #include "settings/types/LayerIndex.h" +#include "settings/types/Ratio.h" #include "utils/IntPoint.h" #include "utils/Point3.h" @@ -40,6 +40,7 @@ class SubDivCube * \param result (output) The resulting lines */ void generateSubdivisionLines(const coord_t z, Polygons& result); + private: /*! * Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too. @@ -90,16 +91,14 @@ class SubDivCube static coord_t distanceFromPointToMesh(SliceMeshStorage& mesh, const LayerIndex layer_nr, Point& location, coord_t* distance2); /*! - * Adds the defined line to the specified polygons. It assumes that the specified polygons are all parallel lines. Combines line segments with touching ends closer than epsilon. - * \param[out] group the polygons to add the line to - * \param from the first endpoint of the line - * \param to the second endpoint of the line + * Adds the defined line to the specified polygons. It assumes that the specified polygons are all parallel lines. Combines line segments with touching ends closer than + * epsilon. \param[out] group the polygons to add the line to \param from the first endpoint of the line \param to the second endpoint of the line */ void addLineAndCombine(Polygons& group, Point from, Point to); size_t depth; //!< the recursion depth of the cube (0 is most recursed) Point3 center; //!< center location of the cube in absolute coordinates - SubDivCube* children[8] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; //!< pointers to this cube's eight octree children + SubDivCube* children[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; //!< pointers to this cube's eight octree children static std::vector cube_properties_per_recursion_step; //!< precomputed array of basic properties of cubes based on recursion depth. static Ratio radius_multiplier; //!< multiplier for the bounding radius when determining if a cube should be subdivided static Point3Matrix rotation_matrix; //!< The rotation matrix to get from axis aligned cubes to cubes standing on a corner point aligned with the infill_angle @@ -107,5 +106,5 @@ class SubDivCube static coord_t radius_addition; //!< addition to the bounding radius when determining if a cube should be subdivided }; -} -#endif //INFILL_SUBDIVCUBE_H +} // namespace cura +#endif // INFILL_SUBDIVCUBE_H diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index 4073cdfc21..150f1a9fcb 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -1,14 +1,14 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SETTINGS_PATH_CONFIGS_H #define SETTINGS_PATH_CONFIGS_H -#include - #include "GCodePathConfig.h" -#include "utils/Coord_t.h" #include "settings/types/LayerIndex.h" +#include "utils/Coord_t.h" + +#include namespace cura { @@ -35,6 +35,7 @@ class PathConfigStorage const std::vector line_width_factor_per_extruder; static std::vector getLineWidthFactorPerExtruder(const LayerIndex& layer_nr); + public: class MeshPathConfigs { @@ -44,7 +45,7 @@ class PathConfigStorage GCodePathConfig bridge_inset0_config; GCodePathConfig bridge_insetX_config; GCodePathConfig skin_config; - GCodePathConfig bridge_skin_config; // used for first bridge layer + GCodePathConfig bridge_skin_config; // used for first bridge layer GCodePathConfig bridge_skin_config2; // used for second bridge layer GCodePathConfig bridge_skin_config3; // used for third bridge layer GCodePathConfig roofing_config; diff --git a/include/support.h b/include/support.h index e3e783494a..43a7676db7 100644 --- a/include/support.h +++ b/include/support.h @@ -4,8 +4,8 @@ #ifndef SUPPORT_H #define SUPPORT_H -#include "utils/polygon.h" #include "settings/types/LayerIndex.h" +#include "utils/polygon.h" #include #include diff --git a/include/utils/FMatrix4x3.h b/include/utils/FMatrix4x3.h index 3f324a9deb..64d44138ca 100644 --- a/include/utils/FMatrix4x3.h +++ b/include/utils/FMatrix4x3.h @@ -1,5 +1,5 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2020 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef FMATRIX4X3_H #define FMATRIX4X3_H @@ -33,7 +33,7 @@ class FMatrix4x3 /*! * The matrix data, row-endian. - * + * * The first index is the column. The second index is the row. */ double m[4][3]; @@ -61,5 +61,5 @@ class FMatrix4x3 Point3 apply(const Point3& p) const; }; -} //namespace cura -#endif //FMATRIX4X3_H \ No newline at end of file +} // namespace cura +#endif // FMATRIX4X3_H \ No newline at end of file diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 32b1ca4b21..46f8c97e44 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -1,21 +1,18 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include "InsetOrderOptimizer.h" + #include "ExtruderTrain.h" #include "FffGcodeWriter.h" -#include "InsetOrderOptimizer.h" #include "LayerPlan.h" #include "utils/views/convert.h" #include "utils/views/dfs.h" -#include - -#include -#include #include #include -#include #include +#include #include #include #include @@ -26,6 +23,10 @@ #include #include #include +#include + +#include +#include namespace rg = ranges; namespace rv = ranges::views; @@ -33,22 +34,23 @@ namespace rv = ranges::views; namespace cura { -InsetOrderOptimizer::InsetOrderOptimizer(const FffGcodeWriter& gcode_writer, - const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const Settings& settings, - const int extruder_nr, - const GCodePathConfig& inset_0_non_bridge_config, - const GCodePathConfig& inset_X_non_bridge_config, - const GCodePathConfig& inset_0_bridge_config, - const GCodePathConfig& inset_X_bridge_config, - const bool retract_before_outer_wall, - const coord_t wall_0_wipe_dist, - const coord_t wall_x_wipe_dist, - const size_t wall_0_extruder_nr, - const size_t wall_x_extruder_nr, - const ZSeamConfig& z_seam_config, - const std::vector& paths) +InsetOrderOptimizer::InsetOrderOptimizer( + const FffGcodeWriter& gcode_writer, + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const Settings& settings, + const int extruder_nr, + const GCodePathConfig& inset_0_non_bridge_config, + const GCodePathConfig& inset_X_non_bridge_config, + const GCodePathConfig& inset_0_bridge_config, + const GCodePathConfig& inset_X_bridge_config, + const bool retract_before_outer_wall, + const coord_t wall_0_wipe_dist, + const coord_t wall_x_wipe_dist, + const size_t wall_0_extruder_nr, + const size_t wall_x_extruder_nr, + const ZSeamConfig& z_seam_config, + const std::vector& paths) : gcode_writer(gcode_writer) , storage(storage) , gcode_layer(gcode_layer) @@ -94,7 +96,8 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool group_outer_walls = true; // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. - PathOrderOptimizer order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config, detect_loops, combing_boundary, reverse, order, group_outer_walls); + PathOrderOptimizer + order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition(), z_seam_config, detect_loops, combing_boundary, reverse, order, group_outer_walls); for (const auto& line : walls_to_be_added) { @@ -157,7 +160,7 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& { const auto poly = std::get<1>(locator); const auto line = std::get<0>(locator); - return LineLoc { + return LineLoc{ .line = line, .poly = poly, .area = line->is_closed ? poly.area() : 0.0, @@ -166,7 +169,13 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& | rg::to_vector; // Sort polygons by increasing area, we are building the graph from the leaves (smallest area) upwards. - rg::sort( locator_view, [](const auto& lhs, const auto& rhs) { return std::abs(lhs) < std::abs(rhs); }, &LineLoc::area); + rg::sort( + locator_view, + [](const auto& lhs, const auto& rhs) + { + return std::abs(lhs) < std::abs(rhs); + }, + &LineLoc::area); // Create a bi-direction directed acyclic graph (Tree). Where polygon B is a child of A if B is inside A. The root of the graph is // the polygon that contains all other polygons. The leaves are polygons that contain no polygons. @@ -209,20 +218,19 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& // - mark all reachable nodes with their depth from the root // - find hole roots, these are the innermost polygons enclosing a hole { - const std::function initialize_nodes = - [graph, root, &hole_roots, &min_node, &min_depth] - (const auto current_node, const auto depth) - { - min_node[current_node] = root; - min_depth[current_node] = depth; + const std::function initialize_nodes + = [graph, root, &hole_roots, &min_node, &min_depth](const auto current_node, const auto depth) + { + min_node[current_node] = root; + min_depth[current_node] = depth; - // find hole roots (defined by a positive area in clipper1), these are leaves of the tree structure - // as odd walls are also leaves we filter them out by adding a non-zero area check - if (current_node != root && graph.count(current_node) == 1 && current_node->line->is_closed && current_node->area > 0) - { - hole_roots.push_back(current_node); - } - }; + // find hole roots (defined by a positive area in clipper1), these are leaves of the tree structure + // as odd walls are also leaves we filter them out by adding a non-zero area check + if (current_node != root && graph.count(current_node) == 1 && current_node->line->is_closed && current_node->area > 0) + { + hole_roots.push_back(current_node); + } + }; actions::dfs_depth_state(root, graph, initialize_nodes); }; @@ -233,16 +241,14 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& { for (auto& hole_root : hole_roots) { - const std::function update_nodes = - [hole_root, &min_depth, &min_node] - (const auto& current_node, auto depth) + const std::function update_nodes = [hole_root, &min_depth, &min_node](const auto& current_node, auto depth) + { + if (depth < min_depth[current_node]) { - if (depth < min_depth[current_node]) - { - min_depth[current_node] = depth; - min_node[current_node] = hole_root; - } - }; + min_depth[current_node] = depth; + min_node[current_node] = hole_root; + } + }; actions::dfs_depth_state(hole_root, graph, update_nodes); } @@ -252,22 +258,21 @@ InsetOrderOptimizer::value_type InsetOrderOptimizer::getRegionOrder(const auto& // the depth is closest to root $r$ { const LineLoc* root_ = root; - const std::function set_order_constraints = - [&order, &min_node, &root_, graph, outer_to_inner] - (const auto& current_node, const auto& parent_node) + const std::function set_order_constraints + = [&order, &min_node, &root_, graph, outer_to_inner](const auto& current_node, const auto& parent_node) + { + if (min_node[current_node] == root_ && parent_node != nullptr) { - if (min_node[current_node] == root_ && parent_node != nullptr) - { - if (outer_to_inner) - { - order.insert(std::make_pair(parent_node->line, current_node->line)); - } - else - { - order.insert(std::make_pair(current_node->line, parent_node->line)); - } - } - }; + if (outer_to_inner) + { + order.insert(std::make_pair(parent_node->line, current_node->line)); + } + else + { + order.insert(std::make_pair(current_node->line, parent_node->line)); + } + } + }; actions::dfs_parent_state(root, graph, set_order_constraints); @@ -354,7 +359,7 @@ std::vector InsetOrderOptimizer::getWallsToBeAdded(const bool rev { if (paths.empty()) { - return { }; + return {}; } rg::any_view view; if (reverse) From 81e21ede05dfc7e72731db3291153435e7504966 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 9 Aug 2023 17:47:52 +0200 Subject: [PATCH 338/656] Revert "Set higher min. line-length for bridging caculations." This reverts commit 928c9dc914a21c55574c4146a04b3158e5573834. --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 7988479dd9..dd153eedec 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -688,7 +688,7 @@ void LayerPlan::addWallLine( Ratio speed_factor, double distance_to_bridge_start) { - const coord_t min_line_len = settings.get("meshfix_maximum_resolution") / 2; // Shouldn't cut up stuff (too much) below the required simplify resolution. + 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 double acceleration_factor = 0.75; // must be < 1, the larger the value, the slower the acceleration const bool spiralize = false; From af21003cdd18e32bf58d5baf15cf850051cb47c7 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 9 Aug 2023 22:51:59 +0200 Subject: [PATCH 339/656] Update fluid motion mesh fix logic This includes updated fluid motion logic. A new definition of fluid motion is introduced, here a tool-path-part is considered to be fluid if all of the following criterial hold a) Vectors [A,B] and [C,D] are "long" (settable through settings) b) Vector [B,C] is "short" (settable through settings) c) either on the angles between vectors - [A_,D_] and BC or, - [A_,B] and BC or, - [B,C] and [C,D_] is below a threshold (settable through the settings) B--C / \ A_ D_ / \ / \ A \ \ D Unit tests are added, both to show intent of the `isSmooth` predicate and to test against future regressions. CURA-10811 --- include/utils/actions/smooth.h | 244 +++++++++++++++++++++++---------- include/utils/types/geometry.h | 19 ++- src/WallToolPaths.cpp | 4 +- tests/test_global_settings.txt | 6 +- tests/utils/SmoothTest.cpp | 155 +++++++++++++++++++-- 5 files changed, 331 insertions(+), 97 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 21a0f44698..d7960239ba 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -4,6 +4,8 @@ #ifndef UTILS_VIEWS_SMOOTH_H #define UTILS_VIEWS_SMOOTH_H +#include "settings/Settings.h" +#include "settings/types/Angle.h" #include "utils/types/arachne.h" #include "utils/types/generic.h" #include "utils/types/geometry.h" @@ -18,36 +20,51 @@ #include #include #include +#include #include #include #include +namespace cura +{ + class SmoothTest_TestSmooth_Test; +} + namespace cura::actions { struct smooth_fn { - constexpr auto operator()(const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const + friend class cura::SmoothTest_TestSmooth_Test; + + const auto operator()(const Settings& settings) const { - return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, fluid_angle)); + const auto fluid_motion_shift_distance = settings.get("meshfix_fluid_motion_shift_distance"); + const auto fluid_motion_small_distance = settings.get("meshfix_fluid_motion_small_distance"); + const auto fluid_motion_angle = settings.get("meshfix_fluid_motion_angle").value; + return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, fluid_motion_shift_distance, fluid_motion_small_distance, fluid_motion_angle)); + } + + constexpr auto operator()(const utils::integral auto fluid_motion_shift_distance, const utils::integral auto fluid_motion_small_distance, const utils::floating_point auto fluid_motion_angle) const + { + return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, fluid_motion_shift_distance, fluid_motion_small_distance, fluid_motion_angle)); } template - requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> &&( + requires + ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> &&( utils::point2d> || utils::junctions)constexpr auto - operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const + operator()(Rng&& rng, const utils::integral auto fluid_motion_shift_distance, const utils::integral auto fluid_motion_small_distance, const utils::floating_point auto fluid_motion_angle) const { + const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; + const auto size = ranges::distance(rng) - 1; if (size < 4) { return static_cast(rng); } - using coord_type = std::remove_cvref_t(*ranges::begin(rng)))>; - const auto allowed_deviation = static_cast(max_resolution * 2 / 3); // The allowed deviation from the original path - const auto smooth_distance = static_cast(max_resolution / 2); // The distance over which the path is smoothed - auto tmp = rng; // We don't want to shift the points of the in-going range, therefore we create a temporary copy auto windows = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; @@ -55,22 +72,21 @@ struct smooth_fn // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. for (auto windows_it = ranges::begin(windows); ranges::distance(windows_it, ranges::end(windows)) > 2; ++windows_it) { - auto A = *windows_it; - auto B = *std::next(windows_it, 1); - auto C = *std::next(windows_it, 2); - auto D = *std::next(windows_it, 3); + const auto A = *windows_it; + const auto B = *std::next(windows_it, 1); + const auto C = *std::next(windows_it, 2); + const auto D = *std::next(windows_it, 3); + + if (dist(*A, *B) < fluid_motion_shift_distance3 || dist(*B, *C) > fluid_motion_small_distance || dist(*C, *D) < fluid_motion_shift_distance3) + { + continue; + } - const auto [AB_magnitude, BC_magnitude, CD_magnitude] = computeMagnitudes(A, B, C, D); - if (! isWithinAllowedDeviations(A, B, C, D, fluid_angle, max_resolution, AB_magnitude, BC_magnitude, CD_magnitude)) + const auto cos_fluid_motion_angle = std::cos(fluid_motion_angle); + if (! isSmooth(*A, *B, *C, *D, cos_fluid_motion_angle)) { - if (AB_magnitude > allowed_deviation) - { - shiftPointTowards(B, A, AB_magnitude, smooth_distance); - } - if (CD_magnitude > allowed_deviation) - { - shiftPointTowards(C, D, CD_magnitude, smooth_distance); - } + *A = shiftPointTowards(*B, *A, fluid_motion_shift_distance); + *D = shiftPointTowards(*C, *D, fluid_motion_shift_distance); } } @@ -78,80 +94,170 @@ struct smooth_fn } private: + /* + * cosine of the angle between the vectors AB and BC + * + * B---------C + * | \ + * | angle + * | + * | + * A + * + */ template requires utils::point2d || utils::junction - constexpr auto computeMagnitudes(Point* A, Point* B, Point* C, Point* D) const noexcept + auto cosAngle(Point& A, Point& B, Point& C) const noexcept { - const auto AB_magnitude = std::hypot(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); - const auto BC_magnitude = std::hypot(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); - const auto CD_magnitude = std::hypot(std::get<"X">(*D) - std::get<"X">(*C), std::get<"Y">(*D) - std::get<"Y">(*C)); + return cosAngle(A, B, C, dist(A, B), dist(B, C)); + } - return std::make_tuple(AB_magnitude, BC_magnitude, CD_magnitude); + template + requires utils::point2d || utils::junction + auto cosAngle(Point& A, Point& B, Point& C, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept + { + return cosAngle(A, B, B, C, AB_magnitude, BC_magnitude); } + /* + * cosine of the angle between the vectors AB and CD + * + * A C + * | \ + * | \ + * B D + * + * The angle will be calculated by shifting points A and C towards the origin, + * and then calculating the angle between the vectors AB and CD. + * + * A,C + * | \ + * | \ + * B D + * + */ template requires utils::point2d || utils::junction - constexpr auto cosAngle(Point* A, Point* B, Point* C, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept + auto cosAngle(Point& A, Point& B, Point& C, Point& D) const noexcept + { + return cosAngle(A, B, C, D, dist(A, B), dist(C, D)); + } + + template + requires utils::point2d || utils::junction + auto cosAngle(Point& A, Point& B, Point& C, Point& D, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept + { + Point VectorA = { std::get<"X">(B) - std::get<"X">(A), std::get<"Y">(B) - std::get<"Y">(A) }; + Point VectorB = { std::get<"X">(D) - std::get<"X">(C), std::get<"Y">(D) - std::get<"Y">(C) }; + + return cosAngle(VectorA, VectorB, AB_magnitude, BC_magnitude); + } + + /* + * cosine of the angle between the vectors A and B + * + * O (origin) + * | \ + * | \ + * A B + * + */ + template + requires utils::point2d || utils::junction + auto cosAngle(Vector& A, Vector& B) const noexcept + { + return cosAngle(A, B, magnitude(A), magnitude(B)); + } + + template + requires utils::point2d || utils::junction + auto cosAngle(Vector& A, Vector& B, const utils::floating_point auto A_magnitude, const utils::floating_point auto B_magnitude) const noexcept { - if (AB_magnitude == 0.0 || BC_magnitude == 0.0) + if (A_magnitude <= FLT_EPSILON || B_magnitude <= FLT_EPSILON) { - return 0.0; + return static_cast(0.0); } - auto AB = std::make_tuple(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); - auto BC = std::make_tuple(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); + return static_cast(dotProduct(A, B)) / (A_magnitude * B_magnitude); + } - const auto dot = dotProduct(&AB, &BC); - return dot / (AB_magnitude * BC_magnitude); + template + requires utils::point2d || utils::junction + constexpr Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance) const noexcept + { + return shiftPointTowards(p0, p1, move_distance, dist(p0, p1)); } template requires utils::point2d || utils::junction - constexpr void shiftPointTowards(Point* point, Point* target, const utils::floating_point auto p0p1_distance, const utils::integral auto smooth_distance) const noexcept + Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) const noexcept { - using coord_type = std::remove_cvref_t(*point))>; - const auto shift_distance = smooth_distance / p0p1_distance; - const auto shift_distance_x = static_cast((std::get<"X">(*target) - std::get<"X">(*point)) * shift_distance); - const auto shift_distance_y = static_cast((std::get<"Y">(*target) - std::get<"Y">(*point)) * shift_distance); - if constexpr (utils::point2d) - { - point->X += shift_distance_x; - point->Y += shift_distance_y; - } - else - { - point->p.X += shift_distance_x; - point->p.Y += shift_distance_y; - } + using coord_type = std::remove_cvref_t(p0))>; + const auto shift_distance = move_distance / p0p1_distance; + return { + std::get<"X">(p0) + static_cast((std::get<"X">(p1) - std::get<"X">(p0)) * shift_distance), + std::get<"Y">(p0) + static_cast((std::get<"Y">(p1) - std::get<"Y">(p0)) * shift_distance), + }; + } + + template + requires utils::point2d || utils::junction + utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept + { + return std::hypot(std::get<"X">(point_0) - std::get<"X">(point_1), std::get<"Y">(point_0) - std::get<"Y">(point_1)); } template requires utils::point2d || utils::junction - constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept + utils::floating_point auto magnitude(Vector& v) const noexcept { - return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); + return std::hypot(std::get<"X">(v), std::get<"Y">(v)); + } + + template + requires utils::point2d || utils::junction + auto dotProduct(Vector& point_0, Vector& point_1) const noexcept + { + return std::get<"X">(point_0) * std::get<"X">(point_1) + std::get<"Y">(point_0) * std::get<"Y">(point_1); } template requires utils::point2d || utils::junction - constexpr auto isWithinAllowedDeviations( - Point* A, - Point* B, - Point* C, - Point* D, - const utils::floating_point auto fluid_angle, - const utils::integral auto max_resolution, - const utils::floating_point auto AB_magnitude, - const utils::floating_point auto BC_magnitude, - const utils::floating_point auto CD_magnitude) const noexcept + bool isSmooth( + Point& A, + Point& B, + Point& C, + Point& D, + utils::floating_point auto fluid_motion_angle + ) const noexcept { - if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this - { - return true; - } - const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); - const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); - const auto abs_angle = std::abs(cos_A - cos_B); - return abs_angle < fluid_angle; + /* + * Move points A and B, so they are both at equal distance from C and D + * + * B--C + * / \ + * A_ D_ + * / \ + * / \ + * A \ + * \ + * D + * + * Points B, C are in a "fluid motion" with points A, D if + * vectors [A_,D_] and [B,C] are oriented within a certain angle + */ + constexpr auto shift_distance = 300.; + auto A_ = shiftPointTowards(B, A, shift_distance); + auto D_ = shiftPointTowards(C, D, shift_distance); + + // precompute distance BC + const auto BC_magnitude = dist(B, C); + + const auto cos_angle_fluid = cosAngle(A_, D_, B, C, dist(A_, D_), BC_magnitude); + const auto cos_angle_abc = cosAngle(A_, B, C, shift_distance, BC_magnitude); + const auto cos_angle_bcd = cosAngle(B, C, D_, BC_magnitude, shift_distance); + + // tThe motion is fluid if either of the marker angles is smaller than the max angle + return cos_angle_fluid >= fluid_motion_angle || cos_angle_abc >= fluid_motion_angle || cos_angle_bcd >= fluid_motion_angle; } }; diff --git a/include/utils/types/geometry.h b/include/utils/types/geometry.h index b87880422c..76620bc38c 100644 --- a/include/utils/types/geometry.h +++ b/include/utils/types/geometry.h @@ -31,7 +31,7 @@ template concept point2d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type>>; - requires utils::integral>; + requires utils::numeric>; }; /*! @@ -43,7 +43,7 @@ template concept point2d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 2; - requires utils::integral>; + requires utils::numeric>; }; @@ -55,8 +55,8 @@ concept point2d_ranged = ranges::range && requires(T point) template concept point2d_named = requires(T point) { - requires utils::integral; - requires utils::integral; + requires utils::numeric; + requires utils::numeric; }; /*! @@ -76,7 +76,7 @@ template concept point3d_tuple = requires(T t) { requires std::is_same_v::type, typename std::tuple_element<0, T>::type, typename std::tuple_element<0, T>::type>>; - requires utils::integral>; + requires utils::numeric>; }; /*! @@ -88,8 +88,7 @@ template concept point3d_ranged = ranges::range && requires(T point) { requires ranges::size(point) == 3; - requires utils::integral>; - + requires utils::numeric>; }; /*! @@ -100,9 +99,9 @@ concept point3d_ranged = ranges::range && requires(T point) template concept point3d_named = requires(T point) { - requires utils::integral; - requires utils::integral; - requires utils::integral; + requires utils::numeric; + requires utils::numeric; + requires utils::numeric; }; /*! diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index dbb87b027a..ef5bfe17e5 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -77,10 +77,10 @@ const std::vector& WallToolPaths::generate() scripta::log("prepared_outline_0", prepared_outline, section_type, layer_idx); prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); prepared_outline = Simplify(settings).polygon(prepared_outline); - if (section_type != SectionType::SUPPORT) + if (settings.get("meshfix_fluid_motion_enabled") && section_type != SectionType::SUPPORT) { // No need to smooth support walls - auto smoother = actions::smooth(settings.get("meshfix_maximum_resolution"), static_cast(settings.get("wall_transition_angle"))); + auto smoother = actions::smooth(settings); for (auto& polygon : prepared_outline) { polygon = smoother(polygon); diff --git a/tests/test_global_settings.txt b/tests/test_global_settings.txt index 684e94a160..cb0c373c6a 100644 --- a/tests/test_global_settings.txt +++ b/tests/test_global_settings.txt @@ -478,4 +478,8 @@ raft_interface_thickness=0.30000000000000004 retraction_hop_only_when_collides=True ironing_flow=10.0 material_shrinkage_percentage_z=100 -material_shrinkage_percentage_xy=100 \ No newline at end of file +material_shrinkage_percentage_xy=100 +meshfix_fluid_motion_enabled=True +meshfix_fluid_motion_shift_distance=0.03 +meshfix_fluid_motion_small_distance=0.001 +meshfix_fluid_motion_angle=5 diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index 4d6361e12e..b24805f1f1 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -16,20 +16,145 @@ namespace cura TEST(SmoothTest, TestSmooth) { - // TODO: Write some actual tests - Polygon poly; - poly.poly = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, - { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; - auto smoother = cura::actions::smooth(2000, 10.0); - Polygon smoothed; - smoothed.poly = smoother(poly.poly); - - std::vector expected{ { -137, 188 }, { 1910, 540 }, { 2820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, - { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; - EXPECT_EQ(smoothed.poly, expected); - - auto original_expected = std::vector{ { -137, 188 }, { 1910, 540 }, { 3820, 540 }, { 3850, 640 }, { 5040, 780 }, { 5660, 2800 }, { 5420, 2720 }, { 5500, 2850 }, - { 5530, 2970 }, { 5290, 3450 }, { 1610, 4030 }, { 1090, 3220 }, { 1060, 3210 }, { 1010, 3210 }, { 970, 3220 }, { -740, 3940 } }; - EXPECT_EQ(poly.poly, original_expected); + // test isSmooth utility function + cura::actions::smooth_fn smooth; + const auto FLUID_ANGLE = 5.; + const auto COS_FLUID_ANGLE = std::cos(FLUID_ANGLE * M_PI / 180.); + + { + /* + * + * A ------------- B + * | + * C --------------- D + * + */ + + auto A = cura::Point { 0, 0 }; + auto B = cura::Point { 0, 100 }; + auto C = cura::Point { 1, 100 }; + auto D = cura::Point { 1, 200 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, false); + } + + { + /* + * + * A ----------- B + * \ + * C + * | + * | + * | + * D + * + */ + auto A = cura::Point { 0, 0 }; + auto B = cura::Point { 100, 0 }; + auto C = cura::Point { 101, 1 }; + auto D = cura::Point { 101, 101 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, true); + } + + { + /* + * + * A ----------- B - C -------------D + * + */ + auto A = cura::Point { 0, 0 }; + auto B = cura::Point { 100, 0 }; + auto C = cura::Point { 101, 0 }; + auto D = cura::Point { 201, 0 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, true); + } + + { + /* + * + * D ----------- C - B -------------A + * + */ + auto A = cura::Point { 201, 0 }; + auto B = cura::Point { 101, 0 }; + auto C = cura::Point { 100, 0 }; + auto D = cura::Point { 0, 0 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, true); + } + + { + /* + * + * + * B + * \ + * D ----------- C + * \ + * \ + * \ + * \ + * \ + * D + * + */ + auto A = cura::Point { 0, 0 }; + auto B = cura::Point { 100, 0 }; + auto C = cura::Point { 99, -1 }; + auto D = cura::Point { 199, 99 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, false); + } + + { + /* + * + * D ----------- C + * \ + * B + * \ + * \ + * \ + * \ + * D + * + */ + auto A = cura::Point { 0, 0 }; + auto B = cura::Point { 100, 0 }; + auto C = cura::Point { 101, 1 }; + auto D = cura::Point { 201, 101 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, true); + } + + { + /* + * + * A ----------- B - C + * | + * | + * | + * | + * D + * + */ + cura::Point A = { 0, 0 }; + cura::Point B = { 100, 0 }; + cura::Point C = { 101, 0 }; + cura::Point D = { 101, 100 }; + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, true); + } + } } // namespace cura \ No newline at end of file From 7ddb9f3704403c93f70087d45a7f1661ad8cfaf1 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Wed, 9 Aug 2023 20:52:32 +0000 Subject: [PATCH 340/656] Applied clang-format. --- include/utils/actions/smooth.h | 33 ++--- src/WallToolPaths.cpp | 260 ++++++++++++++++++++------------- 2 files changed, 171 insertions(+), 122 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index d7960239ba..47ccaa6ebd 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -28,7 +28,7 @@ namespace cura { - class SmoothTest_TestSmooth_Test; +class SmoothTest_TestSmooth_Test; } namespace cura::actions @@ -46,16 +46,22 @@ struct smooth_fn return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, fluid_motion_shift_distance, fluid_motion_small_distance, fluid_motion_angle)); } - constexpr auto operator()(const utils::integral auto fluid_motion_shift_distance, const utils::integral auto fluid_motion_small_distance, const utils::floating_point auto fluid_motion_angle) const + constexpr auto operator()( + const utils::integral auto fluid_motion_shift_distance, + const utils::integral auto fluid_motion_small_distance, + const utils::floating_point auto fluid_motion_angle) const { return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, fluid_motion_shift_distance, fluid_motion_small_distance, fluid_motion_angle)); } template - requires - ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> &&( + requires ranges::forward_range && ranges::sized_range && ranges::erasable_range, ranges::sentinel_t> &&( utils::point2d> || utils::junctions)constexpr auto - operator()(Rng&& rng, const utils::integral auto fluid_motion_shift_distance, const utils::integral auto fluid_motion_small_distance, const utils::floating_point auto fluid_motion_angle) const + operator()( + Rng&& rng, + const utils::integral auto fluid_motion_shift_distance, + const utils::integral auto fluid_motion_small_distance, + const utils::floating_point auto fluid_motion_angle) const { const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; @@ -189,7 +195,8 @@ struct smooth_fn template requires utils::point2d || utils::junction - Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) const noexcept + Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) + const noexcept { using coord_type = std::remove_cvref_t(p0))>; const auto shift_distance = move_distance / p0p1_distance; @@ -200,15 +207,13 @@ struct smooth_fn } template - requires utils::point2d || utils::junction - utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept + requires utils::point2d || utils::junction utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept { return std::hypot(std::get<"X">(point_0) - std::get<"X">(point_1), std::get<"Y">(point_0) - std::get<"Y">(point_1)); } template - requires utils::point2d || utils::junction - utils::floating_point auto magnitude(Vector& v) const noexcept + requires utils::point2d || utils::junction utils::floating_point auto magnitude(Vector& v) const noexcept { return std::hypot(std::get<"X">(v), std::get<"Y">(v)); } @@ -222,13 +227,7 @@ struct smooth_fn template requires utils::point2d || utils::junction - bool isSmooth( - Point& A, - Point& B, - Point& C, - Point& D, - utils::floating_point auto fluid_motion_angle - ) const noexcept + bool isSmooth(Point& A, Point& B, Point& C, Point& D, utils::floating_point auto fluid_motion_angle) const noexcept { /* * Move points A and B, so they are both at equal distance from C and D diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index ef5bfe17e5..f7c9f58176 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -1,29 +1,35 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include //For std::partition_copy and std::min_element. -#include - -#include -#include -#include -#include - #include "WallToolPaths.h" #include "ExtruderTrain.h" #include "SkeletalTrapezoidation.h" #include "utils/PolylineStitcher.h" #include "utils/Simplify.h" -#include "utils/actions/smooth.h" #include "utils/SparsePointGrid.h" //To stitch the inner contour. +#include "utils/actions/smooth.h" #include "utils/polygonUtils.h" +#include +#include +#include +#include + +#include //For std::partition_copy and std::min_element. +#include + 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, const int layer_idx, SectionType section_type) +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, + const int layer_idx, + SectionType section_type) : outline(outline) , bead_width_0(nominal_bead_width) , bead_width_x(nominal_bead_width) @@ -40,8 +46,15 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead { } -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, const int layer_idx, SectionType section_type ) +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, + const int layer_idx, + SectionType section_type) : outline(outline) , bead_width_0(bead_width_0) , bead_width_x(bead_width_x) @@ -54,7 +67,7 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0 , toolpaths_generated(false) , settings(settings) , layer_idx(layer_idx) - , section_type( section_type ) + , section_type(section_type) { } @@ -66,7 +79,8 @@ const std::vector& WallToolPaths::generate() // with half the minimum printable feature size or minimum line width, these slivers are removed, while still // keeping enough information to not degrade the print quality; // These features can't be printed anyhow. See PR CuraEngine#1811 for some screenshots - const coord_t open_close_distance = settings.get("fill_outline_gaps") ? settings.get("min_feature_size") / 2 - 5 : settings.get("min_wall_line_width") / 2 - 5; + const coord_t open_close_distance + = settings.get("fill_outline_gaps") ? settings.get("min_feature_size") / 2 - 5 : settings.get("min_wall_line_width") / 2 - 5; 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); @@ -116,25 +130,22 @@ const std::vector& WallToolPaths::generate() const int wall_distribution_count = settings.get("wall_distribution_count"); const size_t max_bead_count = (inset_count < std::numeric_limits::max() / 2) ? 2 * inset_count : std::numeric_limits::max(); - const auto beading_strat = BeadingStrategyFactory::makeStrategy - ( - bead_width_0, - bead_width_x, - wall_transition_length, - transitioning_angle, - print_thin_walls, - min_bead_width, - min_feature_size, - wall_split_middle_threshold, - wall_add_middle_threshold, - max_bead_count, - wall_0_inset, - wall_distribution_count - ); + const auto beading_strat = BeadingStrategyFactory::makeStrategy( + bead_width_0, + bead_width_x, + wall_transition_length, + transitioning_angle, + print_thin_walls, + min_bead_width, + min_feature_size, + wall_split_middle_threshold, + wall_add_middle_threshold, + max_bead_count, + wall_0_inset, + wall_distribution_count); const coord_t transition_filter_dist = settings.get("wall_transition_filter_distance"); const coord_t allowed_filter_deviation = settings.get("wall_transition_filter_deviation"); - SkeletalTrapezoidation wall_maker - ( + SkeletalTrapezoidation wall_maker( prepared_outline, *beading_strat, beading_strat->getTransitioningAngle(), @@ -143,73 +154,101 @@ const std::vector& WallToolPaths::generate() allowed_filter_deviation, wall_transition_length, layer_idx, - section_type - ); + section_type); wall_maker.generateToolpaths(toolpaths); - scripta::log("toolpaths_0", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "toolpaths_0", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); stitchToolPaths(toolpaths, settings); - scripta::log("toolpaths_1", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); - + scripta::log( + "toolpaths_1", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); + removeSmallLines(toolpaths); - scripta::log("toolpaths_2", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "toolpaths_2", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); simplifyToolPaths(toolpaths, settings); - scripta::log("toolpaths_3", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "toolpaths_3", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); separateOutInnerContour(); removeEmptyToolPaths(toolpaths); - scripta::log("toolpaths_4", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); - assert(std::is_sorted(toolpaths.cbegin(), toolpaths.cend(), - [](const VariableWidthLines& l, const VariableWidthLines& r) - { - return l.front().inset_idx < r.front().inset_idx; - }) && "WallToolPaths should be sorted from the outer 0th to inner_walls"); + scripta::log( + "toolpaths_4", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); + assert( + std::is_sorted( + toolpaths.cbegin(), + toolpaths.cend(), + [](const VariableWidthLines& l, const VariableWidthLines& r) + { + return l.front().inset_idx < r.front().inset_idx; + }) + && "WallToolPaths should be sorted from the outer 0th to inner_walls"); toolpaths_generated = true; - scripta::log("toolpaths_5", toolpaths, section_type, layer_idx, - scripta::CellVDI{"is_closed", &ExtrusionLine::is_closed }, - scripta::CellVDI{"is_odd", &ExtrusionLine::is_odd }, - scripta::CellVDI{"inset_idx", &ExtrusionLine::inset_idx }, - scripta::PointVDI{"width", &ExtrusionJunction::w }, - scripta::PointVDI{"perimeter_index", &ExtrusionJunction::perimeter_index }); + scripta::log( + "toolpaths_5", + toolpaths, + section_type, + layer_idx, + scripta::CellVDI{ "is_closed", &ExtrusionLine::is_closed }, + scripta::CellVDI{ "is_odd", &ExtrusionLine::is_odd }, + scripta::CellVDI{ "inset_idx", &ExtrusionLine::inset_idx }, + scripta::PointVDI{ "width", &ExtrusionJunction::w }, + scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index }); return toolpaths; } void WallToolPaths::stitchToolPaths(std::vector& toolpaths, const Settings& settings) { - const coord_t stitch_distance = settings.get("wall_line_width_x") - 1; //In 0-width contours, junctions can cause up to 1-line-width gaps. Don't stitch more than 1 line width. + const coord_t stitch_distance + = settings.get("wall_line_width_x") - 1; // In 0-width contours, junctions can cause up to 1-line-width gaps. Don't stitch more than 1 line width. for (unsigned int wall_idx = 0; wall_idx < toolpaths.size(); wall_idx++) { VariableWidthLines& wall_lines = toolpaths[wall_idx]; - + VariableWidthLines stitched_polylines; VariableWidthLines closed_polygons; PolylineStitcher::stitch(wall_lines, stitched_polylines, closed_polygons, stitch_distance); @@ -261,23 +300,29 @@ void WallToolPaths::simplifyToolPaths(std::vector& toolpaths for (auto& toolpath : toolpaths) { toolpath = toolpath - | ranges::views::transform([&simplifier](auto& line) { - auto line_ = line.is_closed ? simplifier.polygon(line) : simplifier.polyline(line); - - if (line_.is_closed && line_.size() >= 2 && line_.front() != line_.back()) - { - line_.emplace_back(line_.front()); - } - return line_; - }) - | ranges::views::filter([](const auto& line) { return ! line.empty(); }) + | ranges::views::transform( + [&simplifier](auto& line) + { + auto line_ = line.is_closed ? simplifier.polygon(line) : simplifier.polyline(line); + + if (line_.is_closed && line_.size() >= 2 && line_.front() != line_.back()) + { + line_.emplace_back(line_.front()); + } + return line_; + }) + | ranges::views::filter( + [](const auto& line) + { + return ! line.empty(); + }) | ranges::to_vector; } } const std::vector& WallToolPaths::getToolPaths() { - if (!toolpaths_generated) + if (! toolpaths_generated) { return generate(); } @@ -295,9 +340,9 @@ void WallToolPaths::pushToolPaths(std::vector& paths) void WallToolPaths::separateOutInnerContour() { - //We'll remove all 0-width paths from the original toolpaths and store them separately as polygons. + // We'll remove all 0-width paths from the original toolpaths and store them separately as polygons. std::vector actual_toolpaths; - actual_toolpaths.reserve(toolpaths.size()); //A bit too much, but the correct order of magnitude. + actual_toolpaths.reserve(toolpaths.size()); // A bit too much, but the correct order of magnitude. std::vector contour_paths; contour_paths.reserve(toolpaths.size() / inset_count); inner_contour.clear(); @@ -323,8 +368,8 @@ void WallToolPaths::separateOutInnerContour() break; } } - - + + if (is_contour) { #ifdef DEBUG @@ -355,28 +400,28 @@ void WallToolPaths::separateOutInnerContour() } if (! actual_toolpaths.empty()) { - toolpaths = std::move(actual_toolpaths); //Filtered out the 0-width paths. + toolpaths = std::move(actual_toolpaths); // Filtered out the 0-width paths. } else { toolpaths.clear(); } - //The output walls from the skeletal trapezoidation have no known winding order, especially if they are joined together from polylines. - //They can be in any direction, clockwise or counter-clockwise, regardless of whether the shapes are positive or negative. - //To get a correct shape, we need to make the outside contour positive and any holes inside negative. - //This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon. - //The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation. + // The output walls from the skeletal trapezoidation have no known winding order, especially if they are joined together from polylines. + // They can be in any direction, clockwise or counter-clockwise, regardless of whether the shapes are positive or negative. + // To get a correct shape, we need to make the outside contour positive and any holes inside negative. + // This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon. + // The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation. inner_contour = inner_contour.processEvenOdd(); } const Polygons& WallToolPaths::getInnerContour() { - if (!toolpaths_generated && inset_count > 0) + if (! toolpaths_generated && inset_count > 0) { generate(); } - else if(inset_count == 0) + else if (inset_count == 0) { return outline; } @@ -385,10 +430,15 @@ const Polygons& WallToolPaths::getInnerContour() bool WallToolPaths::removeEmptyToolPaths(std::vector& toolpaths) { - toolpaths.erase(std::remove_if(toolpaths.begin(), toolpaths.end(), [](const VariableWidthLines& lines) - { - return lines.empty(); - }), toolpaths.end()); + toolpaths.erase( + std::remove_if( + toolpaths.begin(), + toolpaths.end(), + [](const VariableWidthLines& lines) + { + return lines.empty(); + }), + toolpaths.end()); return toolpaths.empty(); } From 3065d19489ceabe5056cefce25bc6140809f0bf6 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:01:26 +0200 Subject: [PATCH 341/656] Add GcodeAnalyzer workflow Should run on each push, slice and analyze the files --- .github/workflows/gcodeanalyzer.yml | 276 ++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 .github/workflows/gcodeanalyzer.yml diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml new file mode 100644 index 0000000000..b3f217f0e9 --- /dev/null +++ b/.github/workflows/gcodeanalyzer.yml @@ -0,0 +1,276 @@ +name: GcodeAnalyzer +on: + push: + paths: + - 'include/**' + - 'src/**' + - 'cmake/**' + - 'benchmark/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - '.github/workflows/benchmark.yml' + - '.github/workflows/requirements-conan-package.txt' + branches: + - main + - gcodeanalyzer + tags: + - '[0-9].[0-9].[0-9]*' + +env: + CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} + CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: info + CONAN_NON_INTERACTIVE: 1 + +jobs: + conan-recipe-version: + uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + with: + project_name: curaengine + + gcodeanalyzer: + needs: [ conan-recipe-version ] + name: Run GCodeAnalyzer on the engine + runs-on: ubuntu-22.04 + steps: + - name: Checkout CuraEngine + uses: actions/checkout@v3 + with: + path: 'CuraEngine' + + - name: Checkout GCodeAnalyzer + uses: actions/checkout@v3 + with: + ref: 'main' + path: 'GCodeAnalyzer' + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout Test Models + uses: actions/checkout@v3 + with: + repository: 'Ultimaker/NightlyTestModels' + ref: 'main' + path: 'NightlyTestModels' + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout .def.json files from Cura frontend + run: | + mkdir Cura + cd Cura + git init + git sparse-checkout set resources/definitions resources/extruders + git remote add -f origin https://github.com/Ultimaker/Cura + git pull origin main + cd .. + + - name: Setup Python and pip + uses: actions/setup-python@v4 + with: + python-version: '3.11.x' + architecture: 'x64' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt + + - name: Cache Benchmark library + uses: actions/cache@v1 + with: + path: ./cache + key: ${{ runner.os }}-gcodeanalyzer + + - name: Install Python requirements and Create default Conan profile + run: | + pip install -r .github/workflows/requirements-conan-package.txt + pip install wheel numpy pandas python-dateutil pytz six + pip install git+https://github.com/ultimaker/libcharon@CURA-9495_analyzer_requisites#egg=charon + pip install pytest pytest-benchmark + + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + + - name: Install GCC-12 on ubuntu-22.04 + run: | + sudo apt install g++-12 gcc-12 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + + - name: Get Conan configuration + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan profile new default --detect + + - name: Use Conan download cache (Bash) + run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + + - name: Install dependencies + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=False -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv + working-directory: CuraEngine + + - name: Upload the Dependency package(s) + run: conan upload "*" -r cura --all -c + + - name: Set Environment variables from Conan install (bash) + if: ${{ runner.os != 'Windows' }} + run: | + . ./activate_github_actions_runenv.sh + . ./activate_github_actions_buildenv.sh + working-directory: CuraEngine/build/Release/generators + + - name: Build CuraEngine and tests + run: | + cmake --preset release + cmake --build --preset release + working-directory: CuraEngine + + - name: Collect STL-files, run CuraEngine, output GCode-files + run: | + export CURA_ENGINE_SEARCH_PATH=./Cura/resources/definitions:./Cura/resources/extruders + for file in `ls ../NightlyTestModels/*.stl`; + do + ./build/Release/CuraEngine slice --force-read-parent --force-read-nondefault -v -p -j ../Cura/resources/definitions/ultimaker_s3.def.json -l $file -o ../`basename $file .stl`.gcode + done + working-directory: CuraEngine + + - name: Run GCodeAnalyzer on generated GCode files + id: gcode_out + run: | + import sys + import os + import json + from Charon.filetypes.GCodeFile import GCodeFile + + sys.path.append(".") + import GCodeAnalyzer + GCodeFile.SkipHeaderValidation = True + + folder_path = ".." + jzon = [] + for filename in os.listdir(folder_path): + basename = os.path.basename(filename) + _base, ext = os.path.splitext(basename) + if ext.lower() != ".gcode": + continue + infilename = os.path.join(folder_path, filename) + + frame = GCodeAnalyzer.DataFrame(infilename) + + line_lengths = frame.gc.extrusions['length'].describe() + all_lengths = frame.gc.travels['length'].describe() + extrusion_values = frame.gc.extrusions['E'].describe() + temperatures = frame['T_0_nozzle'].describe() + print_time = frame.time.max() + no_retractions = len(frame.gc.retractions) + total_travel_length = frame.gc.travels.length.sum() + + # microsegments violations + queue = 16 + seg_sec = 80 + violation_threshold = queue / seg_sec + microsegments_wall_skin = frame.gc.extrusions.duration[(frame.type == 'WALL-OUTER') | (frame.type == 'WALL-INNER') | (frame.type == 'SKIN')].rolling(queue).sum() + no_violations_wall_skin = microsegments_wall_skin[microsegments_wall_skin < violation_threshold].count() + + microsegments_infill = frame.gc.extrusions.duration[frame.type == 'FILL'].rolling(queue).sum() + no_violations_infill = microsegments_infill[microsegments_infill < violation_threshold].count() + + jzon += [ + { + "name": f"Print time {basename}", + "unit": "s", + "value": print_time, + }, + { + "name": f"Microsegment violations in wall-skin {basename}", + "unit": "-", + "value": int(no_violations_wall_skin), + }, + { + "name": f"Microsegment violations in infill {basename}", + "unit": "-", + "value": int(no_violations_infill), + }, + { + "name": f"Number of retractions {basename}", + "unit": "-", + "value": no_retractions, + }, + { + "name": f"Total travel length {basename}", + "unit": "mm", + "value": total_travel_length, + }, + { + "name": f"Minimum Line Length {basename}", + "unit": "mm", + "value": line_lengths["min"], + }, + { + "name": f"Line Lengths 25 Percentile {basename}", + "unit": "mm", + "value": line_lengths["25%"], + }, + { + "name": f"Minimum All Distances {basename}", + "unit": "mm", + "value": all_lengths["min"], + }, + { + "name": f"All Distances 25 Percentile {basename}", + "unit": "mm", + "value": all_lengths["25%"], + }, + { + "name": f"Extrusion-Axis {basename}", + "unit": "mm", + "value": extrusion_values["min"], + }, + { + "name": f"Extrusion Lengths 25 Percentile {basename}", + "unit": "mm", + "value": extrusion_values["25%"], + }, + { + "name": f"Number Of Temperature Commands {basename}", + "unit": "#", + "value": temperatures["count"], + }, + { + "name": f"Mean Temperature {basename}", + "unit": "Celcius", + "value": temperatures["50%"], + }, + ] + + with open("../output.json", "w") as outfile: + outfile.write(json.dumps(jzon)) + shell: python + working-directory: GCodeAnalyzer + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + name: CGcodeAnalyzer + external-data-json-path: ./cache/benchmark-data.json + output-file-path: output.json + benchmark-data-dir-path: dev/gcodeanalyzer + tool: customBiggerIsBetter + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true + summary-always: true + gh-repository: github.com/Ultimaker/CuraEngine + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '10%' + comment-on-alert: true + fail-on-alert: true + alert-comment-cc-users: '@jellespijker' From d33726b8432e36c7385b92a9c59b7fad97826466 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 9 Aug 2023 23:08:28 +0200 Subject: [PATCH 342/656] Fix unit tests CURA-10811 --- tests/WallsComputationTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/WallsComputationTest.cpp b/tests/WallsComputationTest.cpp index 90c9748204..a45cb5464a 100644 --- a/tests/WallsComputationTest.cpp +++ b/tests/WallsComputationTest.cpp @@ -73,6 +73,7 @@ class WallsComputationTest : public testing::Test settings.add("magic_spiralize", "false"); settings.add("meshfix_maximum_deviation", "0.1"); settings.add("meshfix_maximum_extrusion_area_deviation", "0.01"); + settings.add("meshfix_fluid_motion_enabled", "false"); settings.add("meshfix_maximum_resolution", "0.01"); settings.add("min_wall_line_width", "0.3"); settings.add("min_bead_width", "0"); From 4310b67d3764a2f47ec24c63e83593c5c340bf5f Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:09:14 +0200 Subject: [PATCH 343/656] Also run on PR's Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index b3f217f0e9..66883c1be7 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -13,9 +13,29 @@ on: - '.github/workflows/requirements-conan-package.txt' branches: - main - - gcodeanalyzer + - '[0-9]+.[0-9]+' + - 'CURA-*' tags: - '[0-9].[0-9].[0-9]*' + pull_request: + types: [ opened, reopened, synchronize ] + paths: + - 'include/**' + - 'src/**' + - 'cmake/**' + - 'tests/**' + - 'test_package/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - '.github/workflows/unit-test.yml' + - '.github/workflows/requirements-conan-package.txt' + branches: + - main + - 'CURA-*' + - '[0-9]+.[0-9]+' + tags: + - '[0-9]+.[0-9]+.[0-9]+' env: CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} From 6b78984fd314d52d8e950748c6e3725bcfbd45f1 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:11:59 +0200 Subject: [PATCH 344/656] Also run on changes to the workflow Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 66883c1be7..59d7e1d842 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -9,7 +9,7 @@ on: - 'conanfile.py' - 'conandata.yml' - 'CMakeLists.txt' - - '.github/workflows/benchmark.yml' + - '.github/workflows/gcodeanalyzer.yml' - '.github/workflows/requirements-conan-package.txt' branches: - main @@ -28,7 +28,7 @@ on: - 'conanfile.py' - 'conandata.yml' - 'CMakeLists.txt' - - '.github/workflows/unit-test.yml' + - '.github/workflows/gcodeanalyzer.yml' - '.github/workflows/requirements-conan-package.txt' branches: - main From 7a776ce9c91a958a4b2663ed713403a2df3538b8 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:14:38 +0200 Subject: [PATCH 345/656] Use python 3.10 Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 59d7e1d842..e12f3fa2e8 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -90,7 +90,7 @@ jobs: - name: Setup Python and pip uses: actions/setup-python@v4 with: - python-version: '3.11.x' + python-version: '3.10.x' architecture: 'x64' cache: 'pip' cache-dependency-path: .github/workflows/requirements-conan-package.txt From cfec456ecb01acb5aa77810ec2210dc1005369e4 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:17:15 +0200 Subject: [PATCH 346/656] Only run on pushes to main Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index e12f3fa2e8..1b5ce36af9 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -13,10 +13,6 @@ on: - '.github/workflows/requirements-conan-package.txt' branches: - main - - '[0-9]+.[0-9]+' - - 'CURA-*' - tags: - - '[0-9].[0-9].[0-9]*' pull_request: types: [ opened, reopened, synchronize ] paths: From 2f89482777d216a8027daa8b8409a47cdfbcddd4 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:19:22 +0200 Subject: [PATCH 347/656] Use correct workflow path Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 1b5ce36af9..e1316e4342 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -89,7 +89,7 @@ jobs: python-version: '3.10.x' architecture: 'x64' cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt + cache-dependency-path: CuraEngine/.github/workflows/requirements-conan-package.txt - name: Cache Benchmark library uses: actions/cache@v1 From 3a6b145dd76cb6445dd815973cc3fbac3589476c Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:21:09 +0200 Subject: [PATCH 348/656] Use correct workflow path Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index e1316e4342..cc5f2348fc 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -99,7 +99,7 @@ jobs: - name: Install Python requirements and Create default Conan profile run: | - pip install -r .github/workflows/requirements-conan-package.txt + pip install -r CuraEngine/.github/workflows/requirements-conan-package.txt pip install wheel numpy pandas python-dateutil pytz six pip install git+https://github.com/ultimaker/libcharon@CURA-9495_analyzer_requisites#egg=charon pip install pytest pytest-benchmark From 057196d4207c34f0ba0bbf77b8c55ea4f476cd60 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:37:29 +0200 Subject: [PATCH 349/656] Use correct def dir Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index cc5f2348fc..681e0a14b7 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -152,7 +152,7 @@ jobs: - name: Collect STL-files, run CuraEngine, output GCode-files run: | - export CURA_ENGINE_SEARCH_PATH=./Cura/resources/definitions:./Cura/resources/extruders + export CURA_ENGINE_SEARCH_PATH=../Cura/resources/definitions:../Cura/resources/extruders for file in `ls ../NightlyTestModels/*.stl`; do ./build/Release/CuraEngine slice --force-read-parent --force-read-nondefault -v -p -j ../Cura/resources/definitions/ultimaker_s3.def.json -l $file -o ../`basename $file .stl`.gcode From e1e0a6305a6626c497d62554cfbf335d55343d9f Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:52:30 +0200 Subject: [PATCH 350/656] Use correct repo for gcodeanalyzer Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 681e0a14b7..d336c2453e 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -61,6 +61,7 @@ jobs: - name: Checkout GCodeAnalyzer uses: actions/checkout@v3 with: + repository: 'Ultimaker/gcodeanalyzer' ref: 'main' path: 'GCodeAnalyzer' token: ${{ secrets.GITHUB_TOKEN }} From 4b01780ae9d9053956901db3a4185615c5c27381 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 9 Aug 2023 23:56:35 +0200 Subject: [PATCH 351/656] Use correct repo for GCodeAnalyzer Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index d336c2453e..55d6d011e2 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -61,7 +61,7 @@ jobs: - name: Checkout GCodeAnalyzer uses: actions/checkout@v3 with: - repository: 'Ultimaker/gcodeanalyzer' + repository: 'Ultimaker/GCodeAnalyzer' ref: 'main' path: 'GCodeAnalyzer' token: ${{ secrets.GITHUB_TOKEN }} From a0ab6cd12acb6c1a63ec0ff6f58cb6e7cbee34bc Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 00:07:59 +0200 Subject: [PATCH 352/656] Use PAT token for GCodeAnalyzer Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 55d6d011e2..932df9e52f 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -64,7 +64,7 @@ jobs: repository: 'Ultimaker/GCodeAnalyzer' ref: 'main' path: 'GCodeAnalyzer' - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GCODEANALYZER_PAT }} - name: Checkout Test Models uses: actions/checkout@v3 From 11f98dac5ac153881cc21c81809212e9d882ad9a Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 00:26:10 +0200 Subject: [PATCH 353/656] no need for external file we have the pages Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 932df9e52f..510203de87 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -92,12 +92,6 @@ jobs: cache: 'pip' cache-dependency-path: CuraEngine/.github/workflows/requirements-conan-package.txt - - name: Cache Benchmark library - uses: actions/cache@v1 - with: - path: ./cache - key: ${{ runner.os }}-gcodeanalyzer - - name: Install Python requirements and Create default Conan profile run: | pip install -r CuraEngine/.github/workflows/requirements-conan-package.txt @@ -278,7 +272,6 @@ jobs: uses: benchmark-action/github-action-benchmark@v1 with: name: CGcodeAnalyzer - external-data-json-path: ./cache/benchmark-data.json output-file-path: output.json benchmark-data-dir-path: dev/gcodeanalyzer tool: customBiggerIsBetter From 0b5a7cdd11b07262e1433b5b8f9990bc3aa004b1 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 00:58:15 +0200 Subject: [PATCH 354/656] try fixing weird crash Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 510203de87..20280a4fd8 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -4,11 +4,6 @@ on: paths: - 'include/**' - 'src/**' - - 'cmake/**' - - 'benchmark/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - '.github/workflows/gcodeanalyzer.yml' - '.github/workflows/requirements-conan-package.txt' branches: @@ -18,12 +13,6 @@ on: paths: - 'include/**' - 'src/**' - - 'cmake/**' - - 'tests/**' - - 'test_package/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - '.github/workflows/gcodeanalyzer.yml' - '.github/workflows/requirements-conan-package.txt' branches: @@ -277,10 +266,8 @@ jobs: tool: customBiggerIsBetter github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true - summary-always: true gh-repository: github.com/Ultimaker/CuraEngine # Show alert with commit comment on detecting possible performance regression alert-threshold: '10%' comment-on-alert: true fail-on-alert: true - alert-comment-cc-users: '@jellespijker' From b7ed5779412cb3f61a05e5aa61beddc24dce4744 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 01:18:27 +0200 Subject: [PATCH 355/656] comment always Contributes to CURA-10925 --- .github/workflows/gcodeanalyzer.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 20280a4fd8..f6aac7bcc5 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -268,6 +268,8 @@ jobs: auto-push: true gh-repository: github.com/Ultimaker/CuraEngine # Show alert with commit comment on detecting possible performance regression - alert-threshold: '10%' + alert-threshold: '110%' + summary-always: true + comment-always: true comment-on-alert: true fail-on-alert: true From d3bfc436be8f0f23ec959e3095a020ae3669539c Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 01:42:51 +0200 Subject: [PATCH 356/656] Update the workflows --- .github/workflows/benchmark.yml | 28 +++++--- .github/workflows/gcodeanalyzer.yml | 3 +- .github/workflows/lint-formatter.yml | 80 +++++++++++----------- .github/workflows/lint-poster.yml | 4 +- .github/workflows/lint-tidier.yml | 3 +- .github/workflows/process-pull-request.yml | 20 +++--- .github/workflows/unit-test.yml | 5 -- 7 files changed, 72 insertions(+), 71 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index e650eaf0fb..fe2592fad8 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,25 +1,30 @@ name: Benchmark on: - schedule: - - cron: '15 6,12,17 * * *' - - # FIXME: remove the path trigger after merge push: paths: - 'include/**' - 'src/**' - - 'cmake/**' - 'benchmark/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - '.github/workflows/benchmark.yml' - '.github/workflows/requirements-conan-package.txt' branches: - main - - CURA-9906_benchmark tags: - '[0-9].[0-9].[0-9]*' + pull_request: + types: [ opened, reopened, synchronize ] + paths: + - 'include/**' + - 'src/**' + - 'benchmark/**' + - '.github/workflows/benchmark.yml' + - '.github/workflows/requirements-conan-package.txt' + branches: + - main + - 'CURA-*' + - '[0-9]+.[0-9]+' + tags: + - '[0-9]+.[0-9]+.[0-9]+' env: CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} @@ -130,6 +135,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true # Show alert with commit comment on detecting possible performance regression - alert-threshold: '200%' + alert-threshold: '175%' + summary-always: true comment-on-alert: true - fail-on-alert: true + max-items-in-chart: 250 diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index f6aac7bcc5..c16974c5fa 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -270,6 +270,5 @@ jobs: # Show alert with commit comment on detecting possible performance regression alert-threshold: '110%' summary-always: true - comment-always: true comment-on-alert: true - fail-on-alert: true + max-items-in-chart: 250 \ No newline at end of file diff --git a/.github/workflows/lint-formatter.yml b/.github/workflows/lint-formatter.yml index 9420c78849..9ae18563b5 100644 --- a/.github/workflows/lint-formatter.yml +++ b/.github/workflows/lint-formatter.yml @@ -1,46 +1,46 @@ name: lint-formatter on: - workflow_dispatch: + workflow_dispatch: - push: - paths: - - 'include/**/*.h*' - - 'src/**/*.c*' + push: + paths: + - 'include/**/*.h*' + - 'src/**/*.c*' jobs: - lint-formatter-job: - name: Auto-apply clang-format - - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - uses: technote-space/get-diff-action@v6 - with: - PATTERNS: | - include/**/*.h* - src/**/*.c* - - - name: Setup Python and pip - if: env.GIT_DIFF && !env.MATCHED_FILES # If nothing happens with python and/or pip after, the clean-up crashes. - uses: actions/setup-python@v4 - with: - python-version: 3.11.x - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-linter.txt - - - name: Install Python requirements for runner - if: env.GIT_DIFF && !env.MATCHED_FILES - run: pip install -r .github/workflows/requirements-linter.txt - - - name: Format file - if: env.GIT_DIFF && !env.MATCHED_FILES - run: | - clang-format -i ${{ env.GIT_DIFF_FILTERED }} - - - uses: stefanzweifel/git-auto-commit-action@v4 - if: env.GIT_DIFF && !env.MATCHED_FILES - with: - commit_message: "Applied clang-format." + lint-formatter-job: + name: Auto-apply clang-format + + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - uses: technote-space/get-diff-action@v6 + with: + PATTERNS: | + include/**/*.h* + src/**/*.c* + + - name: Setup Python and pip + if: env.GIT_DIFF && !env.MATCHED_FILES # If nothing happens with python and/or pip after, the clean-up crashes. + uses: actions/setup-python@v4 + with: + python-version: 3.11.x + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-linter.txt + + - name: Install Python requirements for runner + if: env.GIT_DIFF && !env.MATCHED_FILES + run: pip install -r .github/workflows/requirements-linter.txt + + - name: Format file + if: env.GIT_DIFF && !env.MATCHED_FILES + run: | + clang-format -i ${{ env.GIT_DIFF_FILTERED }} + + - uses: stefanzweifel/git-auto-commit-action@v4 + if: env.GIT_DIFF && !env.MATCHED_FILES + with: + commit_message: "Applied clang-format." diff --git a/.github/workflows/lint-poster.yml b/.github/workflows/lint-poster.yml index 87ef49271c..a016599fec 100644 --- a/.github/workflows/lint-poster.yml +++ b/.github/workflows/lint-poster.yml @@ -2,8 +2,8 @@ name: lint-poster on: workflow_run: - workflows: ["lint-tidier"] - types: [completed] + workflows: [ "lint-tidier" ] + types: [ completed ] jobs: lint-poster-job: diff --git a/.github/workflows/lint-tidier.yml b/.github/workflows/lint-tidier.yml index 4c13db4c35..c28009b384 100644 --- a/.github/workflows/lint-tidier.yml +++ b/.github/workflows/lint-tidier.yml @@ -96,8 +96,9 @@ jobs: path: linter-result/ - name: Run clang-tidy-pr-comments action - uses: platisd/clang-tidy-pr-comments@bc0bb7da034a8317d54e7fe1e819159002f4cc40 + uses: platisd/clang-tidy-pr-comments@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} clang_tidy_fixes: linter-result/fixes.yml request_changes: true + suggestions_per_comment: 30 diff --git a/.github/workflows/process-pull-request.yml b/.github/workflows/process-pull-request.yml index 56fb015b9d..f4a5921214 100644 --- a/.github/workflows/process-pull-request.yml +++ b/.github/workflows/process-pull-request.yml @@ -1,15 +1,15 @@ name: process-pull-request on: - pull_request_target: - types: [opened, reopened, edited, synchronize, review_requested, ready_for_review, assigned] + pull_request_target: + types: [ opened, reopened, edited, synchronize, review_requested, ready_for_review, assigned ] jobs: - add_label: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-ecosystem/action-add-labels@v1 - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - with: - labels: 'PR: Community Contribution :crown:' + add_label: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-ecosystem/action-add-labels@v1 + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + with: + labels: 'PR: Community Contribution :crown:' diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 866e651210..9d8b9a0ed6 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -1,7 +1,5 @@ --- name: unit-test -# FIXME: This should be a reusable workflow - on: push: paths: @@ -11,13 +9,11 @@ on: - 'tests/**' - 'test_package/**' - 'conanfile.py' - - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/unit-test.yml' - '.github/workflows/requirements-conan-package.txt' branches: - main - - 'CURA-*' - '[0-9]+.[0-9]+' tags: - '[0-9]+.[0-9]+.[0-9]+' @@ -30,7 +26,6 @@ on: - 'tests/**' - 'test_package/**' - 'conanfile.py' - - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/unit-test.yml' - '.github/workflows/requirements-conan-package.txt' From 19898e71a3838f2f58a7445e2ec16ba5563c7d12 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 01:53:00 +0200 Subject: [PATCH 357/656] Do not run on forks --- .github/workflows/gcodeanalyzer.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index c16974c5fa..7d2bf394d4 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -32,7 +32,24 @@ env: CONAN_NON_INTERACTIVE: 1 jobs: + check_actor: + runs-on: ubuntu-latest + outputs: + proceed: ${{ steps.skip_check.outputs.proceed }} + steps: + - id: skip_check + run: | + if [[ "${{ github.actor }}" == *"[bot]"* ]]; then + echo "::set-output name=proceed::true" + elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then + echo "::set-output name=proceed::true" + else + echo "::set-output name=proceed::false" + fi + shell: bash + conan-recipe-version: + if: ${{ needs.check_actor.outputs.proceed == 'true' }} uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main with: project_name: curaengine From 6fca56c4f414b845ba5826fb7d215123b07ed11d Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 01:57:45 +0200 Subject: [PATCH 358/656] use environments --- .github/workflows/gcodeanalyzer.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 7d2bf394d4..6751f6717c 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -40,15 +40,16 @@ jobs: - id: skip_check run: | if [[ "${{ github.actor }}" == *"[bot]"* ]]; then - echo "::set-output name=proceed::true" + echo "proceed=true" >> $GITHUB_OUTPUT elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then - echo "::set-output name=proceed::true" + echo "proceed=true" >> $GITHUB_OUTPUT else - echo "::set-output name=proceed::false" + echo "proceed=false" >> $GITHUB_OUTPUT fi shell: bash conan-recipe-version: + needs: [ check_actor ] if: ${{ needs.check_actor.outputs.proceed == 'true' }} uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main with: From ac9e6988229c71ca7e33631e4134d11b33dcdba7 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 02:01:18 +0200 Subject: [PATCH 359/656] run when pushed to main --- .github/workflows/gcodeanalyzer.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 6751f6717c..2428e19697 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -41,6 +41,8 @@ jobs: run: | if [[ "${{ github.actor }}" == *"[bot]"* ]]; then echo "proceed=true" >> $GITHUB_OUTPUT + elif [[ "${{ github.event.pull_request }}" == "" ]]; then + echo "proceed=true" >> $GITHUB_OUTPUT elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then echo "proceed=true" >> $GITHUB_OUTPUT else From c795237e4c45cc3160c8532d7bbc3565c80b2c56 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 02:02:54 +0200 Subject: [PATCH 360/656] run when pushed to main --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 2428e19697..b078d44583 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -41,7 +41,7 @@ jobs: run: | if [[ "${{ github.actor }}" == *"[bot]"* ]]; then echo "proceed=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.pull_request }}" == "" ]]; then + elif [[ "${{ github.event.pull_request }}" == "" ]]; then echo "proceed=true" >> $GITHUB_OUTPUT elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then echo "proceed=true" >> $GITHUB_OUTPUT From 5f3aac269776de09d0ecf5582ed8581bfdb4751f Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 02:05:41 +0200 Subject: [PATCH 361/656] don't run on forks --- .github/workflows/benchmark.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index fe2592fad8..801fef53ee 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -36,7 +36,27 @@ env: CONAN_NON_INTERACTIVE: 1 jobs: + check_actor: + runs-on: ubuntu-latest + outputs: + proceed: ${{ steps.skip_check.outputs.proceed }} + steps: + - id: skip_check + run: | + if [[ "${{ github.actor }}" == *"[bot]"* ]]; then + echo "proceed=true" >> $GITHUB_OUTPUT + elif [[ "${{ github.event.pull_request }}" == "" ]]; then + echo "proceed=true" >> $GITHUB_OUTPUT + elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then + echo "proceed=true" >> $GITHUB_OUTPUT + else + echo "proceed=false" >> $GITHUB_OUTPUT + fi + shell: bash + conan-recipe-version: + needs: [ check_actor ] + if: ${{ needs.check_actor.outputs.proceed == 'true' }} uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main with: project_name: curaengine From 7c8f83eec7c0f1c5e19ffa714508a64c30879be7 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 02:14:41 +0200 Subject: [PATCH 362/656] add quotes --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 801fef53ee..b202a0a029 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -143,7 +143,7 @@ jobs: - name: Run benchmark CuraEngine id: run-test - run: ./benchmarks --benchmark_format=json --benchmark_context=version=`${{ needs.conan-recipe-version.outputs.recipe_semver_full }}` | tee benchmark_result.json + run: ./benchmarks --benchmark_format=json --benchmark_context=version=`"${{ needs.conan-recipe-version.outputs.recipe_semver_full }}"` | tee benchmark_result.json working-directory: build/Release/benchmark - name: Store benchmark result From 68629a1095b191174628cfb998bf68f86cbe8756 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 02:18:35 +0200 Subject: [PATCH 363/656] pin version 1.4.0 which is the latest --- .github/workflows/lint-tidier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-tidier.yml b/.github/workflows/lint-tidier.yml index c28009b384..2504eeda4a 100644 --- a/.github/workflows/lint-tidier.yml +++ b/.github/workflows/lint-tidier.yml @@ -96,7 +96,7 @@ jobs: path: linter-result/ - name: Run clang-tidy-pr-comments action - uses: platisd/clang-tidy-pr-comments@v1 + uses: platisd/clang-tidy-pr-comments@1.4.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} clang_tidy_fixes: linter-result/fixes.yml From c8622c0931f302510ef48e765f1a50d6cf8786da Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 02:58:56 +0200 Subject: [PATCH 364/656] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- include/utils/actions/smooth.h | 4 ++-- src/WallToolPaths.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 47ccaa6ebd..559704a173 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -29,7 +29,7 @@ namespace cura { class SmoothTest_TestSmooth_Test; -} +} // namespace cura namespace cura::actions { @@ -38,7 +38,7 @@ struct smooth_fn { friend class cura::SmoothTest_TestSmooth_Test; - const auto operator()(const Settings& settings) const + auto operator()(const Settings& settings) const { const auto fluid_motion_shift_distance = settings.get("meshfix_fluid_motion_shift_distance"); const auto fluid_motion_small_distance = settings.get("meshfix_fluid_motion_small_distance"); diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index f7c9f58176..c46d4afbf5 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -82,7 +82,7 @@ const std::vector& WallToolPaths::generate() const coord_t open_close_distance = settings.get("fill_outline_gaps") ? settings.get("min_feature_size") / 2 - 5 : settings.get("min_wall_line_width") / 2 - 5; const coord_t epsilon_offset = (allowed_distance / 2) - 1; - const AngleRadians transitioning_angle = settings.get("wall_transition_angle"); + const auto transitioning_angle = settings.get("wall_transition_angle"); constexpr coord_t discretization_step_size = MM2INT(0.8); // Simplify outline for boost::voronoi consumption. Absolutely no self intersections or near-self intersections allowed: @@ -143,8 +143,8 @@ const std::vector& WallToolPaths::generate() max_bead_count, wall_0_inset, wall_distribution_count); - const coord_t transition_filter_dist = settings.get("wall_transition_filter_distance"); - const coord_t allowed_filter_deviation = settings.get("wall_transition_filter_deviation"); + const auto transition_filter_dist = settings.get("wall_transition_filter_distance"); + const auto allowed_filter_deviation = settings.get("wall_transition_filter_deviation"); SkeletalTrapezoidation wall_maker( prepared_outline, *beading_strat, From acd729980d3be2b10cfe8ac20291f16f13818ea0 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 02:42:43 +0200 Subject: [PATCH 365/656] don't set extra context --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b202a0a029..40305203f5 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -143,7 +143,7 @@ jobs: - name: Run benchmark CuraEngine id: run-test - run: ./benchmarks --benchmark_format=json --benchmark_context=version=`"${{ needs.conan-recipe-version.outputs.recipe_semver_full }}"` | tee benchmark_result.json + run: ./benchmarks --benchmark_format=json | tee benchmark_result.json working-directory: build/Release/benchmark - name: Store benchmark result From bca29af988dbe27aefb196962594542adbd37d9a Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 06:03:45 +0200 Subject: [PATCH 366/656] Update gcodeanalyzer.yml to fetch specific branch Updated the 'gcodeanalyzer.yml' workflow to fetch files from the same branch in Cura repository if it exists. Previously, the script always pulled from the 'main' branch. The script now checks if the branch exists in the remote repository as well before pulling the '.def.json' files. This change allows for better synchronization between the branches of different repositories. --- .github/workflows/gcodeanalyzer.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index b078d44583..f94263d739 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -85,12 +85,20 @@ jobs: - name: Checkout .def.json files from Cura frontend run: | + cd CuraEngine + branch_name=$(git rev-parse --abbrev-ref HEAD) + exists=$(git show-ref --quiet "refs/remotes/origin/${branch_name}") + cd .. mkdir Cura cd Cura git init git sparse-checkout set resources/definitions resources/extruders git remote add -f origin https://github.com/Ultimaker/Cura - git pull origin main + if [ ${exists} ]; then + git pull origin ${branch_name} + else + git pull origin main + fi cd .. - name: Setup Python and pip From e6969a32b1e1aebd28ec4d314571dd0389938e7e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 06:17:23 +0200 Subject: [PATCH 367/656] Update branch existence check in workflow Replaced git show-ref command with a curl command to check the existence of a branch in the Cura repository in the gcodeanalyzer workflow. This change ensures a more consistent and reliable way of branch checking as it communicates directly with the GitHub API --- .github/workflows/gcodeanalyzer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index f94263d739..67b80f529f 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -87,14 +87,14 @@ jobs: run: | cd CuraEngine branch_name=$(git rev-parse --abbrev-ref HEAD) - exists=$(git show-ref --quiet "refs/remotes/origin/${branch_name}") + exists=$(curl -s -o /dev/null -w "%{http_code}" https://api.github.com/repos/ultimaker/cura/branches/${branch_name}) cd .. mkdir Cura cd Cura git init git sparse-checkout set resources/definitions resources/extruders git remote add -f origin https://github.com/Ultimaker/Cura - if [ ${exists} ]; then + if [ "$exists" == "200" ]; then git pull origin ${branch_name} else git pull origin main From 114eef3752649d7be19e94299d647361ffab75ea Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 06:26:57 +0200 Subject: [PATCH 368/656] Update branch existence check in gcodeanalyzer workflow Added quotes around the curl URL for better error handling and added echo statements for better debugging by specifying whether 'branch_name' or 'main' is getting used. Quoting the curl URL helps prevent potential issues with special characters in branch names. The echo statements help in understanding workflow logs by indicating which branch is being used. --- .github/workflows/gcodeanalyzer.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 67b80f529f..ccf95b321a 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -87,7 +87,7 @@ jobs: run: | cd CuraEngine branch_name=$(git rev-parse --abbrev-ref HEAD) - exists=$(curl -s -o /dev/null -w "%{http_code}" https://api.github.com/repos/ultimaker/cura/branches/${branch_name}) + exists=$(curl -s -o /dev/null -w "%{http_code}" "https://api.github.com/repos/ultimaker/cura/branches/${branch_name}") cd .. mkdir Cura cd Cura @@ -95,8 +95,10 @@ jobs: git sparse-checkout set resources/definitions resources/extruders git remote add -f origin https://github.com/Ultimaker/Cura if [ "$exists" == "200" ]; then + echo "Using '${branch_name}' for Cura" >> $GITHUB_STEP_SUMMARY git pull origin ${branch_name} else + echo "Using 'main' for Cura" >> $GITHUB_STEP_SUMMARY git pull origin main fi cd .. From 48ff07d943462ca7583b300b531e2e83925b7da4 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 04:36:01 +0000 Subject: [PATCH 369/656] Applied clang-format. --- include/utils/IntPoint.h | 151 +++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/include/utils/IntPoint.h b/include/utils/IntPoint.h index c4cc098ba9..ce5cbc9785 100644 --- a/include/utils/IntPoint.h +++ b/include/utils/IntPoint.h @@ -1,5 +1,5 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2020 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef UTILS_INT_POINT_H #define UTILS_INT_POINT_H @@ -10,21 +10,19 @@ Integer points are used to avoid floating point rounding errors, and because Cli */ #define INLINE static inline -//Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition. -#include +// Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition. +#include "../utils/math.h" // for M_PI. Use relative path to avoid pulling +#include "Point3.h" //For applying Point3Matrices. + #include #include // for hash function object #include // auto-serialization / auto-toString() #include +#include #include -#include "Point3.h" //For applying Point3Matrices. - - -#include "../utils/math.h" // for M_PI. Use relative path to avoid pulling - #ifdef __GNUC__ -#define DEPRECATED(func) func __attribute__ ((deprecated)) +#define DEPRECATED(func) func __attribute__((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED(func) __declspec(deprecated) func #else @@ -45,22 +43,62 @@ typedef ClipperLib::IntPoint Point; static Point no_point(std::numeric_limits::min(), std::numeric_limits::min()); /* Extra operators to make it easier to do math with the 64bit Point objects */ -INLINE Point operator-(const Point& p0) { return Point(-p0.X, -p0.Y); } -INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); } -INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.X, p0.Y-p1.Y); } -INLINE Point operator*(const Point& p0, const coord_t i) { return Point(p0.X * i, p0.Y * i); } -template::value, T>::type> //Use only for numeric types. -INLINE Point operator*(const Point& p0, const T i) { return Point(std::llrint(p0.X * i), std::llrint(p0.Y * i)); } -template::value, T>::type> //Use only for numeric types. -INLINE Point operator*(const T i, const Point& p0) { return p0 * i; } -INLINE Point operator/(const Point& p0, const coord_t i) { return Point(p0.X/i, p0.Y/i); } -INLINE Point operator/(const Point& p0, const Point& p1) { return Point(p0.X/p1.X, p0.Y/p1.Y); } -INLINE Point operator%(const Point& p0, const coord_t i) { return Point(p0.X%i, p0.Y%i); } - -INLINE Point& operator += (Point& p0, const Point& p1) { p0.X += p1.X; p0.Y += p1.Y; return p0; } -INLINE Point& operator -= (Point& p0, const Point& p1) { p0.X -= p1.X; p0.Y -= p1.Y; return p0; } - -INLINE bool operator < (const Point& p0, const Point& p1) { return p0.X < p1.X || (p0.X == p1.X && p0.Y < p1.Y); } +INLINE Point operator-(const Point& p0) +{ + return Point(-p0.X, -p0.Y); +} +INLINE Point operator+(const Point& p0, const Point& p1) +{ + return Point(p0.X + p1.X, p0.Y + p1.Y); +} +INLINE Point operator-(const Point& p0, const Point& p1) +{ + return Point(p0.X - p1.X, p0.Y - p1.Y); +} +INLINE Point operator*(const Point& p0, const coord_t i) +{ + return Point(p0.X * i, p0.Y * i); +} +template::value, T>::type> // Use only for numeric types. +INLINE Point operator*(const Point& p0, const T i) +{ + return Point(std::llrint(p0.X * i), std::llrint(p0.Y * i)); +} +template::value, T>::type> // Use only for numeric types. +INLINE Point operator*(const T i, const Point& p0) +{ + return p0 * i; +} +INLINE Point operator/(const Point& p0, const coord_t i) +{ + return Point(p0.X / i, p0.Y / i); +} +INLINE Point operator/(const Point& p0, const Point& p1) +{ + return Point(p0.X / p1.X, p0.Y / p1.Y); +} +INLINE Point operator%(const Point& p0, const coord_t i) +{ + return Point(p0.X % i, p0.Y % i); +} + +INLINE Point& operator+=(Point& p0, const Point& p1) +{ + p0.X += p1.X; + p0.Y += p1.Y; + return p0; +} +INLINE Point& operator-=(Point& p0, const Point& p1) +{ + p0.X -= p1.X; + p0.Y -= p1.Y; + return p0; +} + +INLINE bool operator<(const Point& p0, const Point& p1) +{ + return p0.X < p1.X || (p0.X == p1.X && p0.Y < p1.Y); +} /* ***** NOTE ***** TL;DR: DO NOT implement operators *= and /= because of the default values in ClipperLib::IntPoint's constructor. @@ -71,16 +109,16 @@ INLINE bool operator < (const Point& p0, const Point& p1) { return p0.X < p1.X | an IntPoint(5, y = 0) and you end up with wrong results. */ -//INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; } -//INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; } +// INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; } +// INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; } INLINE coord_t vSize2(const Point& p0) { - return p0.X*p0.X+p0.Y*p0.Y; + return p0.X * p0.X + p0.Y * p0.Y; } INLINE float vSize2f(const Point& p0) { - return static_cast(p0.X)*static_cast(p0.X)+static_cast(p0.Y)*static_cast(p0.Y); + return static_cast(p0.X) * static_cast(p0.X) + static_cast(p0.Y) * static_cast(p0.Y); } INLINE bool shorterThen(const Point& p0, const coord_t len) @@ -110,7 +148,7 @@ INLINE double vSizeMM(const Point& p0) { double fx = INT2MM(p0.X); double fy = INT2MM(p0.Y); - return sqrt(fx*fx+fy*fy); + return sqrt(fx * fx + fy * fy); } INLINE Point normal(const Point& p0, coord_t len) @@ -146,7 +184,8 @@ INLINE coord_t cross(const Point& p0, const Point& p1) INLINE int angle(const Point& p) { double angle = std::atan2(p.X, p.Y) / M_PI * 180.0; - if (angle < 0.0) angle += 360.0; + if (angle < 0.0) + angle += 360.0; return angle; } @@ -156,12 +195,14 @@ INLINE const Point& make_point(const Point& p) return p; } -}//namespace cura +} // namespace cura -namespace std { -template <> -struct hash { - size_t operator()(const cura::Point & pp) const +namespace std +{ +template<> +struct hash +{ + size_t operator()(const cura::Point& pp) const { static int prime = 31; int result = 89; @@ -170,7 +211,7 @@ struct hash { return result; } }; -} +} // namespace std namespace cura { @@ -234,8 +275,8 @@ class PointMatrix PointMatrix ret; double det = matrix[0] * matrix[3] - matrix[1] * matrix[2]; ret.matrix[0] = matrix[3] / det; - ret.matrix[1] = - matrix[1] / det; - ret.matrix[2] = - matrix[2] / det; + ret.matrix[1] = -matrix[1] / det; + ret.matrix[2] = -matrix[2] / det; ret.matrix[3] = matrix[0] / det; return ret; } @@ -278,9 +319,10 @@ class Point3Matrix Point3 apply(const Point3 p) const { - return Point3(std::llrint(p.x * matrix[0] + p.y * matrix[1] + p.z * matrix[2]) - , std::llrint(p.x * matrix[3] + p.y * matrix[4] + p.z * matrix[5]) - , std::llrint(p.x * matrix[6] + p.y * matrix[7] + p.z * matrix[8])); + return Point3( + std::llrint(p.x * matrix[0] + p.y * matrix[1] + p.z * matrix[2]), + std::llrint(p.x * matrix[3] + p.y * matrix[4] + p.z * matrix[5]), + std::llrint(p.x * matrix[6] + p.y * matrix[7] + p.z * matrix[8])); } /*! @@ -319,33 +361,38 @@ class Point3Matrix }; -inline Point3 operator+(const Point3& p3, const Point& p2) { +inline Point3 operator+(const Point3& p3, const Point& p2) +{ return Point3(p3.x + p2.X, p3.y + p2.Y, p3.z); } -inline Point3& operator+=(Point3& p3, const Point& p2) { +inline Point3& operator+=(Point3& p3, const Point& p2) +{ p3.x += p2.X; p3.y += p2.Y; return p3; } -inline Point operator+(const Point& p2, const Point3& p3) { +inline Point operator+(const Point& p2, const Point3& p3) +{ return Point(p3.x + p2.X, p3.y + p2.Y); } -inline Point3 operator-(const Point3& p3, const Point& p2) { +inline Point3 operator-(const Point3& p3, const Point& p2) +{ return Point3(p3.x - p2.X, p3.y - p2.Y, p3.z); } -inline Point3& operator-=(Point3& p3, const Point& p2) { +inline Point3& operator-=(Point3& p3, const Point& p2) +{ p3.x -= p2.X; p3.y -= p2.Y; return p3; } -inline Point operator-(const Point& p2, const Point3& p3) { +inline Point operator-(const Point& p2, const Point3& p3) +{ return Point(p2.X - p3.x, p2.Y - p3.y); } -}//namespace cura -#endif//UTILS_INT_POINT_H - +} // namespace cura +#endif // UTILS_INT_POINT_H From 6abf7ae811e0153755d9b169bd40c46c55af1c19 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 07:01:30 +0200 Subject: [PATCH 370/656] Refactor branch identification for Cura checkout in workflow The change optimizes the process to determine whether the current branch does exist in the Cura repository before checkout. While before this was done in a complex shell command that also initiated a sparse checkout on the Cura repository, now it's split into two distinct steps: determining branch existence and checking out the required files, making the workflow more readable and maintainable. --- .github/workflows/gcodeanalyzer.yml | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index ccf95b321a..343ec33d7f 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -83,25 +83,26 @@ jobs: path: 'NightlyTestModels' token: ${{ secrets.GITHUB_TOKEN }} - - name: Checkout .def.json files from Cura frontend + - name: Determine the corresponding Cura branch + id: curabranch run: | - cd CuraEngine - branch_name=$(git rev-parse --abbrev-ref HEAD) - exists=$(curl -s -o /dev/null -w "%{http_code}" "https://api.github.com/repos/ultimaker/cura/branches/${branch_name}") - cd .. - mkdir Cura - cd Cura - git init - git sparse-checkout set resources/definitions resources/extruders - git remote add -f origin https://github.com/Ultimaker/Cura - if [ "$exists" == "200" ]; then - echo "Using '${branch_name}' for Cura" >> $GITHUB_STEP_SUMMARY - git pull origin ${branch_name} + status_code=$(curl -s -o /dev/null -w "%{http_code}" "https://api.github.com/repos/ultimaker/cura/branches/${{ github.ref_name }}") + if [ "$status_code" -eq 200 ]; then + echo "The branch exists in Cura" + echo "branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT else - echo "Using 'main' for Cura" >> $GITHUB_STEP_SUMMARY - git pull origin main + echo "branch=main" >> $GITHUB_OUTPUT fi - cd .. + + - name: Checkout Cura + uses: actions/checkout@v3 + with: + repository: 'Ultimaker/Cura' + ref: ${{ steps.curabranch.outputs.branch}} + path: 'Cura' + sparse-checkout: | + resources/definitions + resources/extruders - name: Setup Python and pip uses: actions/setup-python@v4 From 8c113272e881f3c426f55881e430d06f6119f335 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 07:20:36 +0200 Subject: [PATCH 371/656] use head_ref --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 343ec33d7f..03112c67ae 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -86,7 +86,7 @@ jobs: - name: Determine the corresponding Cura branch id: curabranch run: | - status_code=$(curl -s -o /dev/null -w "%{http_code}" "https://api.github.com/repos/ultimaker/cura/branches/${{ github.ref_name }}") + status_code=$(curl -s -o /dev/null -w "%{http_code}" "https://api.github.com/repos/ultimaker/cura/branches/${{ github.head_ref }}") if [ "$status_code" -eq 200 ]; then echo "The branch exists in Cura" echo "branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT From c698ad825ce8a374cb98a68223d60c7006a8f43d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 07:25:00 +0200 Subject: [PATCH 372/656] use head_ref --- .github/workflows/gcodeanalyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 03112c67ae..c9e2039efd 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -89,7 +89,7 @@ jobs: status_code=$(curl -s -o /dev/null -w "%{http_code}" "https://api.github.com/repos/ultimaker/cura/branches/${{ github.head_ref }}") if [ "$status_code" -eq 200 ]; then echo "The branch exists in Cura" - echo "branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "branch=${{ github.head_ref }}" >> $GITHUB_OUTPUT else echo "branch=main" >> $GITHUB_OUTPUT fi From 704b8b31488faa9cecf53f5b3c267d788e448859 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 07:54:17 +0200 Subject: [PATCH 373/656] Update benchmark.yml --- .github/workflows/benchmark.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 40305203f5..9cc29c403a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -143,7 +143,7 @@ jobs: - name: Run benchmark CuraEngine id: run-test - run: ./benchmarks --benchmark_format=json | tee benchmark_result.json + run: ./benchmarks --benchmark_format=json >> benchmark_result.json working-directory: build/Release/benchmark - name: Store benchmark result @@ -156,6 +156,5 @@ jobs: auto-push: true # Show alert with commit comment on detecting possible performance regression alert-threshold: '175%' - summary-always: true comment-on-alert: true max-items-in-chart: 250 From 6d0b4071a17168f72fd5d6ec87cabb7f58e4060c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 08:15:05 +0200 Subject: [PATCH 374/656] Update benchmark.yml --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9cc29c403a..6dfcac34f2 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -156,5 +156,5 @@ jobs: auto-push: true # Show alert with commit comment on detecting possible performance regression alert-threshold: '175%' - comment-on-alert: true + comment-on-alert: false max-items-in-chart: 250 From aab085ae3efe0ad92f7959846d107be2a328887f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 08:25:50 +0200 Subject: [PATCH 375/656] Increase timeout to 5 minutes Supporting longer processes Contributes to CURA-10619 --- include/plugins/components/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h index 661e1dc23d..fe99c7043a 100644 --- a/include/plugins/components/common.h +++ b/include/plugins/components/common.h @@ -29,7 +29,7 @@ using plugin_info_ptr = std::shared_ptr>; * @param timeout - Call timeout duration (optional, default = 500ms) */ inline static void - prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::milliseconds(500)) + prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::minutes(5)) { // Set time-out client_context.set_deadline(std::chrono::system_clock::now() + timeout); From 2d05be95018a4f6cf973ee9f1284922bec933bfa Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 06:26:33 +0000 Subject: [PATCH 376/656] Applied clang-format. --- include/plugins/components/common.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h index fe99c7043a..80666a9b3e 100644 --- a/include/plugins/components/common.h +++ b/include/plugins/components/common.h @@ -28,8 +28,7 @@ using plugin_info_ptr = std::shared_ptr>; * @param client_context - Client context to prepare * @param timeout - Call timeout duration (optional, default = 500ms) */ -inline static void - prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::minutes(5)) +inline static void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::minutes(5)) { // Set time-out client_context.set_deadline(std::chrono::system_clock::now() + timeout); From 5b4c41d9e9b0747d6d0139aa4d0729e58cd71e19 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 10 Aug 2023 11:34:24 +0200 Subject: [PATCH 377/656] In order to prevent risky casts to unsigned, change all to LayerIndex. Otherwise this might cause a _very big_integer where we're expecting a negative one. Not sure how this even worked properly _before_ the refator then, but here we are. CURA-10927 --- include/FffPolygonGenerator.h | 2 +- include/InsetOrderOptimizer.h | 2 +- include/LayerPlan.h | 2 +- include/gcodeExport.h | 4 ++-- include/support.h | 2 +- src/ConicalOverhang.cpp | 3 ++- src/FffGcodeWriter.cpp | 14 +++++++------- src/FffPolygonGenerator.cpp | 10 +++++----- src/InterlockingGenerator.cpp | 5 +++-- src/LayerPlan.cpp | 2 +- src/Mold.cpp | 6 +++--- src/gcodeExport.cpp | 2 +- src/multiVolumes.cpp | 7 ++++--- src/slicer.cpp | 6 +++--- src/support.cpp | 14 +++++++------- 15 files changed, 42 insertions(+), 39 deletions(-) diff --git a/include/FffPolygonGenerator.h b/include/FffPolygonGenerator.h index e92e02c6f6..49342b57fe 100644 --- a/include/FffPolygonGenerator.h +++ b/include/FffPolygonGenerator.h @@ -108,7 +108,7 @@ class FffPolygonGenerator : public NoCopy * * \return Whether or not the layer is empty */ - bool isEmptyLayer(SliceDataStorage& storage, const unsigned int layer_idx); + bool isEmptyLayer(SliceDataStorage& storage, const LayerIndex& layer_idx); /*! * \brief Remove all bottom layers which are empty. diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index 432ae2c29f..eb92783f8d 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -99,7 +99,7 @@ class InsetOrderOptimizer const size_t wall_x_extruder_nr; const ZSeamConfig& z_seam_config; const std::vector& paths; - const unsigned int layer_nr; + const LayerIndex layer_nr; std::vector> inset_polys; // vector of vectors holding the inset polygons Polygons retraction_region; //After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see retraction_region_calculated). diff --git a/include/LayerPlan.h b/include/LayerPlan.h index a4679761e1..a73b6b2f3d 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -327,7 +327,7 @@ class LayerPlan : public NoCopy const Polygons* getCombBoundaryInside() const; - int getLayerNr() const; + LayerIndex getLayerNr() const; /*! * Get the last planned position, or if no position has been planned yet, the user specified layer start position. diff --git a/include/gcodeExport.h b/include/gcodeExport.h index c3f367d3a3..47e423fb65 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -155,7 +155,7 @@ class GCodeExport : public NoCopy std::vector total_print_times; //!< The total estimated print time in seconds for each feature TimeEstimateCalculator estimateCalculator; - unsigned int layer_nr; //!< for sending travel data + LayerIndex layer_nr; //!< for sending travel data bool is_volumetric; bool relative_extrusion; //!< whether to use relative extrusion distances rather than absolute @@ -240,7 +240,7 @@ class GCodeExport : public NoCopy void setSliceUUID(const std::string& slice_uuid); - void setLayerNr(unsigned int layer_nr); + void setLayerNr(const LayerIndex& layer_nr); void setOutputStream(std::ostream* stream); diff --git a/include/support.h b/include/support.h index 43a7676db7..16819c0c90 100644 --- a/include/support.h +++ b/include/support.h @@ -274,7 +274,7 @@ class AreaSupport * \param layer_idx The layer for which to compute the overhang. * \return A pair of basic overhang and full overhang. */ - static std::pair computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx); + static std::pair computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const LayerIndex& layer_idx); /*! * \brief Adds tower pieces to the current support layer. diff --git a/src/ConicalOverhang.cpp b/src/ConicalOverhang.cpp index d1fdf7dd0d..c5cd52b0b0 100644 --- a/src/ConicalOverhang.cpp +++ b/src/ConicalOverhang.cpp @@ -7,6 +7,7 @@ #include "utils/Simplify.h" //Simplifying at every step to prevent getting lots of vertices from all the insets. #include "slicer.h" #include "settings/types/Angle.h" //To process the overhang angle. +#include "settings/types/LayerIndex.h" namespace cura { @@ -19,7 +20,7 @@ void ConicalOverhang::apply(Slicer* slicer, const Mesh& mesh) 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 - for(unsigned int layer_nr = slicer->layers.size() - 2; static_cast(layer_nr) >= 0; layer_nr--) + for(LayerIndex layer_nr = slicer->layers.size() - 2; static_cast(layer_nr) >= 0; layer_nr--) { SlicerLayer& layer = slicer->layers[layer_nr]; SlicerLayer& layer_above = slicer->layers[layer_nr + 1]; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fd836e57a8..27b349d77b 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -169,7 +169,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep }, [this, total_layers](LayerPlan* gcode_layer) { - Progress::messageProgress(Progress::Stage::EXPORT, std::max(0, gcode_layer->getLayerNr()) + 1, total_layers); + Progress::messageProgress(Progress::Stage::EXPORT, std::max(LayerIndex{ 0 }, gcode_layer->getLayerNr()) + 1, total_layers); layer_plan_buffer.handle(*gcode_layer, gcode); }); @@ -1251,7 +1251,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { - unsigned int layer_nr = std::max(0, gcode_layer.getLayerNr()); + LayerIndex layer_nr = std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr()); if (layer_nr == 0 && Application::getInstance().current_slice->scene.current_mesh_group->settings.get("adhesion_type") == EPlatformAdhesion::BRIM) { return; // ooze shield already generated by brim @@ -1265,7 +1265,7 @@ void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPla void FffGcodeWriter::processDraftShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const LayerIndex layer_nr = std::max(0, gcode_layer.getLayerNr()); + const LayerIndex layer_nr = std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr()); if (storage.draft_protection_shield.size() == 0) { return; @@ -2938,7 +2938,7 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla size_t support_infill_extruder_nr = (gcode_layer.getLayerNr() <= 0) ? mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr : mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; - const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; + const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; if (support_layer.support_bottom.empty() && support_layer.support_roof.empty() && support_layer.support_infill_parts.empty()) { return support_added; @@ -2963,7 +2963,7 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { bool added_something = false; - const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; // account for negative layer numbers for raft filler layers + const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; // account for negative layer numbers for raft filler layers if (gcode_layer.getLayerNr() > storage.support.layer_nr_max_filled_layer || support_layer.support_infill_parts.empty()) { @@ -3264,7 +3264,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { - const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; + const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; if (! storage.support.generated || gcode_layer.getLayerNr() > storage.support.layer_nr_max_filled_layer || support_layer.support_roof.empty()) { @@ -3395,7 +3395,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { - const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, gcode_layer.getLayerNr())]; + const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; if (! storage.support.generated || gcode_layer.getLayerNr() > storage.support.layer_nr_max_filled_layer || support_layer.support_bottom.empty()) { diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 817e91ecde..805b7e8bf4 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -295,7 +295,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; // calculate the height at which each layer is actually printed (printZ) - for (unsigned int layer_nr = 0; layer_nr < meshStorage.layers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < meshStorage.layers.size(); layer_nr++) { SliceLayer& layer = meshStorage.layers[layer_nr]; @@ -715,7 +715,7 @@ void FffPolygonGenerator::processWalls(SliceMeshStorage& mesh, size_t layer_nr) walls_computation.generateWalls(layer, SectionType::WALL); } -bool FffPolygonGenerator::isEmptyLayer(SliceDataStorage& storage, const unsigned int layer_idx) +bool FffPolygonGenerator::isEmptyLayer(SliceDataStorage& storage, const LayerIndex& layer_idx) { if (storage.support.generated && layer_idx < storage.support.supportLayers.size()) { @@ -962,10 +962,10 @@ 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 LayerIndex layer_skip{ 500 / 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) + for (LayerIndex layer_nr = 0; layer_nr < storage.print_layer_count && layer_nr < draft_shield_layers; layer_nr += layer_skip) { constexpr bool around_support = true; constexpr bool around_prime_tower = false; @@ -1052,7 +1052,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) auto hole_area = Polygons(); std::function accumulate_is_in_hole = [](const bool& prev_result, const ExtrusionJunction& junction) { return false; }; - for (unsigned int layer_nr = start_layer_nr; layer_nr < mesh.layers.size(); layer_nr++) + for (LayerIndex layer_nr = start_layer_nr; layer_nr < mesh.layers.size(); layer_nr++) { SliceLayer& layer = mesh.layers[layer_nr]; for (SliceLayerPart& part : layer.parts) diff --git a/src/InterlockingGenerator.cpp b/src/InterlockingGenerator.cpp index c9e6cc8b89..7a6a068a78 100644 --- a/src/InterlockingGenerator.cpp +++ b/src/InterlockingGenerator.cpp @@ -9,6 +9,7 @@ #include "Slice.h" #include "slicer.h" #include "utils/polygonUtils.h" +#include "settings/types/LayerIndex.h" #include "utils/VoxelUtils.h" #include @@ -215,7 +216,7 @@ std::vector InterlockingGenerator::computeUnionedVolumeRegions() const const size_t max_layer_count = std::max(mesh_a.layers.size(), mesh_b.layers.size()) + 1; // introduce ghost layer on top for correct skin computation of topmost layer. std::vector layer_regions(max_layer_count); - for (unsigned int layer_nr = 0; layer_nr < max_layer_count; layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < max_layer_count; layer_nr++) { Polygons& layer_region = layer_regions[layer_nr]; for (Slicer* mesh : {&mesh_a, &mesh_b}) @@ -288,7 +289,7 @@ void InterlockingGenerator::applyMicrostructureToOutlines(const std::unordered_s Point3 bottom_corner = vu.toLowerCorner(grid_loc); for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++) { - for (unsigned int layer_nr = bottom_corner.z; layer_nr < bottom_corner.z + cell_size.z && layer_nr < max_layer_count; layer_nr += beam_layer_count) + for (LayerIndex layer_nr = bottom_corner.z; layer_nr < bottom_corner.z + cell_size.z && layer_nr < max_layer_count; layer_nr += beam_layer_count) { Polygons areas_here = cell_area_per_mesh_per_layer[(layer_nr / beam_layer_count) % cell_area_per_mesh_per_layer.size()][mesh_idx]; areas_here.translate(Point(bottom_corner.x, bottom_corner.y)); diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index dd153eedec..2da2fc8a04 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2412,7 +2412,7 @@ void LayerPlan::applyBackPressureCompensation() } } -int LayerPlan::getLayerNr() const +LayerIndex LayerPlan::getLayerNr() const { return layer_nr; } diff --git a/src/Mold.cpp b/src/Mold.cpp index e7d86ffb33..6816d684d7 100644 --- a/src/Mold.cpp +++ b/src/Mold.cpp @@ -34,12 +34,12 @@ void Mold::process(std::vector& slicer_list) } } - unsigned int layer_count = 0; + LayerIndex layer_count = 0; { // compute layer_count for (unsigned int mesh_idx = 0; mesh_idx < slicer_list.size(); mesh_idx++) { Slicer& slicer = *slicer_list[mesh_idx]; - unsigned int layer_count_here = slicer.layers.size(); + LayerIndex layer_count_here = slicer.layers.size(); layer_count = std::max(layer_count, layer_count_here); } } @@ -92,7 +92,7 @@ void Mold::process(std::vector& slicer_list) // add roofs if (roof_layer_count > 0 && layer_nr > 0) { - unsigned int layer_nr_below = std::max(0, static_cast(layer_nr - roof_layer_count)); + LayerIndex layer_nr_below = std::max(0, static_cast(layer_nr - roof_layer_count)); Polygons roofs = slicer.layers[layer_nr_below].polygons.offset(width, ClipperLib::jtRound); // TODO: don't compute offset twice! layer.polygons = layer.polygons.unionPolygons(roofs); } diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 0fd047e76e..0b51882f45 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -285,7 +285,7 @@ std::string GCodeExport::getFileHeader( } -void GCodeExport::setLayerNr(unsigned int layer_nr_) +void GCodeExport::setLayerNr(const LayerIndex& layer_nr_) { layer_nr = layer_nr_; } diff --git a/src/multiVolumes.cpp b/src/multiVolumes.cpp index 25a4147d65..cf42eb3f96 100644 --- a/src/multiVolumes.cpp +++ b/src/multiVolumes.cpp @@ -10,6 +10,7 @@ #include "slicer.h" #include "utils/PolylineStitcher.h" #include "settings/EnumSettings.h" +#include "settings/types/LayerIndex.h" namespace cura { @@ -50,7 +51,7 @@ void carveMultipleVolumes(std::vector &volumes) { continue; } - for (unsigned int layerNr = 0; layerNr < volume_1.layers.size(); layerNr++) + for (LayerIndex layerNr = 0; layerNr < volume_1.layers.size(); layerNr++) { SlicerLayer& layer1 = volume_1.layers[layerNr]; SlicerLayer& layer2 = volume_2.layers[layerNr]; @@ -91,7 +92,7 @@ void generateMultipleVolumesOverlap(std::vector &volumes) } AABB3D aabb(volume->mesh->getAABB()); aabb.expandXY(overlap); // expand to account for the case where two models and their bounding boxes are adjacent along the X or Y-direction - for (unsigned int layer_nr = 0; layer_nr < volume->layers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < volume->layers.size(); layer_nr++) { Polygons all_other_volumes; for (Slicer* other_volume : volumes) @@ -125,7 +126,7 @@ void MultiVolumes::carveCuttingMeshes(std::vector& volumes, const std:: continue; } Slicer& cutting_mesh_volume = *volumes[carving_mesh_idx]; - for (unsigned int layer_nr = 0; layer_nr < cutting_mesh_volume.layers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < cutting_mesh_volume.layers.size(); layer_nr++) { Polygons& cutting_mesh_polygons = cutting_mesh_volume.layers[layer_nr].polygons; Polygons& cutting_mesh_polylines = cutting_mesh_volume.layers[layer_nr].openPolylines; diff --git a/src/slicer.cpp b/src/slicer.cpp index b7144169c3..41365a19e8 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -956,7 +956,7 @@ std::vector } // define all layer z positions (depending on slicing mode, see above) - for (unsigned int layer_nr = 1; layer_nr < slice_layer_count; layer_nr++) + for (LayerIndex layer_nr = 1; layer_nr < slice_layer_count; layer_nr++) { if (use_variable_layer_heights) { @@ -978,13 +978,13 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v switch (slicing_tolerance) { case SlicingTolerance::INCLUSIVE: - for (unsigned int layer_nr = 0; layer_nr + 1 < layers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr + 1 < layers.size(); layer_nr++) { layers[layer_nr].polygons = layers[layer_nr].polygons.unionPolygons(layers[layer_nr + 1].polygons); } break; case SlicingTolerance::EXCLUSIVE: - for (unsigned int layer_nr = 0; layer_nr + 1 < layers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr + 1 < layers.size(); layer_nr++) { layers[layer_nr].polygons = layers[layer_nr].polygons.intersection(layers[layer_nr + 1].polygons); } diff --git a/src/support.cpp b/src/support.cpp index e20aaac53c..45f9901d77 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -56,7 +56,7 @@ bool AreaSupport::handleSupportModifierMesh(SliceDataStorage& storage, const Set }; ModifierType modifier_type = (mesh_settings.get("anti_overhang_mesh")) ? ANTI_OVERHANG : ((mesh_settings.get("support_mesh_drop_down")) ? SUPPORT_DROP_DOWN : SUPPORT_VANILLA); - for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++) { SupportLayer& support_layer = storage.support.supportLayers[layer_nr]; const SlicerLayer& slicer_layer = slicer->layers[layer_nr]; @@ -99,7 +99,7 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( const size_t wall_line_count = infill_extruder.settings.get("support_wall_count"); // Generate separate support islands - for (unsigned int layer_nr = 0; layer_nr < total_layer_count - 1; ++layer_nr) + for (LayerIndex layer_nr = 0; layer_nr < total_layer_count - 1; ++layer_nr) { unsigned int wall_line_count_this_layer = wall_line_count; if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) @@ -425,7 +425,7 @@ void AreaSupport::combineSupportInfillLayers(SliceDataStorage& storage) void AreaSupport::cleanup(SliceDataStorage& storage) { const coord_t support_line_width = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_line_width"); - for (unsigned int layer_nr = 0; layer_nr < storage.support.supportLayers.size(); layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < storage.support.supportLayers.size(); layer_nr++) { SupportLayer& layer = storage.support.supportLayers[layer_nr]; for (unsigned int part_idx = 0; part_idx < layer.support_infill_parts.size(); part_idx++) @@ -685,7 +685,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) } } - for (unsigned int layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++) + for (LayerIndex layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++) { Polygons& support_areas = global_support_areas_per_layer[layer_idx]; support_areas = support_areas.unionPolygons(); @@ -1323,7 +1323,7 @@ void AreaSupport::generateSupportAreasForMesh( conical_support_offset = (tan(-conical_support_angle) - 0.01) * layer_thickness; } const bool conical_support = infill_settings.get("support_conical_enabled") && conical_support_angle != 0; - for (unsigned int layer_idx = 1; layer_idx < storage.support.supportLayers.size(); layer_idx++) + for (LayerIndex layer_idx = 1; layer_idx < storage.support.supportLayers.size(); layer_idx++) { const Polygons& layer = support_areas[layer_idx]; @@ -1507,7 +1507,7 @@ void AreaSupport::moveUpFromModel( * ^^^^^^^^^ overhang extensions * ^^^^^^^^^^^^^^ overhang */ -std::pair AreaSupport::computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx) +std::pair AreaSupport::computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const LayerIndex& layer_idx) { const Polygons outlines = mesh.layers[layer_idx].getOutlines(); constexpr bool no_support = false; @@ -1765,7 +1765,7 @@ void AreaSupport::generateSupportBottom(SliceDataStorage& storage, const SliceMe const double minimum_bottom_area = mesh.settings.get("minimum_bottom_area"); std::vector& support_layers = storage.support.supportLayers; - for (unsigned int layer_idx = support_layers.size() - 1; static_cast(layer_idx) >= static_cast(z_distance_bottom); layer_idx--) + for (LayerIndex layer_idx = support_layers.size() - 1; layer_idx >= static_cast(z_distance_bottom); --layer_idx) { const unsigned int bottom_layer_idx_below = std::max(0, int(layer_idx) - int(bottom_layer_count) - int(z_distance_bottom)); Polygons mesh_outlines; From 35b356ff782dfde8c0729bce331c477dc42a5eb4 Mon Sep 17 00:00:00 2001 From: rburema Date: Thu, 10 Aug 2023 09:48:59 +0000 Subject: [PATCH 378/656] Applied clang-format. --- include/InsetOrderOptimizer.h | 52 +++--- include/LayerPlan.h | 246 +++++++++++++++++-------- src/ConicalOverhang.cpp | 31 ++-- src/FffGcodeWriter.cpp | 3 +- src/FffPolygonGenerator.cpp | 136 ++++++++------ src/InterlockingGenerator.cpp | 53 +++--- src/Mold.cpp | 18 +- src/multiVolumes.cpp | 79 ++++---- src/slicer.cpp | 331 +++++++++++++++++++--------------- 9 files changed, 553 insertions(+), 396 deletions(-) diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index eb92783f8d..f5bdba8f57 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -4,11 +4,11 @@ #ifndef INSET_ORDER_OPTIMIZER_H #define INSET_ORDER_OPTIMIZER_H -#include - #include "settings/ZSeamConfig.h" #include "sliceDataStorage.h" +#include + namespace cura { @@ -37,22 +37,23 @@ class InsetOrderOptimizer * \param part The part from which to read the previously generated insets. * \param layer_nr The current layer number. */ - InsetOrderOptimizer(const FffGcodeWriter& gcode_writer, - const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const Settings& settings, - const int extruder_nr, - const GCodePathConfig& inset_0_non_bridge_config, - const GCodePathConfig& inset_X_non_bridge_config, - const GCodePathConfig& inset_0_bridge_config, - const GCodePathConfig& inset_X_bridge_config, - const bool retract_before_outer_wall, - const coord_t wall_0_wipe_dist, - const coord_t wall_x_wipe_dist, - const size_t wall_0_extruder_nr, - const size_t wall_x_extruder_nr, - const ZSeamConfig& z_seam_config, - const std::vector& paths); + InsetOrderOptimizer( + const FffGcodeWriter& gcode_writer, + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const Settings& settings, + const int extruder_nr, + const GCodePathConfig& inset_0_non_bridge_config, + const GCodePathConfig& inset_X_non_bridge_config, + const GCodePathConfig& inset_0_bridge_config, + const GCodePathConfig& inset_X_bridge_config, + const bool retract_before_outer_wall, + const coord_t wall_0_wipe_dist, + const coord_t wall_x_wipe_dist, + const size_t wall_0_extruder_nr, + const size_t wall_x_extruder_nr, + const ZSeamConfig& z_seam_config, + const std::vector& paths); /*! * Adds the insets to the given layer plan. @@ -66,9 +67,9 @@ class InsetOrderOptimizer /*! * Get the order constraints of the insets when printing walls per region / hole. * Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right. - * + * * Odd walls should always go after their enclosing wall polygons. - * + * * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. */ static value_type getRegionOrder(const auto& input, const bool outer_to_inner); @@ -76,12 +77,13 @@ class InsetOrderOptimizer /*! * Get the order constraints of the insets when printing walls per inset. * Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right. - * + * * Odd walls should always go after their enclosing wall polygons. - * + * * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. */ static value_type getInsetOrder(const auto& input, const bool outer_to_inner); + private: const FffGcodeWriter& gcode_writer; const SliceDataStorage& storage; @@ -102,7 +104,8 @@ class InsetOrderOptimizer const LayerIndex layer_nr; std::vector> inset_polys; // vector of vectors holding the inset polygons - Polygons retraction_region; //After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see retraction_region_calculated). + Polygons retraction_region; // After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see + // retraction_region_calculated). /*! * Determine if the paths should be reversed @@ -130,8 +133,7 @@ class InsetOrderOptimizer * \return A vector of ExtrusionLines with walls that should be printed */ std::vector getWallsToBeAdded(const bool reverse, const bool use_one_extruder); - }; -} //namespace cura +} // namespace cura #endif // INSET_ORDER_OPTIMIZER_H diff --git a/include/LayerPlan.h b/include/LayerPlan.h index a73b6b2f3d..64da17faa1 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -9,24 +9,23 @@ #include #include #ifdef BUILD_TESTS - #include //Friend tests, so that they can inspect the privates. +#include //Friend tests, so that they can inspect the privates. #endif #include "FanSpeedLayerTime.h" -#include "gcodeExport.h" +#include "InsetOrderOptimizer.h" #include "PathOrderOptimizer.h" #include "SpaceFillType.h" +#include "gcodeExport.h" #include "pathPlanning/GCodePath.h" #include "pathPlanning/NozzleTempInsert.h" #include "pathPlanning/TimeMaterialEstimates.h" #include "settings/PathConfigStorage.h" #include "settings/types/LayerIndex.h" -#include "utils/polygon.h" - -#include "InsetOrderOptimizer.h" #include "utils/ExtrusionJunction.h" +#include "utils/polygon.h" -namespace cura +namespace cura { class Comb; @@ -36,7 +35,7 @@ class SliceDataStorage; /*! * An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train. - * + * * It allows for temperature command inserts which can be inserted in between paths. */ class ExtruderPlan @@ -59,18 +58,20 @@ class ExtruderPlan /*! * The required temperature at the start of this extruder plan * or the temp to which to heat gradually over the layer change between this plan and the previous with the same extruder. - * + * * In case this extruder plan uses a different extruder than the last extruder plan: * this is the temperature to which to heat and wait before starting this extruder. - * + * * In case this extruder plan uses the same extruder as the previous extruder plan (previous layer): * this is the temperature used to heat to gradually when moving from the previous extruder layer to the next. * In that case no temperature (and wait) command will be inserted from this value, but a NozzleTempInsert is used instead. * In this case this member is only used as a way to convey information between different calls of \ref LayerPlanBuffer::processBuffer */ double required_start_temperature; - std::optional extrusion_temperature; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate because of the initial and final print temp (none if extruder plan has no extrusion moves) - std::optional::iterator> extrusion_temperature_command; //!< The command to heat from the printing temperature of this extruder plan to the printing temperature of the next extruder plan (if it has the same extruder). + std::optional extrusion_temperature; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate because of the + //!< initial and final print temp (none if extruder plan has no extrusion moves) + std::optional::iterator> extrusion_temperature_command; //!< The command to heat from the printing temperature of this extruder plan to the printing + //!< temperature of the next extruder plan (if it has the same extruder). std::optional prev_extruder_standby_temp; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder. TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan. @@ -81,15 +82,22 @@ class ExtruderPlan /*! * Simple contructor. - * + * * \warning Doesn't set the required temperature yet. - * + * * \param extruder The extruder number for which this object is a plan. * \param layer_nr The layer index of the layer that this extruder plan is * part of. * \param is_raft_layer Whether this extruder plan is part of a raft layer. */ - ExtruderPlan(const size_t extruder, const LayerIndex layer_nr, const bool is_initial_layer, const bool is_raft_layer, const coord_t layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config); + ExtruderPlan( + const size_t extruder, + const LayerIndex layer_nr, + const bool is_initial_layer, + const bool is_raft_layer, + const coord_t layer_thickness, + const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, + const RetractionConfig& retraction_config); void insertCommand(auto&& insert) @@ -99,7 +107,7 @@ class ExtruderPlan /*! * Insert the inserts into gcode which should be inserted before \p path_idx - * + * * \param path_idx The index into ExtruderPlan::paths which is currently being consider for temperature command insertion * \param gcode The gcode exporter to which to write the temperature command. * \param cumulative_path_time The time spend on this path up to this point. @@ -108,18 +116,18 @@ class ExtruderPlan /*! * Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan - * + * * Inserts temperature commands which should be inserted _after_ the last path. * Also inserts all temperatures which should have been inserted earlier, * but for which ExtruderPlan::handleInserts hasn't been called correctly. - * + * * \param gcode The gcode exporter to which to write the temperature command. */ void handleAllRemainingInserts(GCodeExport& gcode); /*! * Applying fan speed changes for minimal layer times. - * + * * \param starting_position The position the head was before starting this extruder plan * \param minTime Maximum minimum layer time for all extruders in this layer * \param time_other_extr_plans The time spent on the other extruder plans in this layer @@ -196,7 +204,7 @@ class ExtruderPlan /*! * Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates. * and store them in each ExtruderPlan and each GCodePath. - * + * * \param starting_position The position the head was in before starting this layer * \return the total estimates of this layer */ @@ -205,15 +213,15 @@ class ExtruderPlan class LayerPlanBuffer; // forward declaration to prevent circular dependency -/*! +/*! * The LayerPlan class stores multiple moves that are planned. - * - * + * + * * It facilitates the combing to keep the head inside the print. * It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time. - * + * * A LayerPlan is also knows as a 'layer plan'. - * + * */ class LayerPlan : public NoCopy { @@ -227,7 +235,6 @@ class LayerPlan : public NoCopy bool mode_skip_agressive_merge; //!< Whether to give every new path the 'skip_agressive_merge_hint' property (see GCodePath); default is false. private: - const SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor const LayerIndex layer_nr; //!< The layer number of this layer plan const bool is_initial_layer; //!< Whether this is the first layer (which might be raft) @@ -260,7 +267,7 @@ class LayerPlan : public NoCopy Polygons comb_boundary_minimum; //!< The minimum boundary within which to comb, or to move into when performing a retraction. Polygons comb_boundary_preferred; //!< The boundary preferably within which to comb, or to move into when performing a retraction. Comb* comb; - coord_t comb_move_inside_distance; //!< Whenever using the minimum boundary for combing it tries to move the coordinates inside by this distance after calculating the combing. + coord_t comb_move_inside_distance; //!< Whenever using the minimum boundary for combing it tries to move the coordinates inside by this distance after calculating the combing. Polygons bridge_wall_mask; //!< The regions of a layer part that are not supported, used for bridging Polygons overhang_mask; //!< The regions of a layer part where the walls overhang @@ -275,7 +282,7 @@ class LayerPlan : public NoCopy /*! * Either create a new path with the given config or return the last path if it already had that config. * If LayerPlan::forceNewPathStart has been called a new path will always be returned. - * + * * \param config The config used for the path returned * \param space_fill_type The type of space filling which this path employs * \param flow (optional) A ratio for the extrusion speed @@ -283,17 +290,23 @@ class LayerPlan : public NoCopy * \param speed_factor (optional) a factor which the speed will be multiplied by. * \return A path with the given config which is now the last path in LayerPlan::paths */ - GCodePath* getLatestPathWithConfig(const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio flow = 1.0_r, const Ratio width_factor = 1.0_r, bool spiralize = false, const Ratio speed_factor = 1.0_r); + GCodePath* getLatestPathWithConfig( + const GCodePathConfig& config, + SpaceFillType space_fill_type, + const Ratio flow = 1.0_r, + const Ratio width_factor = 1.0_r, + bool spiralize = false, + const Ratio speed_factor = 1.0_r); public: /*! * Force LayerPlan::getLatestPathWithConfig to return a new path. - * - * This function is introduced because in some cases - * LayerPlan::getLatestPathWithConfig is called consecutively with the same config pointer, + * + * This function is introduced because in some cases + * LayerPlan::getLatestPathWithConfig is called consecutively with the same config pointer, * though the content of the config has changed. - * - * Example cases: + * + * Example cases: * - when changing extruder, the same travel config is used, but its extruder field is changed. */ void forceNewPathStart(); @@ -313,7 +326,16 @@ class LayerPlan : public NoCopy * \param travel_avoid_distance The distance by which to avoid other layer * parts when travelling through air. */ - LayerPlan(const SliceDataStorage& storage, LayerIndex layer_nr, coord_t z, coord_t layer_height, size_t start_extruder, const std::vector& fan_speed_layer_time_settings_per_extruder, coord_t comb_boundary_offset, coord_t comb_move_inside_distance, coord_t travel_avoid_distance); + LayerPlan( + const SliceDataStorage& storage, + LayerIndex layer_nr, + coord_t z, + coord_t layer_height, + size_t start_extruder, + const std::vector& fan_speed_layer_time_settings_per_extruder, + coord_t comb_boundary_offset, + coord_t comb_move_inside_distance, + coord_t travel_avoid_distance); ~LayerPlan(); @@ -331,7 +353,7 @@ class LayerPlan : public NoCopy /*! * Get the last planned position, or if no position has been planned yet, the user specified layer start position. - * + * * \warning The layer start position might be outside of the build plate! */ Point getLastPlannedPositionOrStartingPosition() const; @@ -361,17 +383,17 @@ class LayerPlan : public NoCopy /*! * Get the destination state of the first travel move. * This consists of the location and whether the destination was inside the model, or e.g. to support - * + * * Returns nothing if the layer is empty and no travel move was ever made. */ std::optional> getFirstTravelDestinationState() const; /*! - * Set whether the next destination is inside a layer part or not. - * - * Features like infill, walls, skin etc. are considered inside. - * Features like prime tower and support are considered outside. - */ + * Set whether the next destination is inside a layer part or not. + * + * Features like infill, walls, skin etc. are considered inside. + * Features like prime tower and support are considered outside. + */ void setIsInside(bool is_inside); /*! @@ -438,9 +460,9 @@ class LayerPlan : public NoCopy /*! * Add a travel path to a certain point and retract if needed. - * + * * No combing is performed. - * + * * \param p The point to travel to * \param path (optional) The travel path to which to add the point \p p */ @@ -453,7 +475,7 @@ class LayerPlan : public NoCopy /*! * Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config. - * + * * \param p The point to extrude to * \param config The config with which to extrude * \param space_fill_type Of what space filling type this extrusion move is @@ -469,7 +491,15 @@ class LayerPlan : public NoCopy * forming one polygon.) * \param fan_speed Fan speed override for this path. */ - void addExtrusionMove(Point p, const GCodePathConfig& config, SpaceFillType space_fill_type, const Ratio& flow = 1.0_r, const Ratio width_factor = 1.0_r, bool spiralize = false, Ratio speed_factor = 1.0_r, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); + void addExtrusionMove( + Point p, + const GCodePathConfig& config, + SpaceFillType space_fill_type, + const Ratio& flow = 1.0_r, + const Ratio width_factor = 1.0_r, + bool spiralize = false, + Ratio speed_factor = 1.0_r, + double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); /*! * Add polygon to the gcode starting at vertex \p startIdx @@ -482,11 +512,19 @@ class LayerPlan : public NoCopy * \param flow_ratio The ratio with which to multiply the extrusion amount * \param always_retract Whether to force a retraction when moving to the start of the polygon (used for outer walls) */ - void addPolygon(ConstPolygonRef polygon, int startIdx, const bool reverse, const GCodePathConfig& config, coord_t wall_0_wipe_dist = 0, bool spiralize = false, const Ratio& flow_ratio = 1.0_r, bool always_retract = false); + void addPolygon( + ConstPolygonRef polygon, + int startIdx, + const bool reverse, + const GCodePathConfig& config, + coord_t wall_0_wipe_dist = 0, + bool spiralize = false, + const Ratio& flow_ratio = 1.0_r, + bool always_retract = false); /*! * Add polygons to the gcode with optimized order. - * + * * When \p spiralize is true, each polygon will gradually increase from a z * corresponding to this layer to the z corresponding to the next layer. * Doing this for each polygon means there is a chance for the print head to @@ -494,7 +532,7 @@ class LayerPlan : public NoCopy * would mean you are printing half of the layer in non-spiralize mode, * while each layer starts with a different part. Two towers would result in * alternating spiralize and non-spiralize layers. - * + * * \param polygons The polygons. * \param config The config with which to print the polygon lines. * for each given segment (optionally nullptr). @@ -512,7 +550,16 @@ class LayerPlan : public NoCopy * \param start_near_location Start optimising the path near this location. * If unset, this causes it to start near the last planned location. */ - void addPolygonsByOptimizer(const Polygons& polygons, const GCodePathConfig& config, const ZSeamConfig& z_seam_config = ZSeamConfig(), coord_t wall_0_wipe_dist = 0, bool spiralize = false, const Ratio flow_ratio = 1.0_r, bool always_retract = false, bool reverse_order = false, const std::optional start_near_location = std::optional()); + void addPolygonsByOptimizer( + const Polygons& polygons, + const GCodePathConfig& config, + const ZSeamConfig& z_seam_config = ZSeamConfig(), + coord_t wall_0_wipe_dist = 0, + bool spiralize = false, + const Ratio flow_ratio = 1.0_r, + bool always_retract = false, + bool reverse_order = false, + const std::optional start_near_location = std::optional()); /*! * Add a single line that is part of a wall to the gcode. @@ -534,7 +581,17 @@ class LayerPlan : public NoCopy * \param distance_to_bridge_start The distance along the wall from p0 to * the first bridge segment. */ - void addWallLine(const Point& p0, const Point& p1, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, float flow, const Ratio width_factor, float& non_bridge_line_volume, Ratio speed_factor, double distance_to_bridge_start); + void addWallLine( + const Point& p0, + const Point& p1, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + float flow, + const Ratio width_factor, + float& non_bridge_line_volume, + Ratio speed_factor, + double distance_to_bridge_start); /*! * Add a wall to the g-code starting at vertex \p start_idx @@ -551,7 +608,15 @@ class LayerPlan : public NoCopy * \param always_retract Whether to force a retraction when moving to the * start of the wall (used for outer walls). */ - void addWall(ConstPolygonRef wall, int start_idx, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, float flow_ratio, bool always_retract); + void addWall( + ConstPolygonRef wall, + int start_idx, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract); /*! * Add a wall to the g-code starting at vertex \p start_idx @@ -572,7 +637,18 @@ class LayerPlan : public NoCopy * \param is_reversed Whether to print this wall in reverse direction. * \param is_linked_path Whether the path is a continuation off the previous path */ - void addWall(const ExtrusionLine& wall, int start_idx, const Settings& settings, const GCodePathConfig& non_bridge_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, float flow_ratio, bool always_retract, const bool is_closed, const bool is_reversed, const bool is_linked_path); + void addWall( + const ExtrusionLine& wall, + int start_idx, + const Settings& settings, + const GCodePathConfig& non_bridge_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + float flow_ratio, + bool always_retract, + const bool is_closed, + const bool is_reversed, + const bool is_linked_path); /*! * Add an infill wall to the g-code @@ -594,8 +670,7 @@ class LayerPlan : public NoCopy * \param always_retract Whether to force a retraction when moving to the start of a wall (used for outer walls) * \param alternate_inset_direction_modifier Whether to alternate the direction of the walls for each inset. */ - void addWalls - ( + void addWalls( const Polygons& walls, const Settings& settings, const GCodePathConfig& non_bridge_config, @@ -603,8 +678,7 @@ class LayerPlan : public NoCopy const ZSeamConfig& z_seam_config = ZSeamConfig(), coord_t wall_0_wipe_dist = 0, float flow_ratio = 1.0, - bool always_retract = false - ); + bool always_retract = false); /*! * Add lines to the gcode with optimized order. @@ -619,7 +693,17 @@ class LayerPlan : public NoCopy * \param reverse_print_direction Whether to reverse the optimized order and their printing direction. * \param order_requirements Pairs where first needs to be printed before second. Pointers are pointing to elements of \p polygons */ - void addLinesByOptimizer(const Polygons& polygons, const GCodePathConfig& config, const SpaceFillType space_fill_type, const bool enable_travel_optimization = false, const coord_t wipe_dist = 0, const Ratio flow_ratio = 1.0, const std::optional near_start_location = std::optional(), const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, const bool reverse_print_direction = false, const std::unordered_multimap& order_requirements = PathOrderOptimizer::no_order_requirements); + void addLinesByOptimizer( + const Polygons& polygons, + const GCodePathConfig& config, + const SpaceFillType space_fill_type, + const bool enable_travel_optimization = false, + const coord_t wipe_dist = 0, + const Ratio flow_ratio = 1.0, + const std::optional near_start_location = std::optional(), + const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, + const bool reverse_print_direction = false, + const std::unordered_multimap& order_requirements = PathOrderOptimizer::no_order_requirements); /*! * Add polygons to the g-code with monotonic order. @@ -642,8 +726,7 @@ class LayerPlan : public NoCopy * \param flow_ratio The ratio with which to multiply the extrusion amount. * \param fan_speed Fan speed override for this path. */ - void addLinesMonotonic - ( + void addLinesMonotonic( const Polygons& area, const Polygons& polygons, const GCodePathConfig& config, @@ -653,8 +736,7 @@ class LayerPlan : public NoCopy const coord_t exclude_distance = 0, const coord_t wipe_dist = 0, const Ratio flow_ratio = 1.0_r, - const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT - ); + const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); protected: /*! @@ -672,8 +754,7 @@ class LayerPlan : public NoCopy const SpaceFillType space_fill_type, const coord_t wipe_dist, const Ratio flow_ratio, - const double fan_speed - ); + const double fan_speed); public: /*! @@ -689,7 +770,14 @@ class LayerPlan : public NoCopy * \param is_top_layer true when the top layer of the spiral is being printed * \param is_bottom_layer true when the bottom layer of the spiral is being printed */ - void spiralizeWallSlice(const GCodePathConfig& config, ConstPolygonRef wall, ConstPolygonRef last_wall, int seam_vertex_idx, int last_seam_vertex_idx, const bool is_top_layer, const bool is_bottom_layer); + void spiralizeWallSlice( + const GCodePathConfig& config, + ConstPolygonRef wall, + ConstPolygonRef last_wall, + int seam_vertex_idx, + int last_seam_vertex_idx, + const bool is_top_layer, + const bool is_bottom_layer); /*! @@ -716,7 +804,7 @@ class LayerPlan : public NoCopy while (true) { const Point& vertex = cura::make_point(wall[curr_idx]); - if (!air_below.inside(vertex, true)) + if (! air_below.inside(vertex, true)) { // vertex isn't above air so it's OK to use return curr_idx; @@ -737,7 +825,7 @@ class LayerPlan : public NoCopy /*! * Write the planned paths to gcode - * + * * \param gcode The gcode to write the planned paths to */ void writeGCode(GCodeExport& gcode); @@ -745,18 +833,18 @@ class LayerPlan : public NoCopy /*! * Whether the current retracted path is to be an extruder switch retraction. * This function is used to avoid a G10 S1 after a G10. - * + * * \param extruder_plan_idx The index of the current extruder plan - * \param path_idx The index of the current retracted path + * \param path_idx The index of the current retracted path * \return Whether the path should be an extruder switch retracted path */ bool makeRetractSwitchRetract(unsigned int extruder_plan_idx, unsigned int path_idx); - + /*! * Writes a path to GCode and performs coasting, or returns false if it did nothing. - * + * * Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines. - * + * * \param gcode The gcode to write the planned paths to. * \param extruder_plan_idx The index of the current extruder plan. * \param path_idx The index into LayerPlan::paths for the next path to be @@ -764,15 +852,20 @@ class LayerPlan : public NoCopy * \param layer_thickness The height of the current layer. * \return Whether any GCode has been written for the path. */ - bool writePathWithCoasting(GCodeExport& gcode, const size_t extruder_plan_idx, const size_t path_idx, const coord_t layer_thickness, const std::function insertTempOnTime); + bool writePathWithCoasting( + GCodeExport& gcode, + const size_t extruder_plan_idx, + const size_t path_idx, + const coord_t layer_thickness, + const std::function insertTempOnTime); /*! - * Applying speed corrections for minimal layer times and determine the fanSpeed. - * + * Applying speed corrections for minimal layer times and determine the fanSpeed. + * * \param starting_position The position of the print head when the first extruder plan of this layer starts */ void processFanSpeedAndMinimalLayerTime(Point starting_position); - + /*! * Add a travel move to the layer plan to move inside the current layer part * by a given distance away from the outline. @@ -796,7 +889,6 @@ class LayerPlan : public NoCopy void applyBackPressureCompensation(); private: - /*! * \brief Compute the preferred or minimum combing boundary * @@ -816,6 +908,6 @@ class LayerPlan : public NoCopy Polygons computeCombBoundary(const CombBoundary boundary_type); }; -}//namespace cura +} // namespace cura #endif // LAYER_PLAN_H diff --git a/src/ConicalOverhang.cpp b/src/ConicalOverhang.cpp index c5cd52b0b0..03b3b80222 100644 --- a/src/ConicalOverhang.cpp +++ b/src/ConicalOverhang.cpp @@ -1,13 +1,14 @@ -//Copyright (c) 2016 Tim Kuipers -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2016 Tim Kuipers +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "ConicalOverhang.h" + #include "mesh.h" -#include "utils/Simplify.h" //Simplifying at every step to prevent getting lots of vertices from all the insets. -#include "slicer.h" #include "settings/types/Angle.h" //To process the overhang angle. #include "settings/types/LayerIndex.h" +#include "slicer.h" +#include "utils/Simplify.h" //Simplifying at every step to prevent getting lots of vertices from all the insets. namespace cura { @@ -16,11 +17,11 @@ 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 double tan_angle = tan(angle); // the XY-component of the angle 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 - for(LayerIndex layer_nr = slicer->layers.size() - 2; static_cast(layer_nr) >= 0; layer_nr--) + for (LayerIndex layer_nr = slicer->layers.size() - 2; static_cast(layer_nr) >= 0; layer_nr--) { SlicerLayer& layer = slicer->layers[layer_nr]; SlicerLayer& layer_above = slicer->layers[layer_nr + 1]; @@ -31,7 +32,7 @@ void ConicalOverhang::apply(Slicer* slicer, const Mesh& mesh) Polygons diff = layer_above.polygons.difference(layer.polygons.offset(-safe_dist)); layer.polygons = layer.polygons.unionPolygons(diff); layer.polygons = layer.polygons.smooth(safe_dist); - layer.polygons = Simplify(safe_dist, safe_dist / 2, 0).polygon(layer.polygons); + layer.polygons = Simplify(safe_dist, safe_dist / 2, 0).polygon(layer.polygons); // somehow layer.polygons get really jagged lines with a lot of vertices // without the above steps slicing goes really slow } @@ -44,22 +45,22 @@ void ConicalOverhang::apply(Slicer* slicer, const Mesh& mesh) // Now go through all the holes in the current layer and check if they intersect anything in the layer above // If not, then they're the top of a hole and should be cut from the layer above before the union - for(unsigned int part = 0; part < layerParts.size(); part++) + for (unsigned int part = 0; part < layerParts.size(); part++) { - if(layerParts[part].size() > 1) // first poly is the outer contour, 1..n are the holes + if (layerParts[part].size() > 1) // first poly is the outer contour, 1..n are the holes { - for(unsigned int hole_nr = 1; hole_nr < layerParts[part].size(); ++hole_nr) + for (unsigned int hole_nr = 1; hole_nr < layerParts[part].size(); ++hole_nr) { Polygons holePoly; holePoly.add(layerParts[part][hole_nr]); if (maxHoleArea > 0.0 && INT2MM2(std::abs(holePoly.area())) < maxHoleArea) { Polygons holeWithAbove = holePoly.intersection(above); - if(!holeWithAbove.empty()) + if (! holeWithAbove.empty()) { // The hole had some intersection with the above layer, check if it's a complete overlap Polygons holeDifference = holePoly.xorPolygons(holeWithAbove); - if(holeDifference.empty()) + if (holeDifference.empty()) { // The hole was returned unchanged, so the layer above must completely cover it. Remove the hole from the layer above. above = above.difference(holePoly); @@ -69,10 +70,10 @@ void ConicalOverhang::apply(Slicer* slicer, const Mesh& mesh) } } } - // And now union with offset of the resulting above layer + // And now union with offset of the resulting above layer layer.polygons = layer.polygons.unionPolygons(above.offset(-max_dist_from_lower_layer)); } } } -}//namespace cura +} // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 27b349d77b..b7d545ff95 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2963,7 +2963,8 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { bool added_something = false; - const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; // account for negative layer numbers for raft filler layers + const SupportLayer& support_layer + = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; // account for negative layer numbers for raft filler layers if (gcode_layer.getLayerNr() > storage.support.layer_nr_max_filled_layer || support_layer.support_infill_parts.empty()) { diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 805b7e8bf4..39648aafcf 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -1,14 +1,14 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include + #include #include #include // ifstream.good() #include // multimap (ordered map allowing duplicate keys) #include -#include - // Code smell: Order of the includes is important here, probably due to some forward declarations which might be masking some undefined behaviours // clang-format off #include "Application.h" @@ -80,7 +80,9 @@ size_t FffPolygonGenerator::getDraftShieldLayerCount(const size_t total_layers) case DraftShieldHeightLimitation::FULL: return total_layers; case DraftShieldHeightLimitation::LIMITED: - return std::max((coord_t)0, (mesh_group_settings.get("draft_shield_height") - mesh_group_settings.get("layer_height_0")) / mesh_group_settings.get("layer_height") + 1); + return std::max( + (coord_t)0, + (mesh_group_settings.get("draft_shield_height") - mesh_group_settings.get("layer_height_0")) / mesh_group_settings.get("layer_height") + 1); default: spdlog::warn("A draft shield height limitation option was added without implementing the new option in getDraftShieldLayerCount."); return total_layers; @@ -128,9 +130,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe const auto variable_layer_height_max_variation = mesh_group_settings.get("adaptive_layer_height_variation"); const auto variable_layer_height_variation_step = mesh_group_settings.get("adaptive_layer_height_variation_step"); const auto adaptive_threshold = mesh_group_settings.get("adaptive_layer_height_threshold"); - adaptive_layer_heights = new AdaptiveLayerHeights(layer_thickness, variable_layer_height_max_variation, - variable_layer_height_variation_step, adaptive_threshold, - meshgroup); + adaptive_layer_heights + = new AdaptiveLayerHeights(layer_thickness, variable_layer_height_max_variation, variable_layer_height_variation_step, adaptive_threshold, meshgroup); // Get the amount of layers slice_layer_count = adaptive_layer_heights->getLayerCount(); @@ -174,7 +175,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe case SlicingTolerance::INCLUSIVE: if (mesh_height < initial_layer_thickness) { - slice_layer_count = std::max(slice_layer_count, (mesh_height > 0) ? 1 : 0); // If less than the initial layer height, it always has 1 layer unless the height is truly zero. + slice_layer_count + = std::max(slice_layer_count, (mesh_height > 0) ? 1 : 0); // If less than the initial layer height, it always has 1 layer unless the height is truly zero. } else { @@ -265,7 +267,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe } storage.support.supportLayers.resize(storage.print_layer_count); - storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated. + storage.meshes.reserve( + slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated. for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++) { Slicer* slicer = slicerList[meshIdx]; @@ -322,7 +325,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe if (has_raft) { const ExtruderTrain& train = mesh_group_settings.get("raft_surface_extruder_nr"); - layer.printZ += Raft::getTotalThickness() + train.settings.get("raft_airgap") - train.settings.get("layer_0_z_overlap"); // shift all layers (except 0) down + layer.printZ += Raft::getTotalThickness() + train.settings.get("raft_airgap") + - train.settings.get("layer_0_z_overlap"); // shift all layers (except 0) down if (layer_nr == 0) { @@ -386,7 +390,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // only remove empty layers if we haven't generate support, because then support was added underneath the model. // for some materials it's better to print on support than on the build plate. const auto has_support = mesh_group_settings.get("support_enable") || mesh_group_settings.get("support_mesh"); - const auto remove_empty_first_layers = mesh_group_settings.get("remove_empty_first_layers") && !has_support; + const auto remove_empty_first_layers = mesh_group_settings.get("remove_empty_first_layers") && ! has_support; if (remove_empty_first_layers) { removeEmptyFirstLayers(storage, storage.print_layer_count); // changes storage.print_layer_count! @@ -438,7 +442,11 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& AreaSupport::generateSupportInfillFeatures(storage); } -void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate) +void FffPolygonGenerator::processBasicWallsSkinInfill( + SliceDataStorage& storage, + const size_t mesh_order_idx, + const std::vector& mesh_order, + ProgressStageEstimator& inset_skin_progress_estimate) { size_t mesh_idx = mesh_order[mesh_order_idx]; SliceMeshStorage& mesh = storage.meshes[mesh_idx]; @@ -485,14 +493,15 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, } guarded_progress = { inset_skin_progress_estimate }; // walls - cura::parallel_for(0, - mesh_layer_count, - [&](size_t layer_number) - { - spdlog::debug("Processing insets for layer {} of {}", layer_number, mesh.layers.size()); - processWalls(mesh, layer_number); - guarded_progress++; - }); + cura::parallel_for( + 0, + mesh_layer_count, + [&](size_t layer_number) + { + spdlog::debug("Processing insets for layer {} of {}", layer_number, mesh.layers.size()); + processWalls(mesh, layer_number); + guarded_progress++; + }); ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count); mesh_inset_skin_progress_estimator->nextStage(skin_estimator); @@ -527,17 +536,18 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, } guarded_progress.reset(); - cura::parallel_for(0, - mesh_layer_count, - [&](size_t layer_number) - { - spdlog::debug("Processing skins and infill layer {} of {}", layer_number, mesh.layers.size()); - if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/downskin and infill for the first X layers when spiralize is choosen. - { - processSkinsAndInfill(mesh, layer_number, process_infill); - } - guarded_progress++; - }); + cura::parallel_for( + 0, + mesh_layer_count, + [&](size_t layer_number) + { + spdlog::debug("Processing skins and infill layer {} of {}", layer_number, mesh.layers.size()); + if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/downskin and infill for the first X layers when spiralize is choosen. + { + processSkinsAndInfill(mesh, layer_number, process_infill); + } + guarded_progress++; + }); } void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order) @@ -667,13 +677,18 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) } // Pre-compute Cross Fractal - if (mesh.settings.get("infill_line_distance") > 0 && (mesh.settings.get("infill_pattern") == EFillMethod::CROSS || mesh.settings.get("infill_pattern") == EFillMethod::CROSS_3D)) + if (mesh.settings.get("infill_line_distance") > 0 + && (mesh.settings.get("infill_pattern") == EFillMethod::CROSS || mesh.settings.get("infill_pattern") == EFillMethod::CROSS_3D)) { const std::string cross_subdivision_spec_image_file = mesh.settings.get("cross_infill_density_image"); std::ifstream cross_fs(cross_subdivision_spec_image_file.c_str()); if (! cross_subdivision_spec_image_file.empty() && cross_fs.good()) { - mesh.cross_fill_provider = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width"), cross_subdivision_spec_image_file); + mesh.cross_fill_provider = new SierpinskiFillProvider( + mesh.bounding_box, + mesh.settings.get("infill_line_distance"), + mesh.settings.get("infill_line_width"), + cross_subdivision_spec_image_file); } else { @@ -681,7 +696,8 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) { spdlog::error("Cannot find density image: {}.", cross_subdivision_spec_image_file); } - mesh.cross_fill_provider = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width")); + mesh.cross_fill_provider + = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width")); } } @@ -696,7 +712,7 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) SkinInfillAreaComputation::combineInfillLayers(mesh); // Fuzzy skin. Disabled when using interlocking structures, the internal interlocking walls become fuzzy. - if (mesh.settings.get("magic_fuzzy_skin_enabled") && !mesh.settings.get("interlocking_enable")) + if (mesh.settings.get("magic_fuzzy_skin_enabled") && ! mesh.settings.get("interlocking_enable")) { processFuzzyWalls(mesh); } @@ -847,11 +863,14 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage // Height of where the support reaches. Scene& scene = Application::getInstance().current_slice->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const size_t support_infill_extruder_nr = mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; // TODO: Support extruder should be configurable per object. + const size_t support_infill_extruder_nr + = mesh_group_settings.get("support_infill_extruder_nr").extruder_nr; // TODO: Support extruder should be configurable per object. max_print_height_per_extruder[support_infill_extruder_nr] = std::max(max_print_height_per_extruder[support_infill_extruder_nr], storage.support.layer_nr_max_filled_layer); - const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; // TODO: Support roof extruder should be configurable per object. + const size_t support_roof_extruder_nr + = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; // TODO: Support roof extruder should be configurable per object. max_print_height_per_extruder[support_roof_extruder_nr] = std::max(max_print_height_per_extruder[support_roof_extruder_nr], storage.support.layer_nr_max_filled_layer); - const size_t support_bottom_extruder_nr = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; // TODO: Support bottom extruder should be configurable per object. + const size_t support_bottom_extruder_nr + = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; // TODO: Support bottom extruder should be configurable per object. max_print_height_per_extruder[support_bottom_extruder_nr] = std::max(max_print_height_per_extruder[support_bottom_extruder_nr], storage.support.layer_nr_max_filled_layer); // Height of where the platform adhesion reaches. @@ -861,12 +880,12 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage case EPlatformAdhesion::SKIRT: case EPlatformAdhesion::BRIM: { - const std::vector skirt_brim_extruder_trains = mesh_group_settings.get>("skirt_brim_extruder_nr"); - for (ExtruderTrain* train : skirt_brim_extruder_trains) - { - const size_t skirt_brim_extruder_nr = train->extruder_nr; - max_print_height_per_extruder[skirt_brim_extruder_nr] = std::max(0, max_print_height_per_extruder[skirt_brim_extruder_nr]); // Includes layer 0. - } + const std::vector skirt_brim_extruder_trains = mesh_group_settings.get>("skirt_brim_extruder_nr"); + for (ExtruderTrain* train : skirt_brim_extruder_trains) + { + const size_t skirt_brim_extruder_nr = train->extruder_nr; + max_print_height_per_extruder[skirt_brim_extruder_nr] = std::max(0, max_print_height_per_extruder[skirt_brim_extruder_nr]); // Includes layer 0. + } break; } case EPlatformAdhesion::RAFT: @@ -874,9 +893,11 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage const size_t base_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; max_print_height_per_extruder[base_extruder_nr] = std::max(-raft_layers, max_print_height_per_extruder[base_extruder_nr]); // Includes the lowest raft layer. const size_t interface_extruder_nr = mesh_group_settings.get("raft_interface_extruder_nr").extruder_nr; - max_print_height_per_extruder[interface_extruder_nr] = std::max(-raft_layers + 1, max_print_height_per_extruder[interface_extruder_nr]); // Includes the second-lowest raft layer. + max_print_height_per_extruder[interface_extruder_nr] + = std::max(-raft_layers + 1, max_print_height_per_extruder[interface_extruder_nr]); // Includes the second-lowest raft layer. const size_t surface_extruder_nr = mesh_group_settings.get("raft_surface_extruder_nr").extruder_nr; - max_print_height_per_extruder[surface_extruder_nr] = std::max(-1, max_print_height_per_extruder[surface_extruder_nr]); // Includes up to the first layer below the model (so -1). + max_print_height_per_extruder[surface_extruder_nr] + = std::max(-1, max_print_height_per_extruder[surface_extruder_nr]); // Includes up to the first layer below the model (so -1). break; } default: @@ -917,7 +938,8 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) const AngleDegrees angle = mesh_group_settings.get("ooze_shield_angle"); if (angle <= 89) { - const coord_t allowed_angle_offset = tan(mesh_group_settings.get("ooze_shield_angle")) * mesh_group_settings.get("layer_height"); // Allow for a 60deg angle in the oozeShield. + const coord_t allowed_angle_offset + = tan(mesh_group_settings.get("ooze_shield_angle")) * mesh_group_settings.get("layer_height"); // Allow for a 60deg angle in the oozeShield. for (LayerIndex layer_nr = 1; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++) { storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr - 1].offset(-allowed_angle_offset)); @@ -941,7 +963,8 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) const auto& extruders = Application::getInstance().current_slice->scene.extruders; for (int extruder_nr = 0; extruder_nr < int(extruders.size()); extruder_nr++) { - if ( ! extruder_is_used[extruder_nr]) continue; + if (! extruder_is_used[extruder_nr]) + continue; max_line_width = std::max(max_line_width, extruders[extruder_nr].settings.get("skirt_brim_line_width")); } } @@ -992,7 +1015,8 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) const auto& extruders = Application::getInstance().current_slice->scene.extruders; for (int extruder_nr = 0; extruder_nr < int(extruders.size()); extruder_nr++) { - if ( ! extruder_is_used[extruder_nr]) continue; + if (! extruder_is_used[extruder_nr]) + continue; max_line_width = std::max(max_line_width, extruders[extruder_nr].settings.get("skirt_brim_line_width")); } } @@ -1047,10 +1071,14 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) const coord_t avg_dist_between_points = mesh.settings.get("magic_fuzzy_skin_point_dist"); const coord_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value const coord_t range_random_point_dist = avg_dist_between_points / 2; - unsigned int start_layer_nr = (mesh.settings.get("adhesion_type") == EPlatformAdhesion::BRIM) ? 1 : 0; // don't make fuzzy skin on first layer if there's a brim + unsigned int start_layer_nr + = (mesh.settings.get("adhesion_type") == EPlatformAdhesion::BRIM) ? 1 : 0; // don't make fuzzy skin on first layer if there's a brim auto hole_area = Polygons(); - std::function accumulate_is_in_hole = [](const bool& prev_result, const ExtrusionJunction& junction) { return false; }; + std::function accumulate_is_in_hole = [](const bool& prev_result, const ExtrusionJunction& junction) + { + return false; + }; for (LayerIndex layer_nr = start_layer_nr; layer_nr < mesh.layers.size(); layer_nr++) { @@ -1071,7 +1099,10 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) if (apply_outside_only) { hole_area = part.print_outline.getOutsidePolygons().offset(-line_width); - accumulate_is_in_hole = [&hole_area](const bool& prev_result, const ExtrusionJunction& junction) { return prev_result || hole_area.inside(junction.p); }; + accumulate_is_in_hole = [&hole_area](const bool& prev_result, const ExtrusionJunction& junction) + { + return prev_result || hole_area.inside(junction.p); + }; } for (auto& line : toolpath) { @@ -1087,7 +1118,8 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) result.is_closed = line.is_closed; // generate points in between p0 and p1 - int64_t dist_left_over = (min_dist_between_points / 4) + rand() % (min_dist_between_points / 4); // the distance to be traversed on the line before making the first new point + int64_t dist_left_over + = (min_dist_between_points / 4) + rand() % (min_dist_between_points / 4); // the distance to be traversed on the line before making the first new point auto* p0 = &line.front(); for (auto& p1 : line) { diff --git a/src/InterlockingGenerator.cpp b/src/InterlockingGenerator.cpp index 7a6a068a78..6310a7ef16 100644 --- a/src/InterlockingGenerator.cpp +++ b/src/InterlockingGenerator.cpp @@ -1,22 +1,22 @@ -//Copyright (c) 2023 UltiMaker -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "InterlockingGenerator.h" -#include // max - #include "Application.h" #include "Slice.h" -#include "slicer.h" -#include "utils/polygonUtils.h" #include "settings/types/LayerIndex.h" +#include "slicer.h" #include "utils/VoxelUtils.h" +#include "utils/polygonUtils.h" #include #include #include #include +#include // max + namespace cura { @@ -44,7 +44,7 @@ void InterlockingGenerator::generateInterlockingStructure(std::vector& continue; } - if (extruder_nr_a == extruder_nr_b || !mesh_a.mesh->getAABB().expand(ignored_gap).hit(mesh_b.mesh->getAABB())) + if (extruder_nr_a == extruder_nr_b || ! mesh_a.mesh->getAABB().expand(ignored_gap).hit(mesh_b.mesh->getAABB())) { // early out for when meshes don't share any overlap in their bounding box continue; @@ -86,7 +86,7 @@ std::pair InterlockingGenerator::growBorderAreasPerpendicula from_border_b = temp_b.difference(temp_a); } - return { from_border_a, from_border_b}; + return { from_border_a, from_border_b }; } void InterlockingGenerator::handleThinAreas(const std::unordered_set& has_all_meshes) const @@ -133,8 +133,12 @@ void InterlockingGenerator::handleThinAreas(const std::unordered_set const Polygons large_b{ polys_b.offset(-detect).offset(detect) }; // Derive the area that the thin areas need to expand into (so the added areas to the thin strips) from the information we already have. - const Polygons thin_expansion_a{ large_b.intersection(polys_a.difference(large_a).offset(expand)).intersection(near_interlock_per_layer[layer_nr]).intersection(from_border_a).offset(rounding_errors) }; - const Polygons thin_expansion_b{ large_a.intersection(polys_b.difference(large_b).offset(expand)).intersection(near_interlock_per_layer[layer_nr]).intersection(from_border_b).offset(rounding_errors) }; + const Polygons thin_expansion_a{ + large_b.intersection(polys_a.difference(large_a).offset(expand)).intersection(near_interlock_per_layer[layer_nr]).intersection(from_border_a).offset(rounding_errors) + }; + const Polygons thin_expansion_b{ + large_a.intersection(polys_b.difference(large_b).offset(expand)).intersection(near_interlock_per_layer[layer_nr]).intersection(from_border_b).offset(rounding_errors) + }; // Expanded thin areas of the opposing polygon should 'eat into' the larger areas of the polygon, // and conversely, add the expansions to their own thin areas. @@ -176,9 +180,9 @@ std::vector> InterlockingGenerator::getShellVoxel // mark all cells which contain some boundary for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++) { - Slicer* mesh = (mesh_idx == 0)? &mesh_a : &mesh_b; + Slicer* mesh = (mesh_idx == 0) ? &mesh_a : &mesh_b; std::unordered_set& mesh_voxels = voxels_per_mesh[mesh_idx]; - + std::vector rotated_polygons_per_layer(mesh->layers.size()); for (size_t layer_nr = 0; layer_nr < mesh->layers.size(); layer_nr++) { @@ -186,7 +190,7 @@ std::vector> InterlockingGenerator::getShellVoxel rotated_polygons_per_layer[layer_nr] = layer.polygons; rotated_polygons_per_layer[layer_nr].applyMatrix(rotation); } - + addBoundaryCells(rotated_polygons_per_layer, kernel, mesh_voxels); } @@ -195,7 +199,11 @@ std::vector> InterlockingGenerator::getShellVoxel void InterlockingGenerator::addBoundaryCells(const std::vector& layers, const DilationKernel& kernel, std::unordered_set& cells) const { - auto voxel_emplacer = [&cells](GridPoint3 p) { cells.emplace(p); return true; }; + auto voxel_emplacer = [&cells](GridPoint3 p) + { + cells.emplace(p); + return true; + }; for (size_t layer_nr = 0; layer_nr < layers.size(); layer_nr++) { @@ -219,7 +227,7 @@ std::vector InterlockingGenerator::computeUnionedVolumeRegions() const for (LayerIndex layer_nr = 0; layer_nr < max_layer_count; layer_nr++) { Polygons& layer_region = layer_regions[layer_nr]; - for (Slicer* mesh : {&mesh_a, &mesh_b}) + for (Slicer* mesh : { &mesh_a, &mesh_b }) { if (layer_nr >= mesh->layers.size()) { @@ -242,9 +250,9 @@ std::vector> InterlockingGenerator::generateMicrostructure const coord_t beam_w_sum = beam_width_a + beam_width_b; const coord_t middle = cell_size.x * beam_width_a / beam_w_sum; const coord_t width[2] = { middle, cell_size.x - middle }; - for (size_t mesh_idx : {0, 1}) + for (size_t mesh_idx : { 0, 1 }) { - Point offset(mesh_idx? middle : 0, 0); + Point offset(mesh_idx ? middle : 0, 0); Point area_size(width[mesh_idx], cell_size.y); PolygonRef poly = cell_area_per_mesh_per_layer[0][mesh_idx].newPoly(); @@ -310,7 +318,7 @@ void InterlockingGenerator::applyMicrostructureToOutlines(const std::unordered_s for (size_t mesh_idx = 0; mesh_idx < 2; mesh_idx++) { - Slicer* mesh = (mesh_idx == 0)? &mesh_a : &mesh_b; + Slicer* mesh = (mesh_idx == 0) ? &mesh_a : &mesh_b; for (size_t layer_nr = 0; layer_nr < max_layer_count; layer_nr++) { if (layer_nr >= mesh->layers.size()) @@ -322,13 +330,14 @@ void InterlockingGenerator::applyMicrostructureToOutlines(const std::unordered_s layer_outlines.applyMatrix(unapply_rotation); const Polygons areas_here = structure_per_layer[mesh_idx][layer_nr / beam_layer_count].intersection(layer_outlines); - const Polygons& areas_other = structure_per_layer[ ! mesh_idx][layer_nr / beam_layer_count]; + const Polygons& areas_other = structure_per_layer[! mesh_idx][layer_nr / beam_layer_count]; SlicerLayer& layer = mesh->layers[layer_nr]; - layer.polygons = layer.polygons.difference(areas_other) // reduce layer areas inward with beams from other mesh - .unionPolygons(areas_here); // extend layer areas outward with newly added beams + layer.polygons = layer.polygons + .difference(areas_other) // reduce layer areas inward with beams from other mesh + .unionPolygons(areas_here); // extend layer areas outward with newly added beams } } } -}//namespace cura +} // namespace cura diff --git a/src/Mold.cpp b/src/Mold.cpp index 6816d684d7..bd5ff2e0b1 100644 --- a/src/Mold.cpp +++ b/src/Mold.cpp @@ -1,14 +1,15 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "Mold.h" #include "Application.h" //To get settings. #include "ExtruderTrain.h" -#include "Mold.h" #include "Scene.h" #include "Slice.h" +#include "settings/types/Ratio.h" #include "sliceDataStorage.h" #include "slicer.h" -#include "settings/types/Ratio.h" #include "utils/IntPoint.h" namespace cura @@ -28,7 +29,7 @@ void Mold::process(std::vector& slicer_list) mesh.expandXY(mesh.settings.get("mold_width")); } } - if (!has_any_mold) + if (! has_any_mold) { return; } @@ -56,7 +57,7 @@ void Mold::process(std::vector& slicer_list) { const Mesh& mesh = scene.current_mesh_group->meshes[mesh_idx]; Slicer& slicer = *slicer_list[mesh_idx]; - if (!mesh.settings.get("mold_enabled") || layer_nr >= static_cast(slicer.layers.size())) + if (! mesh.settings.get("mold_enabled") || layer_nr >= static_cast(slicer.layers.size())) { continue; } @@ -107,7 +108,7 @@ void Mold::process(std::vector& slicer_list) for (unsigned int mesh_idx = 0; mesh_idx < slicer_list.size(); mesh_idx++) { const Mesh& mesh = scene.current_mesh_group->meshes[mesh_idx]; - if (!mesh.settings.get("mold_enabled")) + if (! mesh.settings.get("mold_enabled")) { continue; // only cut original models out of all molds } @@ -116,8 +117,7 @@ void Mold::process(std::vector& slicer_list) layer.polygons = layer.polygons.difference(all_original_mold_outlines); } } - } -}//namespace cura +} // namespace cura diff --git a/src/multiVolumes.cpp b/src/multiVolumes.cpp index cf42eb3f96..8241d777e9 100644 --- a/src/multiVolumes.cpp +++ b/src/multiVolumes.cpp @@ -1,53 +1,49 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "multiVolumes.h" -#include - #include "Application.h" #include "Slice.h" -#include "slicer.h" -#include "utils/PolylineStitcher.h" #include "settings/EnumSettings.h" #include "settings/types/LayerIndex.h" +#include "slicer.h" +#include "utils/PolylineStitcher.h" -namespace cura +#include + +namespace cura { - -void carveMultipleVolumes(std::vector &volumes) + +void carveMultipleVolumes(std::vector& volumes) { - //Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas. + // Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas. const bool alternate_carve_order = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("alternate_carve_order"); std::vector ranked_volumes = volumes; - std::sort(ranked_volumes.begin(), ranked_volumes.end(), - [](Slicer* volume_1, Slicer* volume_2) - { - return volume_1->mesh->settings.get("infill_mesh_order") < volume_2->mesh->settings.get("infill_mesh_order"); - } ); + std::sort( + ranked_volumes.begin(), + ranked_volumes.end(), + [](Slicer* volume_1, Slicer* volume_2) + { + return volume_1->mesh->settings.get("infill_mesh_order") < volume_2->mesh->settings.get("infill_mesh_order"); + }); for (unsigned int volume_1_idx = 1; volume_1_idx < volumes.size(); volume_1_idx++) { Slicer& volume_1 = *ranked_volumes[volume_1_idx]; - if (volume_1.mesh->settings.get("infill_mesh") - || volume_1.mesh->settings.get("anti_overhang_mesh") - || volume_1.mesh->settings.get("support_mesh") - || volume_1.mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE - ) + if (volume_1.mesh->settings.get("infill_mesh") || volume_1.mesh->settings.get("anti_overhang_mesh") || volume_1.mesh->settings.get("support_mesh") + || volume_1.mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; } for (unsigned int volume_2_idx = 0; volume_2_idx < volume_1_idx; volume_2_idx++) { Slicer& volume_2 = *ranked_volumes[volume_2_idx]; - if (volume_2.mesh->settings.get("infill_mesh") - || volume_2.mesh->settings.get("anti_overhang_mesh") - || volume_2.mesh->settings.get("support_mesh") - || volume_2.mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE - ) + if (volume_2.mesh->settings.get("infill_mesh") || volume_2.mesh->settings.get("anti_overhang_mesh") || volume_2.mesh->settings.get("support_mesh") + || volume_2.mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; } - if (!volume_1.mesh->getAABB().hit(volume_2.mesh->getAABB())) + if (! volume_1.mesh->getAABB().hit(volume_2.mesh->getAABB())) { continue; } @@ -67,10 +63,10 @@ void carveMultipleVolumes(std::vector &volumes) } } } - -//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes. -//This generates some overlap in dual extrusion, for better bonding in touching parts. -void generateMultipleVolumesOverlap(std::vector &volumes) + +// Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes. +// This generates some overlap in dual extrusion, for better bonding in touching parts. +void generateMultipleVolumesOverlap(std::vector& volumes) { if (volumes.size() < 2) { @@ -83,9 +79,7 @@ void generateMultipleVolumesOverlap(std::vector &volumes) ClipperLib::PolyFillType fill_type = volume->mesh->settings.get("meshfix_union_all") ? ClipperLib::pftNonZero : ClipperLib::pftEvenOdd; coord_t overlap = volume->mesh->settings.get("multiple_mesh_overlap"); - if (volume->mesh->settings.get("infill_mesh") - || volume->mesh->settings.get("anti_overhang_mesh") - || volume->mesh->settings.get("support_mesh") + if (volume->mesh->settings.get("infill_mesh") || volume->mesh->settings.get("anti_overhang_mesh") || volume->mesh->settings.get("support_mesh") || overlap == 0) { continue; @@ -97,12 +91,8 @@ void generateMultipleVolumesOverlap(std::vector &volumes) Polygons all_other_volumes; for (Slicer* other_volume : volumes) { - if (other_volume->mesh->settings.get("infill_mesh") - || other_volume->mesh->settings.get("anti_overhang_mesh") - || other_volume->mesh->settings.get("support_mesh") - || !other_volume->mesh->getAABB().hit(aabb) - || other_volume == volume - ) + if (other_volume->mesh->settings.get("infill_mesh") || other_volume->mesh->settings.get("anti_overhang_mesh") + || other_volume->mesh->settings.get("support_mesh") || ! other_volume->mesh->getAABB().hit(aabb) || other_volume == volume) { continue; } @@ -121,7 +111,7 @@ void MultiVolumes::carveCuttingMeshes(std::vector& volumes, const std:: for (unsigned int carving_mesh_idx = 0; carving_mesh_idx < volumes.size(); carving_mesh_idx++) { const Mesh& cutting_mesh = meshes[carving_mesh_idx]; - if (!cutting_mesh.settings.get("cutting_mesh")) + if (! cutting_mesh.settings.get("cutting_mesh")) { continue; } @@ -157,15 +147,14 @@ void MultiVolumes::carveCuttingMeshes(std::vector& volumes, const std:: cutting_mesh_area = &cutting_mesh_polygons; } } - + Polygons new_outlines; Polygons new_polylines; for (unsigned int carved_mesh_idx = 0; carved_mesh_idx < volumes.size(); carved_mesh_idx++) { const Mesh& carved_mesh = meshes[carved_mesh_idx]; - //Do not apply cutting_mesh for meshes which have settings (cutting_mesh, anti_overhang_mesh, support_mesh). - if (carved_mesh.settings.get("cutting_mesh") || carved_mesh.settings.get("anti_overhang_mesh") - || carved_mesh.settings.get("support_mesh")) + // Do not apply cutting_mesh for meshes which have settings (cutting_mesh, anti_overhang_mesh, support_mesh). + if (carved_mesh.settings.get("cutting_mesh") || carved_mesh.settings.get("anti_overhang_mesh") || carved_mesh.settings.get("support_mesh")) { continue; } @@ -192,4 +181,4 @@ void MultiVolumes::carveCuttingMeshes(std::vector& volumes, const std:: } -}//namespace cura +} // namespace cura diff --git a/src/slicer.cpp b/src/slicer.cpp index 41365a19e8..3462573d88 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -1,25 +1,26 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include // remove_if -#include -#include - -#include -#include +#include "slicer.h" #include "Application.h" #include "Slice.h" #include "settings/AdaptiveLayerHeights.h" #include "settings/EnumSettings.h" #include "settings/types/LayerIndex.h" -#include "slicer.h" #include "utils/Simplify.h" #include "utils/SparsePointGridInclusive.h" #include "utils/ThreadPool.h" #include "utils/gettime.h" #include "utils/section_type.h" +#include +#include + +#include // remove_if +#include +#include + namespace cura { @@ -407,7 +408,8 @@ void SlicerLayer::joinPolylines(PolygonRef& polyline_0, PolygonRef& polyline_1, polyline_1.clear(); } -SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) : m_terminus_old_to_cur_map(end_idx) +SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) + : m_terminus_old_to_cur_map(end_idx) { // Initialize map to everything points to itself since nothing has moved yet. for (size_t idx = 0U; idx != end_idx; ++idx) @@ -417,7 +419,12 @@ SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) : m_terminus_cur_to_old_map = m_terminus_old_to_cur_map; } -void SlicerLayer::TerminusTrackingMap::updateMap(size_t num_terms, const Terminus* cur_terms, const Terminus* next_terms, size_t num_removed_terms, const Terminus* removed_cur_terms) +void SlicerLayer::TerminusTrackingMap::updateMap( + size_t num_terms, + const Terminus* cur_terms, + const Terminus* next_terms, + size_t num_removed_terms, + const Terminus* removed_cur_terms) { // save old locations std::vector old_terms(num_terms); @@ -736,10 +743,12 @@ void SlicerLayer::makePolygons(const Mesh* mesh) connectOpenPolylines(open_polylines); - // TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in the middle between the two open polygons. + // TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in + // the middle between the two open polygons. if (mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::NORMAL) - { // don't stitch when using (any) mesh surface mode, i.e. also don't stitch when using mixed mesh surface and closed polygons, because then polylines which are supposed to be open will be closed + { // don't stitch when using (any) mesh surface mode, i.e. also don't stitch when using mixed mesh surface and closed polygons, because then polylines which are supposed to be + // open will be closed stitch(open_polylines); } @@ -767,7 +776,13 @@ void SlicerLayer::makePolygons(const Mesh* mesh) // Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print. const coord_t snap_distance = std::max(mesh->settings.get("minimum_polygon_circumference"), static_cast(1)); - auto it = std::remove_if(polygons.begin(), polygons.end(), [snap_distance](PolygonRef poly) { return poly.shorterThan(snap_distance); }); + auto it = std::remove_if( + polygons.begin(), + polygons.end(), + [snap_distance](PolygonRef poly) + { + return poly.shorterThan(snap_distance); + }); polygons.erase(it, polygons.end()); // Finally optimize all the polygons. Every point removed saves time in the long run. @@ -775,13 +790,20 @@ void SlicerLayer::makePolygons(const Mesh* mesh) polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments // Clean up polylines for Surface Mode printing - it = std::remove_if(openPolylines.begin(), openPolylines.end(), [snap_distance](PolygonRef poly) { return poly.shorterThan(snap_distance); }); + it = std::remove_if( + openPolylines.begin(), + openPolylines.end(), + [snap_distance](PolygonRef poly) + { + return poly.shorterThan(snap_distance); + }); openPolylines.erase(it, openPolylines.end()); openPolylines.removeDegenerateVertsPolyline(); } -Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_count, bool use_variable_layer_heights, std::vector* adaptive_layers) : mesh(i_mesh) +Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_count, bool use_variable_layer_heights, std::vector* adaptive_layers) + : mesh(i_mesh) { const SlicingTolerance slicing_tolerance = mesh->settings.get("slicing_tolerance"); const coord_t initial_layer_thickness = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height_0"); @@ -806,137 +828,143 @@ Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_c void Slicer::buildSegments(const Mesh& mesh, const std::vector>& zbbox, const SlicingTolerance& slicing_tolerance, std::vector& layers) { - cura::parallel_for(layers, - [&](auto layer_it) - { - SlicerLayer& layer = *layer_it; - const int32_t& z = layer.z; - layer.segments.reserve(100); - - // loop over all mesh faces - for (unsigned int mesh_idx = 0; mesh_idx < mesh.faces.size(); mesh_idx++) - { - if ((z < zbbox[mesh_idx].first) || (z > zbbox[mesh_idx].second)) - { - continue; - } - - // get all vertices per face - const MeshFace& face = mesh.faces[mesh_idx]; - const MeshVertex& v0 = mesh.vertices[face.vertex_index[0]]; - const MeshVertex& v1 = mesh.vertices[face.vertex_index[1]]; - const MeshVertex& v2 = mesh.vertices[face.vertex_index[2]]; - - // get all vertices represented as 3D point - Point3 p0 = v0.p; - Point3 p1 = v1.p; - Point3 p2 = v2.p; - - // Compensate for points exactly on the slice-boundary, except for 'inclusive', which already handles this correctly. - if (slicing_tolerance != SlicingTolerance::INCLUSIVE) - { - p0.z += static_cast(p0.z == z) * -static_cast(p0.z < 1); - p1.z += static_cast(p1.z == z) * -static_cast(p1.z < 1); - p2.z += static_cast(p2.z == z) * -static_cast(p2.z < 1); - } - - SlicerSegment s; - s.endVertex = nullptr; - int end_edge_idx = -1; - - /* - Now see if the triangle intersects the layer, and if so, where. - - Edge cases are important here: - - If all three vertices of the triangle are exactly on the layer, - don't count the triangle at all, because if the model is - watertight, there will be adjacent triangles on all 3 sides that - are not flat on the layer. - - If two of the vertices are exactly on the layer, only count the - triangle if the last vertex is going up. We can't count both - upwards and downwards triangles here, because if the model is - manifold there will always be an adjacent triangle that is going - the other way and you'd get double edges. You would also get one - layer too many if the total model height is an exact multiple of - the layer thickness. Between going up and going down, we need to - choose the triangles going up, because otherwise the first layer - of where the model starts will be empty and the model will float - in mid-air. We'd much rather let the last layer be empty in that - case. - - If only one of the vertices is exactly on the layer, the - intersection between the triangle and the plane would be a point. - We can't print points and with a manifold model there would be - line segments adjacent to the point on both sides anyway, so we - need to discard this 0-length line segment then. - - Vertices in ccw order if look from outside. - */ - - if (p0.z < z && p1.z > z && p2.z > z) // 1_______2 - { // \ / - s = project2D(p0, p2, p1, z); //------------- z - end_edge_idx = 0; // \ / - } // 0 - - else if (p0.z > z && p1.z <= z && p2.z <= z) // 0 - { // / \ . - s = project2D(p0, p1, p2, z); //------------- z - end_edge_idx = 2; // / \ . - if (p2.z == z) // 1_______2 - { - s.endVertex = &v2; - } - } - - else if (p1.z < z && p0.z > z && p2.z > z) // 0_______2 - { // \ / - s = project2D(p1, p0, p2, z); //------------- z - end_edge_idx = 1; // \ / - } // 1 - - else if (p1.z > z && p0.z <= z && p2.z <= z) // 1 - { // / \ . - s = project2D(p1, p2, p0, z); //------------- z - end_edge_idx = 0; // / \ . - if (p0.z == z) // 0_______2 - { - s.endVertex = &v0; - } - } - - else if (p2.z < z && p1.z > z && p0.z > z) // 0_______1 - { // \ / - s = project2D(p2, p1, p0, z); //------------- z - end_edge_idx = 2; // \ / - } // 2 - - else if (p2.z > z && p1.z <= z && p0.z <= z) // 2 - { // / \ . - s = project2D(p2, p0, p1, z); //------------- z - end_edge_idx = 1; // / \ . - if (p1.z == z) // 0_______1 - { - s.endVertex = &v1; - } - } - else - { - // Not all cases create a segment, because a point of a face could create just a dot, and two touching faces - // on the slice would create two segments - continue; - } - - // store the segments per layer - layer.face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layer.segments.size())); - s.faceIndex = mesh_idx; - s.endOtherFaceIdx = face.connected_face_index[end_edge_idx]; - s.addedToPolygon = false; - layer.segments.push_back(s); - } - }); + cura::parallel_for( + layers, + [&](auto layer_it) + { + SlicerLayer& layer = *layer_it; + const int32_t& z = layer.z; + layer.segments.reserve(100); + + // loop over all mesh faces + for (unsigned int mesh_idx = 0; mesh_idx < mesh.faces.size(); mesh_idx++) + { + if ((z < zbbox[mesh_idx].first) || (z > zbbox[mesh_idx].second)) + { + continue; + } + + // get all vertices per face + const MeshFace& face = mesh.faces[mesh_idx]; + const MeshVertex& v0 = mesh.vertices[face.vertex_index[0]]; + const MeshVertex& v1 = mesh.vertices[face.vertex_index[1]]; + const MeshVertex& v2 = mesh.vertices[face.vertex_index[2]]; + + // get all vertices represented as 3D point + Point3 p0 = v0.p; + Point3 p1 = v1.p; + Point3 p2 = v2.p; + + // Compensate for points exactly on the slice-boundary, except for 'inclusive', which already handles this correctly. + if (slicing_tolerance != SlicingTolerance::INCLUSIVE) + { + p0.z += static_cast(p0.z == z) * -static_cast(p0.z < 1); + p1.z += static_cast(p1.z == z) * -static_cast(p1.z < 1); + p2.z += static_cast(p2.z == z) * -static_cast(p2.z < 1); + } + + SlicerSegment s; + s.endVertex = nullptr; + int end_edge_idx = -1; + + /* + Now see if the triangle intersects the layer, and if so, where. + + Edge cases are important here: + - If all three vertices of the triangle are exactly on the layer, + don't count the triangle at all, because if the model is + watertight, there will be adjacent triangles on all 3 sides that + are not flat on the layer. + - If two of the vertices are exactly on the layer, only count the + triangle if the last vertex is going up. We can't count both + upwards and downwards triangles here, because if the model is + manifold there will always be an adjacent triangle that is going + the other way and you'd get double edges. You would also get one + layer too many if the total model height is an exact multiple of + the layer thickness. Between going up and going down, we need to + choose the triangles going up, because otherwise the first layer + of where the model starts will be empty and the model will float + in mid-air. We'd much rather let the last layer be empty in that + case. + - If only one of the vertices is exactly on the layer, the + intersection between the triangle and the plane would be a point. + We can't print points and with a manifold model there would be + line segments adjacent to the point on both sides anyway, so we + need to discard this 0-length line segment then. + - Vertices in ccw order if look from outside. + */ + + if (p0.z < z && p1.z > z && p2.z > z) // 1_______2 + { // \ / + s = project2D(p0, p2, p1, z); //------------- z + end_edge_idx = 0; // \ / + } // 0 + + else if (p0.z > z && p1.z <= z && p2.z <= z) // 0 + { // / \ . + s = project2D(p0, p1, p2, z); //------------- z + end_edge_idx = 2; // / \ . + if (p2.z == z) // 1_______2 + { + s.endVertex = &v2; + } + } + + else if (p1.z < z && p0.z > z && p2.z > z) // 0_______2 + { // \ / + s = project2D(p1, p0, p2, z); //------------- z + end_edge_idx = 1; // \ / + } // 1 + + else if (p1.z > z && p0.z <= z && p2.z <= z) // 1 + { // / \ . + s = project2D(p1, p2, p0, z); //------------- z + end_edge_idx = 0; // / \ . + if (p0.z == z) // 0_______2 + { + s.endVertex = &v0; + } + } + + else if (p2.z < z && p1.z > z && p0.z > z) // 0_______1 + { // \ / + s = project2D(p2, p1, p0, z); //------------- z + end_edge_idx = 2; // \ / + } // 2 + + else if (p2.z > z && p1.z <= z && p0.z <= z) // 2 + { // / \ . + s = project2D(p2, p0, p1, z); //------------- z + end_edge_idx = 1; // / \ . + if (p1.z == z) // 0_______1 + { + s.endVertex = &v1; + } + } + else + { + // Not all cases create a segment, because a point of a face could create just a dot, and two touching faces + // on the slice would create two segments + continue; + } + + // store the segments per layer + layer.face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layer.segments.size())); + s.faceIndex = mesh_idx; + s.endOtherFaceIdx = face.connected_face_index[end_edge_idx]; + s.addedToPolygon = false; + layer.segments.push_back(s); + } + }); } -std::vector - Slicer::buildLayersWithHeight(size_t slice_layer_count, SlicingTolerance slicing_tolerance, coord_t initial_layer_thickness, coord_t thickness, bool use_variable_layer_heights, const std::vector* adaptive_layers) +std::vector Slicer::buildLayersWithHeight( + size_t slice_layer_count, + SlicingTolerance slicing_tolerance, + coord_t initial_layer_thickness, + coord_t thickness, + bool use_variable_layer_heights, + const std::vector* adaptive_layers) { std::vector layers_res; @@ -973,7 +1001,12 @@ std::vector void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::vector& layers) { - cura::parallel_for(layers, [&mesh](auto layer_it) { layer_it->makePolygons(&mesh); }); + cura::parallel_for( + layers, + [&mesh](auto layer_it) + { + layer_it->makePolygons(&mesh); + }); switch (slicing_tolerance) { @@ -997,8 +1030,8 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v } size_t layer_apply_initial_xy_offset = 0; - if (layers.size() > 0 && layers[0].polygons.size() == 0 && ! mesh.settings.get("support_mesh") && ! mesh.settings.get("anti_overhang_mesh") && ! mesh.settings.get("cutting_mesh") - && ! mesh.settings.get("infill_mesh")) + if (layers.size() > 0 && layers[0].polygons.size() == 0 && ! mesh.settings.get("support_mesh") && ! mesh.settings.get("anti_overhang_mesh") + && ! mesh.settings.get("cutting_mesh") && ! mesh.settings.get("infill_mesh")) { layer_apply_initial_xy_offset = 1; } @@ -1011,8 +1044,7 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v const auto max_hole_area = std::numbers::pi / 4 * static_cast(hole_offset_max_diameter * hole_offset_max_diameter); - cura::parallel_for - ( + cura::parallel_for( 0, layers.size(), [&layers, layer_apply_initial_xy_offset, xy_offset, xy_offset_0, xy_offset_hole, hole_offset_max_diameter, max_hole_area](size_t layer_nr) @@ -1061,8 +1093,7 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v layers[layer_nr].polygons.add(outline.difference(holes.unionPolygons())); } } - } - ); + }); mesh.expandXY(xy_offset); } From 6650e8021a01c9203fd2dbd052d3b9a25eaf1f19 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 11:49:52 +0200 Subject: [PATCH 379/656] Refactor broadcast and invoke components, upgrade asio-grpc This commit refactors broadcast and invoke components to remove repetitive code and increase maintainability. It introduces 'broadcast_stub' and 'broadcast_rpc' structs to replace the 'broadcast_factory' and 'broadcast_message_factory' functions, improving clarity. Additionally, the 'agrpc::RPC' references have been updated to 'agrpc::ClientRPC' for better specificity. The asio-grpc library has also been upgraded from 2.4.0 to 2.6.0 to benefit from the latest features and improvements. The grpc backend option for asio-grpc is also set to 'boost' instead of the default value. Contribute to CURA-10619 --- conanfile.py | 3 ++- include/plugins/broadcasts.h | 29 ++++++++++++++++++-------- include/plugins/components/broadcast.h | 13 ++++++------ include/plugins/components/invoke.h | 3 ++- include/plugins/pluginproxy.h | 4 +++- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/conanfile.py b/conanfile.py index b65c67a704..e6b63b137e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -71,6 +71,7 @@ def configure(self): self.options["grpc"].php_plugin = False self.options["grpc"].python_plugin = False self.options["grpc"].ruby_plugin = False + self.options["asio-grpc"].backend = "boost" self.options["asio-grpc"].local_allocator = "recycling_allocator" if self.options.enable_arcus: self.options["arcus"].shared = True @@ -105,7 +106,7 @@ def requirements(self): self.requires("protobuf/3.21.9") self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") - self.requires("asio-grpc/2.4.0") + self.requires("asio-grpc/2.6.0") self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") def generate(self): diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h index dd0ab746f8..4b19477ccc 100644 --- a/include/plugins/broadcasts.h +++ b/include/plugins/broadcasts.h @@ -5,6 +5,7 @@ #define PLUGINS_BROADCAST_H #include "cura/plugins/v0/slot_id.pb.h" +#include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "plugins/converters.h" #include @@ -23,20 +24,30 @@ struct is_broadcast_channel template inline constexpr bool is_broadcast_channel_v = is_broadcast_channel::value; -template -requires is_broadcast_channel_v -constexpr auto broadcast_message_factory(auto&&... args) +template +struct broadcast_stub { - return broadcast_settings_request{}(std::forward(args)...); -}; + using stub_type = slots::broadcast::v0::BroadcastService::Stub; + using derived_type = T; + friend derived_type; + + constexpr auto operator()(auto&&... args) + { + return request_(std::forward(args)...); + } +private: + C request_{}; +}; -template +template requires is_broadcast_channel_v -constexpr auto broadcast_factory() +struct broadcast_rpc : public broadcast_stub, broadcast_settings_request> { - return agrpc::RPC<&Stub::PrepareAsyncBroadcastSettings>{}; -} + using base_type = broadcast_stub>; + using ClientRPC = agrpc::ClientRPC<&base_type::stub_type::PrepareAsyncBroadcastSettings>; +}; + } // namespace cura::plugins::details diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index 57720a61cc..76214a7929 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -14,7 +14,9 @@ #include "utils/types/generic.h" #include +#include #include +#include #include #include #include @@ -30,8 +32,6 @@ namespace cura::plugins template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. class PluginProxyBroadcastComponent { - using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; - public: constexpr PluginProxyBroadcastComponent() = default; @@ -78,16 +78,17 @@ class PluginProxyBroadcastComponent grpc::ClientContext client_context{}; prep_client_context(client_context, *slot_info_); - auto broadcaster{ details::broadcast_factory() }; - auto request = details::broadcast_message_factory(std::forward(args)...); + using request_type = details::broadcast_rpc; + request_type request{}; + auto response = google::protobuf::Empty{}; - status = co_await broadcaster.request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); + status = co_await request_type::ClientRPC::request(grpc_context, broadcast_stub_, client_context, request(std::forward(args)...), response, boost::asio::use_awaitable); co_return; } details::slot_info_ptr slot_info_; details::plugin_info_ptr plugin_info_; - ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. + ranges::semiregular_box::stub_type> broadcast_stub_; ///< The gRPC Broadcast stub for communication. }; } // namespace cura::plugins diff --git a/include/plugins/components/invoke.h b/include/plugins/components/invoke.h index 616f33c087..332d08a4e2 100644 --- a/include/plugins/components/invoke.h +++ b/include/plugins/components/invoke.h @@ -14,6 +14,7 @@ #include "utils/types/generic.h" #include +#include #include #include #include @@ -139,7 +140,7 @@ class PluginProxyInvokeComponent */ boost::asio::awaitable invokeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) { - using RPC = agrpc::RPC<&invoke_stub_t::PrepareAsyncCall>; + using RPC = agrpc::ClientRPC<&invoke_stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; prep_client_context(client_context, *slot_info_); diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 2b6c7e5b38..49840be61d 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -21,6 +21,8 @@ #include "utils/types/generic.h" #include +#include +#include #include #include #include @@ -92,7 +94,7 @@ class PluginProxy grpc_context, [this, &grpc_context, &status, &plugin_info, &handshake_stub]() -> boost::asio::awaitable { - using RPC = agrpc::RPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; + using RPC = agrpc::ClientRPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; grpc::ClientContext client_context{}; prep_client_context(client_context, *slot_info_); From 5efa2cb700653418e3b0706631c8ab589ff99a25 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 09:51:43 +0000 Subject: [PATCH 380/656] Applied clang-format. --- include/plugins/broadcasts.h | 2 +- include/plugins/components/broadcast.h | 8 +++++++- include/plugins/pluginproxy.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h index 4b19477ccc..acfaaa23ef 100644 --- a/include/plugins/broadcasts.h +++ b/include/plugins/broadcasts.h @@ -4,8 +4,8 @@ #ifndef PLUGINS_BROADCAST_H #define PLUGINS_BROADCAST_H -#include "cura/plugins/v0/slot_id.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" +#include "cura/plugins/v0/slot_id.pb.h" #include "plugins/converters.h" #include diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index 76214a7929..225ae0f586 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -82,7 +82,13 @@ class PluginProxyBroadcastComponent request_type request{}; auto response = google::protobuf::Empty{}; - status = co_await request_type::ClientRPC::request(grpc_context, broadcast_stub_, client_context, request(std::forward(args)...), response, boost::asio::use_awaitable); + status = co_await request_type::ClientRPC::request( + grpc_context, + broadcast_stub_, + client_context, + request(std::forward(args)...), + response, + boost::asio::use_awaitable); co_return; } diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 49840be61d..a7d0f6dac8 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -22,8 +22,8 @@ #include #include -#include #include +#include #include #include #include From c7143be489c1bc92bc3ef2d0ac05de64315f835a Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 10 Aug 2023 12:10:51 +0200 Subject: [PATCH 381/656] Add unit test CURA-10811 --- tests/utils/SmoothTest.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index b24805f1f1..d09eff53a7 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -94,9 +94,9 @@ TEST(SmoothTest, TestSmooth) /* * * - * B + * C * \ - * D ----------- C + * A ----------- B * \ * \ * \ @@ -156,5 +156,20 @@ TEST(SmoothTest, TestSmooth) EXPECT_EQ(is_smooth, true); } + { + // real life example of a line that is clearly not smooth + auto A = cura::Point{ 148451, 162177 }; + auto B = cura::Point{ 148854, 162229 }; + auto C = cura::Point{ 148866, 162244 }; + auto D = cura::Point{ 149772, 162297 }; + + spdlog::info("smooth.dist(A, B): {}", smooth.dist(A, B)); + spdlog::info("smooth.dist(B, C): {}", smooth.dist(B, C)); + spdlog::info("smooth.dist(C, D): {}", smooth.dist(C, D)); + + const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); + EXPECT_EQ(is_smooth, false); + }; + } } // namespace cura \ No newline at end of file From 9eba6adf9280f0a86d526ce7bc37e606bfb23d5e Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 10 Aug 2023 12:11:37 +0200 Subject: [PATCH 382/656] Fix bug in smooth It was changing the wrong points CURA-10811 --- include/utils/actions/smooth.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 559704a173..01733835c3 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -63,8 +63,6 @@ struct smooth_fn const utils::integral auto fluid_motion_small_distance, const utils::floating_point auto fluid_motion_angle) const { - const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; - const auto size = ranges::distance(rng) - 1; if (size < 4) { @@ -83,16 +81,16 @@ struct smooth_fn const auto C = *std::next(windows_it, 2); const auto D = *std::next(windows_it, 3); - if (dist(*A, *B) < fluid_motion_shift_distance3 || dist(*B, *C) > fluid_motion_small_distance || dist(*C, *D) < fluid_motion_shift_distance3) - { + const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; + if (dist(*A, *B) < fluid_motion_shift_distance3 || dist(*B, *C) > fluid_motion_small_distance || dist(*C, *D) < fluid_motion_shift_distance3) { continue; } const auto cos_fluid_motion_angle = std::cos(fluid_motion_angle); if (! isSmooth(*A, *B, *C, *D, cos_fluid_motion_angle)) { - *A = shiftPointTowards(*B, *A, fluid_motion_shift_distance); - *D = shiftPointTowards(*C, *D, fluid_motion_shift_distance); + *B = shiftPointTowards(*B, *A, fluid_motion_shift_distance); + *C = shiftPointTowards(*C, *D, fluid_motion_shift_distance); } } @@ -255,7 +253,7 @@ struct smooth_fn const auto cos_angle_abc = cosAngle(A_, B, C, shift_distance, BC_magnitude); const auto cos_angle_bcd = cosAngle(B, C, D_, BC_magnitude, shift_distance); - // tThe motion is fluid if either of the marker angles is smaller than the max angle + // The motion is fluid if either of the marker angles is smaller than the max angle return cos_angle_fluid >= fluid_motion_angle || cos_angle_abc >= fluid_motion_angle || cos_angle_bcd >= fluid_motion_angle; } }; From df7d1e89e4adadc3b77438302cd0fcb449634876 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 10 Aug 2023 10:12:31 +0000 Subject: [PATCH 383/656] Applied clang-format. --- include/utils/actions/smooth.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 01733835c3..e5b5eb53f9 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -82,7 +82,8 @@ struct smooth_fn const auto D = *std::next(windows_it, 3); const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; - if (dist(*A, *B) < fluid_motion_shift_distance3 || dist(*B, *C) > fluid_motion_small_distance || dist(*C, *D) < fluid_motion_shift_distance3) { + if (dist(*A, *B) < fluid_motion_shift_distance3 || dist(*B, *C) > fluid_motion_small_distance || dist(*C, *D) < fluid_motion_shift_distance3) + { continue; } From c884409e6d36462cbad26202d89e8f6dbfeb08d8 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 10 Aug 2023 12:17:46 +0200 Subject: [PATCH 384/656] Remove unneeded comments CURA-10811 --- tests/utils/SmoothTest.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/utils/SmoothTest.cpp b/tests/utils/SmoothTest.cpp index d09eff53a7..7efb08cbb2 100644 --- a/tests/utils/SmoothTest.cpp +++ b/tests/utils/SmoothTest.cpp @@ -18,7 +18,7 @@ TEST(SmoothTest, TestSmooth) { // test isSmooth utility function cura::actions::smooth_fn smooth; - const auto FLUID_ANGLE = 5.; + const auto FLUID_ANGLE = 15.; const auto COS_FLUID_ANGLE = std::cos(FLUID_ANGLE * M_PI / 180.); { @@ -163,10 +163,6 @@ TEST(SmoothTest, TestSmooth) auto C = cura::Point{ 148866, 162244 }; auto D = cura::Point{ 149772, 162297 }; - spdlog::info("smooth.dist(A, B): {}", smooth.dist(A, B)); - spdlog::info("smooth.dist(B, C): {}", smooth.dist(B, C)); - spdlog::info("smooth.dist(C, D): {}", smooth.dist(C, D)); - const auto is_smooth = smooth.isSmooth(A, B, C, D, COS_FLUID_ANGLE); EXPECT_EQ(is_smooth, false); }; From 7967365a6b1a6f653d1a4ae72ca35366362256a2 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 10 Aug 2023 13:05:33 +0200 Subject: [PATCH 385/656] Use limits CURA-10811 --- include/utils/actions/smooth.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index e5b5eb53f9..367d3fb052 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -178,7 +179,7 @@ struct smooth_fn requires utils::point2d || utils::junction auto cosAngle(Vector& A, Vector& B, const utils::floating_point auto A_magnitude, const utils::floating_point auto B_magnitude) const noexcept { - if (A_magnitude <= FLT_EPSILON || B_magnitude <= FLT_EPSILON) + if (A_magnitude <= std::numeric_limits::epsilon || B_magnitude <= std::numeric_limits::epsilon) { return static_cast(0.0); } From 714d639825e93d7f5e86d0ecc50f1797d79b0a5d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 13:06:12 +0200 Subject: [PATCH 386/656] Fix missing include Contributes to CURA-10927 --- include/Mold.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/Mold.h b/include/Mold.h index 744fb3dd3e..4726279805 100644 --- a/include/Mold.h +++ b/include/Mold.h @@ -4,6 +4,8 @@ #ifndef MOLD_H #define MOLD_H +#include + namespace cura { From 137c0128a149acccf814231962660272844168f2 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 11:06:54 +0000 Subject: [PATCH 387/656] Applied clang-format. --- include/Mold.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/Mold.h b/include/Mold.h index 4726279805..03d5ecc536 100644 --- a/include/Mold.h +++ b/include/Mold.h @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef MOLD_H #define MOLD_H @@ -35,9 +35,10 @@ class Mold * offset from the mold angle). */ static void process(std::vector& slicer_list); + private: }; -}//namespace cura +} // namespace cura -#endif//MOLD_H +#endif // MOLD_H From eb3cfab0378bd68f11c9f14432c02ab2df784c85 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 13:30:51 +0200 Subject: [PATCH 388/656] Update benchmark flows --- .github/workflows/benchmark.yml | 13 +++++++++---- .github/workflows/gcodeanalyzer.yml | 6 ++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6dfcac34f2..794294d01d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -26,6 +26,10 @@ on: tags: - '[0-9]+.[0-9]+.[0-9]+' +permissions: + contents: write + deployments: write + env: CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} @@ -143,18 +147,19 @@ jobs: - name: Run benchmark CuraEngine id: run-test - run: ./benchmarks --benchmark_format=json >> benchmark_result.json + run: ./benchmarks --benchmark_format=json --benchmark_out=benchmark_result.json working-directory: build/Release/benchmark - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 with: name: C++ Benchmark - tool: 'googlecpp' output-file-path: build/Release/benchmark/benchmark_result.json + benchmark-data-dir-path: dev/bench + tool: 'googlecpp' github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true - # Show alert with commit comment on detecting possible performance regression alert-threshold: '175%' - comment-on-alert: false + summary-always: true + comment-on-alert: true max-items-in-chart: 250 diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index c9e2039efd..94e13f053a 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -22,6 +22,10 @@ on: tags: - '[0-9]+.[0-9]+.[0-9]+' +permissions: + contents: write + deployments: write + env: CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} @@ -297,8 +301,6 @@ jobs: tool: customBiggerIsBetter github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true - gh-repository: github.com/Ultimaker/CuraEngine - # Show alert with commit comment on detecting possible performance regression alert-threshold: '110%' summary-always: true comment-on-alert: true From 456474b8e23f95bcdf8d8c2b381a729ed0de764b Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 10 Aug 2023 13:30:38 +0200 Subject: [PATCH 389/656] Fix builds Epsilon is a function aparently and not a constant CURA-10811 --- include/utils/actions/smooth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 367d3fb052..0e4431564b 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -179,7 +179,7 @@ struct smooth_fn requires utils::point2d || utils::junction auto cosAngle(Vector& A, Vector& B, const utils::floating_point auto A_magnitude, const utils::floating_point auto B_magnitude) const noexcept { - if (A_magnitude <= std::numeric_limits::epsilon || B_magnitude <= std::numeric_limits::epsilon) + if (A_magnitude <= std::numeric_limits::epsilon() || B_magnitude <= std::numeric_limits::epsilon()) { return static_cast(0.0); } From 1d710193d103c2e3ea9e9bac0b9f094ebfb4f564 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 10 Aug 2023 15:17:29 +0200 Subject: [PATCH 390/656] Apply suggestions from linter and some optimisation Used snake case variable names Use pre-computed dist values CURA-10811 --- include/utils/actions/smooth.h | 107 +++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 0e4431564b..0c750c838f 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -70,6 +70,9 @@ struct smooth_fn return static_cast(rng); } + const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; + const auto cos_fluid_motion_angle = std::cos(fluid_motion_angle); + auto tmp = rng; // We don't want to shift the points of the in-going range, therefore we create a temporary copy auto windows = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; @@ -77,22 +80,32 @@ struct smooth_fn // The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. for (auto windows_it = ranges::begin(windows); ranges::distance(windows_it, ranges::end(windows)) > 2; ++windows_it) { - const auto A = *windows_it; - const auto B = *std::next(windows_it, 1); - const auto C = *std::next(windows_it, 2); - const auto D = *std::next(windows_it, 3); + const auto a = *windows_it; + const auto b = *std::next(windows_it, 1); + const auto c = *std::next(windows_it, 2); + const auto d = *std::next(windows_it, 3); + + const auto magnitude_ab = dist(*a, *b); + const auto magnitude_bc = dist(*b, *c); + const auto magnitude_cd = dist(*c, *d); - const auto fluid_motion_shift_distance3 = 3 * fluid_motion_shift_distance; - if (dist(*A, *B) < fluid_motion_shift_distance3 || dist(*B, *C) > fluid_motion_small_distance || dist(*C, *D) < fluid_motion_shift_distance3) + if (magnitude_bc > fluid_motion_small_distance) { continue; } - const auto cos_fluid_motion_angle = std::cos(fluid_motion_angle); - if (! isSmooth(*A, *B, *C, *D, cos_fluid_motion_angle)) + // only if segments ab and cd are long enough, we can shift b and c + // 3 * fluid_motion_shift_distance is the minimum length of the segments ab and cd + // as this allows us to shift both ends with the shift distance and still have some room to spare + if (magnitude_ab < fluid_motion_shift_distance3 || magnitude_cd < fluid_motion_shift_distance3) { - *B = shiftPointTowards(*B, *A, fluid_motion_shift_distance); - *C = shiftPointTowards(*C, *D, fluid_motion_shift_distance); + continue; + } + + if (! isSmooth(*a, *b, *c, *d, cos_fluid_motion_angle, magnitude_ab, magnitude_bc, magnitude_cd)) + { + *b = shiftPointTowards(*b, *a, fluid_motion_shift_distance, magnitude_ab); + *c = shiftPointTowards(*c, *d, fluid_motion_shift_distance, magnitude_cd); } } @@ -113,16 +126,16 @@ struct smooth_fn */ template requires utils::point2d || utils::junction - auto cosAngle(Point& A, Point& B, Point& C) const noexcept + constexpr auto cosAngle(Point& a, Point& b, Point& c) const noexcept { - return cosAngle(A, B, C, dist(A, B), dist(B, C)); + return cosAngle(a, b, c, dist(a, b), dist(b, c)); } template requires utils::point2d || utils::junction - auto cosAngle(Point& A, Point& B, Point& C, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept + constexpr auto cosAngle(Point& a, Point& b, Point& c, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept { - return cosAngle(A, B, B, C, AB_magnitude, BC_magnitude); + return cosAngle(a, b, b, c, ab_magnitude, bc_magnitude); } /* @@ -144,19 +157,19 @@ struct smooth_fn */ template requires utils::point2d || utils::junction - auto cosAngle(Point& A, Point& B, Point& C, Point& D) const noexcept + constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d) const noexcept { - return cosAngle(A, B, C, D, dist(A, B), dist(C, D)); + return cosAngle(a, b, c, d, dist(a, b), dist(c, d)); } template requires utils::point2d || utils::junction - auto cosAngle(Point& A, Point& B, Point& C, Point& D, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept + constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept { - Point VectorA = { std::get<"X">(B) - std::get<"X">(A), std::get<"Y">(B) - std::get<"Y">(A) }; - Point VectorB = { std::get<"X">(D) - std::get<"X">(C), std::get<"Y">(D) - std::get<"Y">(C) }; + Point vector_a = { std::get<"X">(b) - std::get<"X">(a), std::get<"Y">(b) - std::get<"Y">(a) }; + Point vector_b = { std::get<"X">(d) - std::get<"X">(c), std::get<"Y">(d) - std::get<"Y">(c) }; - return cosAngle(VectorA, VectorB, AB_magnitude, BC_magnitude); + return cosAngle(vector_a, vector_b, ab_magnitude, bc_magnitude); } /* @@ -170,20 +183,20 @@ struct smooth_fn */ template requires utils::point2d || utils::junction - auto cosAngle(Vector& A, Vector& B) const noexcept + constexpr auto cosAngle(Vector& a, Vector& b) const noexcept { - return cosAngle(A, B, magnitude(A), magnitude(B)); + return cosAngle(a, b, magnitude(a), magnitude(b)); } template requires utils::point2d || utils::junction - auto cosAngle(Vector& A, Vector& B, const utils::floating_point auto A_magnitude, const utils::floating_point auto B_magnitude) const noexcept + constexpr auto cosAngle(Vector& a, Vector& b, const utils::floating_point auto a_magnitude, const utils::floating_point auto b_magnitude) const noexcept { - if (A_magnitude <= std::numeric_limits::epsilon() || B_magnitude <= std::numeric_limits::epsilon()) + if (a_magnitude <= std::numeric_limits::epsilon()|| b_magnitude <= std::numeric_limits::epsilon()) { - return static_cast(0.0); + return static_cast(0.0); } - return static_cast(dotProduct(A, B)) / (A_magnitude * B_magnitude); + return static_cast(dotProduct(a, b)) / (a_magnitude * b_magnitude); } template @@ -195,8 +208,7 @@ struct smooth_fn template requires utils::point2d || utils::junction - Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) - const noexcept + constexpr Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) const noexcept { using coord_type = std::remove_cvref_t(p0))>; const auto shift_distance = move_distance / p0p1_distance; @@ -207,27 +219,45 @@ struct smooth_fn } template - requires utils::point2d || utils::junction utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept + requires utils::point2d || utils::junction + constexpr utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept { return std::hypot(std::get<"X">(point_0) - std::get<"X">(point_1), std::get<"Y">(point_0) - std::get<"Y">(point_1)); } template - requires utils::point2d || utils::junction utils::floating_point auto magnitude(Vector& v) const noexcept + requires utils::point2d || utils::junction + constexpr utils::floating_point auto magnitude(Vector& v) const noexcept { return std::hypot(std::get<"X">(v), std::get<"Y">(v)); } template requires utils::point2d || utils::junction - auto dotProduct(Vector& point_0, Vector& point_1) const noexcept + constexpr auto dotProduct(Vector& point_0, Vector& point_1) const noexcept { return std::get<"X">(point_0) * std::get<"X">(point_1) + std::get<"Y">(point_0) * std::get<"Y">(point_1); } template requires utils::point2d || utils::junction - bool isSmooth(Point& A, Point& B, Point& C, Point& D, utils::floating_point auto fluid_motion_angle) const noexcept + constexpr bool isSmooth(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto fluid_motion_angle) const noexcept + { + return isSmooth(a, b, c, d, fluid_motion_angle, dist(a, b), dist(b, c), dist(c, d)); + } + + template + requires utils::point2d || utils::junction + constexpr bool isSmooth( + Point& a, + Point& b, + Point& c, + Point& d, + const utils::floating_point auto fluid_motion_angle, + const utils::floating_point auto dist_ab, + const utils::floating_point auto dist_bc, + const utils::floating_point auto dist_cd + ) const noexcept { /* * Move points A and B, so they are both at equal distance from C and D @@ -245,15 +275,12 @@ struct smooth_fn * vectors [A_,D_] and [B,C] are oriented within a certain angle */ constexpr auto shift_distance = 300.; - auto A_ = shiftPointTowards(B, A, shift_distance); - auto D_ = shiftPointTowards(C, D, shift_distance); - - // precompute distance BC - const auto BC_magnitude = dist(B, C); + auto a_ = shiftPointTowards(b, a, shift_distance, dist_ab); + auto d_ = shiftPointTowards(c, d, shift_distance, dist_cd); - const auto cos_angle_fluid = cosAngle(A_, D_, B, C, dist(A_, D_), BC_magnitude); - const auto cos_angle_abc = cosAngle(A_, B, C, shift_distance, BC_magnitude); - const auto cos_angle_bcd = cosAngle(B, C, D_, BC_magnitude, shift_distance); + const auto cos_angle_fluid = cosAngle(a_, d_, b, c, dist(a_, d_), dist_bc); + const auto cos_angle_abc = cosAngle(a_, b, c, shift_distance, dist_bc); + const auto cos_angle_bcd = cosAngle(b, c, d_, dist_bc, shift_distance); // The motion is fluid if either of the marker angles is smaller than the max angle return cos_angle_fluid >= fluid_motion_angle || cos_angle_abc >= fluid_motion_angle || cos_angle_bcd >= fluid_motion_angle; From 69e57ee4e0329c5c96811b8617b6ca59dcaac4ae Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 10 Aug 2023 13:18:34 +0000 Subject: [PATCH 391/656] Applied clang-format. --- include/utils/actions/smooth.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 0c750c838f..a1d746b31e 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -192,7 +192,7 @@ struct smooth_fn requires utils::point2d || utils::junction constexpr auto cosAngle(Vector& a, Vector& b, const utils::floating_point auto a_magnitude, const utils::floating_point auto b_magnitude) const noexcept { - if (a_magnitude <= std::numeric_limits::epsilon()|| b_magnitude <= std::numeric_limits::epsilon()) + if (a_magnitude <= std::numeric_limits::epsilon() || b_magnitude <= std::numeric_limits::epsilon()) { return static_cast(0.0); } @@ -256,8 +256,7 @@ struct smooth_fn const utils::floating_point auto fluid_motion_angle, const utils::floating_point auto dist_ab, const utils::floating_point auto dist_bc, - const utils::floating_point auto dist_cd - ) const noexcept + const utils::floating_point auto dist_cd) const noexcept { /* * Move points A and B, so they are both at equal distance from C and D From 8e55e23b87e7bff526e00cc7a0d449f220ae5b42 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 15:28:03 +0200 Subject: [PATCH 392/656] Don't inherit from numeric_facade for LayerIndex There where a lot of conversion troubles because it returned the base type. Contribute to CURA-10916 --- include/settings/types/LayerIndex.h | 188 +++++++++++++++++++++++++-- include/utils/types/numeric_facade.h | 64 +++------ 2 files changed, 192 insertions(+), 60 deletions(-) diff --git a/include/settings/types/LayerIndex.h b/include/settings/types/LayerIndex.h index 4588642cd5..28822675ff 100644 --- a/include/settings/types/LayerIndex.h +++ b/include/settings/types/LayerIndex.h @@ -4,26 +4,190 @@ #ifndef LAYERINDEX_H #define LAYERINDEX_H -#include "utils/types/numeric_facade.h" +#include "utils/types/generic.h" #include namespace cura { -/* - * \brief Struct behaving like a layer number. - * - * This is a facade. It behaves exactly like an integer but is used to indicate - * that it is a layer number. - */ -struct LayerIndex : public utils::NumericFacade +struct LayerIndex { - using base_type = utils::NumericFacade; - using base_type::NumericFacade; + using value_type = int64_t; + using difference_type = std::ptrdiff_t; - constexpr LayerIndex(const base_type& base) noexcept - : base_type{ base } {}; + value_type value{}; + + constexpr LayerIndex() noexcept = default; + + constexpr LayerIndex(const LayerIndex& other) noexcept = default; + constexpr LayerIndex(LayerIndex&& other) noexcept = default; + + constexpr explicit LayerIndex(const utils::floating_point auto val) noexcept : value{ static_cast(val) } {}; + + constexpr LayerIndex(const utils::integral auto val) noexcept : value{ static_cast(val) } {}; + + constexpr LayerIndex& operator=(const LayerIndex& other) noexcept = default; + + constexpr LayerIndex& operator=(const utils::integral auto& other) noexcept + { + this->value = static_cast(other); + return *this; + } + + constexpr LayerIndex& operator=(LayerIndex&& other) noexcept = default; + constexpr LayerIndex& operator=(const utils::integral auto&& other) noexcept + { + this->value = static_cast(other); + return *this; + } + + ~LayerIndex() noexcept = default; + + constexpr operator value_type() const noexcept + { + return value; + } + + constexpr bool operator==(const LayerIndex& other) const noexcept + { + return value == other.value; + } + + constexpr bool operator==(const utils::integral auto& other) const noexcept + { + return value == static_cast(other); + } + + constexpr auto operator<=>(const LayerIndex& other) const noexcept = default; + constexpr auto operator<=>(const utils::integral auto& other) const noexcept + { + return value <=> static_cast(other); + }; + + constexpr LayerIndex& operator+=(const LayerIndex& other) noexcept + { + value += other.value; + return *this; + } + + constexpr LayerIndex& operator+=(const utils::integral auto& other) noexcept + { + value += static_cast(other); + return *this; + } + + constexpr LayerIndex& operator-=(const LayerIndex& other) noexcept + { + value -= other.value; + return *this; + } + + constexpr LayerIndex& operator-=(const utils::integral auto& other) noexcept + { + value -= static_cast(other); + return *this; + } + + constexpr LayerIndex& operator*=(const LayerIndex& other) noexcept + { + value *= other.value; + return *this; + } + + constexpr LayerIndex& operator*=(const utils::integral auto& other) noexcept + { + value *= static_cast(other); + return *this; + } + + constexpr LayerIndex& operator/=(const LayerIndex& other) + { + value /= other.value; + return *this; + } + + constexpr LayerIndex& operator/=(const utils::integral auto& other) + { + value /= static_cast(other); + return *this; + } + + constexpr LayerIndex operator+(const LayerIndex& other) const noexcept + { + return { value + other.value }; + } + + constexpr LayerIndex operator+(LayerIndex&& other) const noexcept + { + return { value + other.value }; + } + + constexpr LayerIndex operator+(const utils::integral auto& other) const noexcept + { + return { value + static_cast(other) }; + } + + constexpr LayerIndex operator-(const LayerIndex& other) const noexcept + { + return { value - other.value }; + } + + constexpr LayerIndex operator-(const utils::integral auto& other) const noexcept + { + return { value - static_cast(other) }; + } + + constexpr LayerIndex operator*(const LayerIndex& other) const noexcept + { + return { value * other.value }; + } + + constexpr LayerIndex operator*(const utils::integral auto& other) const noexcept + { + return { value * static_cast(other) }; + } + + constexpr LayerIndex operator/(const LayerIndex& other) const + { + return { value / other.value }; + } + + constexpr LayerIndex operator/(const utils::integral auto& other) const + { + return { value / static_cast(other) }; + } + + constexpr LayerIndex operator-() const noexcept + { + return { -value }; + } + + constexpr LayerIndex& operator++() noexcept + { + ++value; + return *this; + } + + LayerIndex operator++(int) noexcept + { + LayerIndex tmp{ *this }; + operator++(); + return tmp; + } + + constexpr LayerIndex& operator--() noexcept + { + --value; + return *this; + } + + LayerIndex operator--(int) noexcept + { + LayerIndex tmp{ *this }; + operator--(); + return tmp; + } }; } // namespace cura diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h index ae6a6074ec..6f70517199 100644 --- a/include/utils/types/numeric_facade.h +++ b/include/utils/types/numeric_facade.h @@ -9,11 +9,10 @@ namespace cura::utils { -template +template struct NumericFacade { using value_type = T; - using difference_type = std::ptrdiff_t; value_type value{}; @@ -22,30 +21,20 @@ struct NumericFacade constexpr NumericFacade(const NumericFacade& other) noexcept = default; constexpr NumericFacade(NumericFacade&& other) noexcept = default; - constexpr NumericFacade(const floating_point auto val) noexcept requires floating_point : value{ static_cast(val) } {}; - - constexpr NumericFacade(const integral auto val) noexcept requires integral : value{ static_cast(val) } {}; + constexpr NumericFacade(const floating_point auto val) noexcept : value{ static_cast(val) } {}; + constexpr explicit NumericFacade(const integral auto val) noexcept : value{ static_cast(val) } {}; constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; - constexpr NumericFacade& operator=(const floating_point auto& other) noexcept requires floating_point - { - this->value = static_cast(other); - return *this; - } - constexpr NumericFacade& operator=(const integral auto& other) noexcept requires integral + constexpr NumericFacade& operator=(const floating_point auto& other) noexcept { this->value = static_cast(other); return *this; } constexpr NumericFacade& operator=(NumericFacade&& other) noexcept = default; - constexpr NumericFacade& operator=(const integral auto&& other) noexcept requires integral - { - this->value = static_cast(other); - return *this; - } - constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept requires floating_point + + constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept { this->value = static_cast(other); return *this; @@ -63,13 +52,13 @@ struct NumericFacade return value == other.value; } - constexpr bool operator==(const numeric auto& other) const noexcept + constexpr bool operator==(const floating_point auto& other) const noexcept { return value == static_cast(other); } constexpr auto operator<=>(const NumericFacade& other) const noexcept = default; - constexpr auto operator<=>(const numeric auto& other) const noexcept + constexpr auto operator<=>(const floating_point auto& other) const noexcept { return value <=> static_cast(other); }; @@ -80,7 +69,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator+=(const numeric auto& other) noexcept + constexpr NumericFacade& operator+=(const floating_point auto& other) noexcept { value += static_cast(other); return *this; @@ -92,7 +81,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator-=(const numeric auto& other) noexcept + constexpr NumericFacade& operator-=(const floating_point auto& other) noexcept { value -= static_cast(other); return *this; @@ -104,7 +93,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator*=(const numeric auto& other) noexcept + constexpr NumericFacade& operator*=(const floating_point auto& other) noexcept { value *= static_cast(other); return *this; @@ -116,7 +105,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator/=(const numeric auto& other) + constexpr NumericFacade& operator/=(const floating_point auto& other) { value /= static_cast(other); return *this; @@ -132,7 +121,7 @@ struct NumericFacade return { value + other.value }; } - constexpr NumericFacade operator+(const numeric auto& other) const noexcept + constexpr NumericFacade operator+(const floating_point auto& other) const noexcept { return { value + static_cast(other) }; } @@ -142,7 +131,7 @@ struct NumericFacade return { value - other.value }; } - constexpr NumericFacade operator-(const numeric auto& other) const noexcept + constexpr NumericFacade operator-(const floating_point auto& other) const noexcept { return { value - static_cast(other) }; } @@ -152,7 +141,7 @@ struct NumericFacade return { value * other.value }; } - constexpr NumericFacade operator*(const numeric auto& other) const noexcept + constexpr NumericFacade operator*(const floating_point auto& other) const noexcept { return { value * static_cast(other) }; } @@ -162,7 +151,7 @@ struct NumericFacade return { value / other.value }; } - constexpr NumericFacade operator/(const numeric auto& other) const + constexpr NumericFacade operator/(const floating_point auto& other) const { return { value / static_cast(other) }; } @@ -172,27 +161,6 @@ struct NumericFacade return { -value }; } - constexpr NumericFacade& operator++() noexcept requires integral - { - ++value; - return *this; - } - - constexpr NumericFacade operator++(int) noexcept requires integral - { - return { value++ }; - } - - constexpr NumericFacade& operator--() noexcept requires integral - { - --value; - return *this; - } - - constexpr NumericFacade operator--(int) noexcept requires integral - { - return { value-- }; - } }; } // namespace cura::utils From d2309635f5ecece62eef7fb9de154b99fc1dd148 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 15:29:54 +0200 Subject: [PATCH 393/656] Make the connection a bit more robust Contribute to CURA-10916 --- src/utils/channel.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/channel.cpp b/src/utils/channel.cpp index 2dbe8fca23..2a4c775767 100644 --- a/src/utils/channel.cpp +++ b/src/utils/channel.cpp @@ -36,7 +36,12 @@ std::shared_ptr createChannel(const ChannelSetupConfiguration& co spdlog::warn("Remote plugins where disabled, will not connect to {}:{}.", config.host, config.port); return std::shared_ptr(); }; - return grpc::CreateChannel(fmt::format("{}:{}", config.host, config.port), create_credentials(config)); + grpc::ChannelArguments args; + args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 200 * 1000 /*200 sec*/); + args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 100 * 1000 /*100 sec*/); + args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); + + return grpc::CreateCustomChannel(fmt::format("{}:{}", config.host, config.port), create_credentials(config), args); } } // namespace cura::utils From 0ee09437c2cd437a51fec17c0d67d0ecd10e424d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 15:30:39 +0200 Subject: [PATCH 394/656] Make sure that COmmunication works with concrete int64 Contribute to CURA-10916 --- include/communication/ArcusCommunication.h | 4 ++-- include/communication/ArcusCommunicationPrivate.h | 2 +- include/communication/CommandLine.h | 4 ++-- include/communication/Communication.h | 4 ++-- src/communication/ArcusCommunication.cpp | 4 ++-- src/communication/ArcusCommunicationPrivate.cpp | 2 +- src/communication/CommandLine.cpp | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index 133c68268f..c3dfa02846 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -117,7 +117,7 @@ class ArcusCommunication : public Communication * \param z The z-coordinate of the top side of the layer. * \param thickness The thickness of the layer. */ - void sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) override; + void sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) override; /* * \brief Send a line to the front-end to display in layer view. @@ -192,7 +192,7 @@ class ArcusCommunication : public Communication * \param layer_nr The index of the layer to send data for. This is zero- * indexed but may be negative for raft layers. */ - void setLayerForSend(const LayerIndex& layer_nr) override; + void setLayerForSend(const LayerIndex::value_type& layer_nr) override; /* * \brief Slice the next scene that the front-end wants us to slice. diff --git a/include/communication/ArcusCommunicationPrivate.h b/include/communication/ArcusCommunicationPrivate.h index 48534deee4..5d2cebfed2 100644 --- a/include/communication/ArcusCommunicationPrivate.h +++ b/include/communication/ArcusCommunicationPrivate.h @@ -27,7 +27,7 @@ class ArcusCommunication::Private * \param layer_nr The layer number to get the optimised layer data for. * \return The optimised layer data for that layer. */ - std::shared_ptr getOptimizedLayerById(LayerIndex layer_nr); + std::shared_ptr getOptimizedLayerById(LayerIndex::value_type layer_nr); /* * Reads the global settings from a Protobuf message. diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index 321671fd1c..b949ec647f 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -86,7 +86,7 @@ class CommandLine : public Communication * The command line doesn't do anything with that information so this is * ignored. */ - void sendLayerComplete(const LayerIndex&, const coord_t&, const coord_t&) override; + void sendLayerComplete(const LayerIndex::value_type&, const coord_t&, const coord_t&) override; /* * \brief Send a line for display. @@ -143,7 +143,7 @@ class CommandLine : public Communication * This has no effect though because we don't shwo these three functions * because the command line doesn't show layer view. */ - void setLayerForSend(const LayerIndex&) override; + void setLayerForSend(const LayerIndex::value_type&) override; /* * \brief Slice the next scene that the command line commands us to slice. diff --git a/include/communication/Communication.h b/include/communication/Communication.h index 48cc6a3133..bdd68b9630 100644 --- a/include/communication/Communication.h +++ b/include/communication/Communication.h @@ -62,7 +62,7 @@ class Communication * \param z The z-coordinate of the top side of the layer. * \param thickness The thickness of the layer. */ - virtual void sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) = 0; + virtual void sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) = 0; /* * \brief Send polygons to the user to visualise. @@ -126,7 +126,7 @@ class Communication * \param layer_nr The index of the layer to send data for. This is zero- * indexed but may be negative for raft layers. */ - virtual void setLayerForSend(const LayerIndex& layer_nr) = 0; + virtual void setLayerForSend(const LayerIndex::value_type& layer_nr) = 0; /* * \brief Send the sliced layer data through this communication after the diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index cd360ae5e4..0df3ac998d 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -393,7 +393,7 @@ void ArcusCommunication::sendFinishedSlicing() const spdlog::debug("Sent slicing finished message."); } -void ArcusCommunication::sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) +void ArcusCommunication::sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) { std::shared_ptr layer = private_data->getOptimizedLayerById(layer_nr); layer->set_height(z); @@ -494,7 +494,7 @@ void ArcusCommunication::sendProgress(const float& progress) const private_data->last_sent_progress = rounded_amount; } -void ArcusCommunication::setLayerForSend(const LayerIndex& layer_nr) +void ArcusCommunication::setLayerForSend(const LayerIndex::value_type& layer_nr) { path_compiler->setLayer(layer_nr); } diff --git a/src/communication/ArcusCommunicationPrivate.cpp b/src/communication/ArcusCommunicationPrivate.cpp index 9de1b6e2d3..fbeda21d21 100644 --- a/src/communication/ArcusCommunicationPrivate.cpp +++ b/src/communication/ArcusCommunicationPrivate.cpp @@ -20,7 +20,7 @@ ArcusCommunication::Private::Private() : socket(nullptr), object_count(0), last_ { } -std::shared_ptr ArcusCommunication::Private::getOptimizedLayerById(LayerIndex layer_nr) +std::shared_ptr ArcusCommunication::Private::getOptimizedLayerById(LayerIndex::value_type layer_nr) { layer_nr += optimized_layers.current_layer_offset; std::unordered_map>::iterator find_result = optimized_layers.slice_data.find(layer_nr); diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index cac3c7da6e..33410072ed 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -40,7 +40,7 @@ void CommandLine::sendCurrentPosition(const Point&) void CommandLine::sendFinishedSlicing() const { } -void CommandLine::sendLayerComplete(const LayerIndex&, const coord_t&, const coord_t&) +void CommandLine::sendLayerComplete(const LayerIndex::value_type&, const coord_t&, const coord_t&) { } void CommandLine::sendLineTo(const PrintFeatureType&, const Point&, const coord_t&, const coord_t&, const Velocity&) @@ -58,7 +58,7 @@ void CommandLine::sendPolygons(const PrintFeatureType&, const Polygons&, const c void CommandLine::setExtruderForSend(const ExtruderTrain&) { } -void CommandLine::setLayerForSend(const LayerIndex&) +void CommandLine::setLayerForSend(const LayerIndex::value_type&) { } From da5979d00a5a5d4dad426a7da18a470fc9c7fd06 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 13:31:21 +0000 Subject: [PATCH 395/656] Applied clang-format. --- include/communication/ArcusCommunication.h | 16 ++--- include/communication/CommandLine.h | 14 ++--- include/settings/types/LayerIndex.h | 6 +- include/utils/types/numeric_facade.h | 7 ++- .../ArcusCommunicationPrivate.cpp | 16 +++-- src/communication/CommandLine.cpp | 62 +++++++++---------- 6 files changed, 65 insertions(+), 56 deletions(-) diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index c3dfa02846..7cc7679535 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -6,17 +6,17 @@ #ifdef ARCUS #ifdef BUILD_TESTS - #include +#include #endif -#include //For unique_ptr and shared_ptr. - #include "Communication.h" //The class we're implementing. #include "Cura.pb.h" //To create Protobuf messages for Cura's front-end. -//Forward declarations to speed up compilation. +#include //For unique_ptr and shared_ptr. + +// Forward declarations to speed up compilation. namespace Arcus { - class Socket; +class Socket; } namespace cura @@ -227,7 +227,7 @@ class ArcusCommunication : public Communication const std::unique_ptr path_compiler; }; -} //namespace cura +} // namespace cura -#endif //ARCUS -#endif //ARCUSCOMMUNICATION_H +#endif // ARCUS +#endif // ARCUSCOMMUNICATION_H diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index b949ec647f..2948241c51 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -4,13 +4,13 @@ #ifndef COMMANDLINE_H #define COMMANDLINE_H +#include "Communication.h" //The class we're implementing. + #include //Loading JSON documents to get settings from them. #include //To store the command line arguments. #include #include //To store the command line arguments. -#include "Communication.h" //The class we're implementing. - namespace cura { class Settings; @@ -188,14 +188,12 @@ class CommandLine : public Communication * \return Error code. If it's 0, the document was successfully loaded. If * it's 1, some inheriting file could not be opened. */ - int loadJSON - ( + int loadJSON( const rapidjson::Document& document, const std::unordered_set& search_directories, Settings& settings, bool force_read_parent = false, - bool force_read_nondefault = false - ); + bool force_read_nondefault = false); /* * \brief Load an element containing a list of settings. @@ -216,6 +214,6 @@ class CommandLine : public Communication const std::string findDefinitionFile(const std::string& definition_id, const std::unordered_set& search_directories); }; -} //namespace cura +} // namespace cura -#endif //COMMANDLINE_H \ No newline at end of file +#endif // COMMANDLINE_H \ No newline at end of file diff --git a/include/settings/types/LayerIndex.h b/include/settings/types/LayerIndex.h index 28822675ff..b1a7524428 100644 --- a/include/settings/types/LayerIndex.h +++ b/include/settings/types/LayerIndex.h @@ -23,9 +23,11 @@ struct LayerIndex constexpr LayerIndex(const LayerIndex& other) noexcept = default; constexpr LayerIndex(LayerIndex&& other) noexcept = default; - constexpr explicit LayerIndex(const utils::floating_point auto val) noexcept : value{ static_cast(val) } {}; + constexpr explicit LayerIndex(const utils::floating_point auto val) noexcept + : value{ static_cast(val) } {}; - constexpr LayerIndex(const utils::integral auto val) noexcept : value{ static_cast(val) } {}; + constexpr LayerIndex(const utils::integral auto val) noexcept + : value{ static_cast(val) } {}; constexpr LayerIndex& operator=(const LayerIndex& other) noexcept = default; diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h index 6f70517199..ce986fcf25 100644 --- a/include/utils/types/numeric_facade.h +++ b/include/utils/types/numeric_facade.h @@ -21,8 +21,10 @@ struct NumericFacade constexpr NumericFacade(const NumericFacade& other) noexcept = default; constexpr NumericFacade(NumericFacade&& other) noexcept = default; - constexpr NumericFacade(const floating_point auto val) noexcept : value{ static_cast(val) } {}; - constexpr explicit NumericFacade(const integral auto val) noexcept : value{ static_cast(val) } {}; + constexpr NumericFacade(const floating_point auto val) noexcept + : value{ static_cast(val) } {}; + constexpr explicit NumericFacade(const integral auto val) noexcept + : value{ static_cast(val) } {}; constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; @@ -160,7 +162,6 @@ struct NumericFacade { return { -value }; } - }; } // namespace cura::utils diff --git a/src/communication/ArcusCommunicationPrivate.cpp b/src/communication/ArcusCommunicationPrivate.cpp index fbeda21d21..90b23c1e64 100644 --- a/src/communication/ArcusCommunicationPrivate.cpp +++ b/src/communication/ArcusCommunicationPrivate.cpp @@ -3,20 +3,26 @@ #ifdef ARCUS -#include +#include "communication/ArcusCommunicationPrivate.h" #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" -#include "communication/ArcusCommunicationPrivate.h" #include "settings/types/LayerIndex.h" #include "utils/FMatrix4x3.h" //To convert vertices to integer-points. #include "utils/floatpoint.h" //To accept vertices (which are provided in floating point). +#include + namespace cura { -ArcusCommunication::Private::Private() : socket(nullptr), object_count(0), last_sent_progress(-1), slice_count(0), millisecUntilNextTry(100) +ArcusCommunication::Private::Private() + : socket(nullptr) + , object_count(0) + , last_sent_progress(-1) + , slice_count(0) + , millisecUntilNextTry(100) { } @@ -67,7 +73,9 @@ void ArcusCommunication::Private::readExtruderSettingsMessage(const google::prot spdlog::warn("Received extruder index that is out of range: {}", extruder_nr); continue; } - ExtruderTrain& extruder = slice->scene.extruders[extruder_nr]; // Extruder messages may arrive out of order, so don't iteratively get the next extruder but take the extruder_nr from this message. + ExtruderTrain& extruder + = slice->scene + .extruders[extruder_nr]; // Extruder messages may arrive out of order, so don't iteratively get the next extruder but take the extruder_nr from this message. for (const cura::proto::Setting& setting_message : extruder_message.settings().settings()) { extruder.settings.add(setting_message.name(), setting_message.value()); diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 33410072ed..7b7c0f7163 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -1,29 +1,32 @@ // Copyright (c) 2022 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher +#include "communication/CommandLine.h" + +#include "Application.h" //To get the extruders for material estimates. +#include "ExtruderTrain.h" +#include "FffProcessor.h" //To start a slice and get time estimates. +#include "Slice.h" +#include "utils/FMatrix4x3.h" //For the mesh_rotation_matrix setting. + +#include + #include //For strtok and strcopy. #include // error number when trying to read file #include #include //To check if files exist. #include //For std::accumulate. -#include - #include //Loading JSON documents to get settings from them. #include #include -#include - -#include "Application.h" //To get the extruders for material estimates. -#include "ExtruderTrain.h" -#include "FffProcessor.h" //To start a slice and get time estimates. -#include "Slice.h" -#include "communication/CommandLine.h" -#include "utils/FMatrix4x3.h" //For the mesh_rotation_matrix setting. +#include namespace cura { -CommandLine::CommandLine(const std::vector& arguments) : arguments(arguments), last_shown_progress(0) +CommandLine::CommandLine(const std::vector& arguments) + : arguments(arguments) + , last_shown_progress(0) { } @@ -165,7 +168,8 @@ void CommandLine::sliceNext() } else if (argument.find("--force-read-nondefault") == 0 || argument.find("--force_read_nondefault") == 0) { - spdlog::info("From this point on, if 'default_value' is not available, force the parser to read 'value' (instead of dropping it) to fill the used setting-values."); + spdlog::info( + "From this point on, if 'default_value' is not available, force the parser to read 'value' (instead of dropping it) to fill the used setting-values."); force_read_nondefault = true; } else if (argument.find("--end-force-read") == 0 || argument.find("--end_force_read") == 0) @@ -407,14 +411,12 @@ std::unordered_set CommandLine::defaultSearchDirectories() return result; } -int CommandLine::loadJSON -( +int CommandLine::loadJSON( const rapidjson::Document& document, const std::unordered_set& search_directories, Settings& settings, bool force_read_parent, - bool force_read_nondefault -) + bool force_read_nondefault) { // Inheritance from other JSON documents. if (document.HasMember("inherits") && document["inherits"].IsString()) @@ -433,7 +435,8 @@ int CommandLine::loadJSON } // Extruders defined from here, if any. - // Note that this always puts the extruder settings in the slice of the current extruder. It doesn't keep the nested structure of the JSON files, if extruders would have their own sub-extruders. + // Note that this always puts the extruder settings in the slice of the current extruder. It doesn't keep the nested structure of the JSON files, if extruders would have their + // own sub-extruders. Scene& scene = Application::getInstance().current_slice->scene; if (document.HasMember("metadata") && document["metadata"].IsObject()) { @@ -502,20 +505,17 @@ bool jsonValue2Str(const rapidjson::Value& value, std::string& value_string) } std::string temp; jsonValue2Str(value[0], temp); - value_string = - std::string("[") + - std::accumulate - ( - std::next(value.Begin()), - value.End(), - temp, - [&temp](std::string converted, const rapidjson::Value& next) - { - jsonValue2Str(next, temp); - return std::move(converted) + "," + temp; - } - ) + - std::string("]"); + value_string = std::string("[") + + std::accumulate( + std::next(value.Begin()), + value.End(), + temp, + [&temp](std::string converted, const rapidjson::Value& next) + { + jsonValue2Str(next, temp); + return std::move(converted) + "," + temp; + }) + + std::string("]"); } else { From 88d21c36d3ab1ce808fe9362c0b1016a94051cb5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 15:28:03 +0200 Subject: [PATCH 396/656] Don't inherit from numeric_facade for LayerIndex There where a lot of conversion troubles because it returned the base type. Contribute to CURA-10916 --- include/settings/types/LayerIndex.h | 188 +++++++++++++++++++++++++-- include/utils/types/numeric_facade.h | 64 +++------ 2 files changed, 192 insertions(+), 60 deletions(-) diff --git a/include/settings/types/LayerIndex.h b/include/settings/types/LayerIndex.h index 4588642cd5..28822675ff 100644 --- a/include/settings/types/LayerIndex.h +++ b/include/settings/types/LayerIndex.h @@ -4,26 +4,190 @@ #ifndef LAYERINDEX_H #define LAYERINDEX_H -#include "utils/types/numeric_facade.h" +#include "utils/types/generic.h" #include namespace cura { -/* - * \brief Struct behaving like a layer number. - * - * This is a facade. It behaves exactly like an integer but is used to indicate - * that it is a layer number. - */ -struct LayerIndex : public utils::NumericFacade +struct LayerIndex { - using base_type = utils::NumericFacade; - using base_type::NumericFacade; + using value_type = int64_t; + using difference_type = std::ptrdiff_t; - constexpr LayerIndex(const base_type& base) noexcept - : base_type{ base } {}; + value_type value{}; + + constexpr LayerIndex() noexcept = default; + + constexpr LayerIndex(const LayerIndex& other) noexcept = default; + constexpr LayerIndex(LayerIndex&& other) noexcept = default; + + constexpr explicit LayerIndex(const utils::floating_point auto val) noexcept : value{ static_cast(val) } {}; + + constexpr LayerIndex(const utils::integral auto val) noexcept : value{ static_cast(val) } {}; + + constexpr LayerIndex& operator=(const LayerIndex& other) noexcept = default; + + constexpr LayerIndex& operator=(const utils::integral auto& other) noexcept + { + this->value = static_cast(other); + return *this; + } + + constexpr LayerIndex& operator=(LayerIndex&& other) noexcept = default; + constexpr LayerIndex& operator=(const utils::integral auto&& other) noexcept + { + this->value = static_cast(other); + return *this; + } + + ~LayerIndex() noexcept = default; + + constexpr operator value_type() const noexcept + { + return value; + } + + constexpr bool operator==(const LayerIndex& other) const noexcept + { + return value == other.value; + } + + constexpr bool operator==(const utils::integral auto& other) const noexcept + { + return value == static_cast(other); + } + + constexpr auto operator<=>(const LayerIndex& other) const noexcept = default; + constexpr auto operator<=>(const utils::integral auto& other) const noexcept + { + return value <=> static_cast(other); + }; + + constexpr LayerIndex& operator+=(const LayerIndex& other) noexcept + { + value += other.value; + return *this; + } + + constexpr LayerIndex& operator+=(const utils::integral auto& other) noexcept + { + value += static_cast(other); + return *this; + } + + constexpr LayerIndex& operator-=(const LayerIndex& other) noexcept + { + value -= other.value; + return *this; + } + + constexpr LayerIndex& operator-=(const utils::integral auto& other) noexcept + { + value -= static_cast(other); + return *this; + } + + constexpr LayerIndex& operator*=(const LayerIndex& other) noexcept + { + value *= other.value; + return *this; + } + + constexpr LayerIndex& operator*=(const utils::integral auto& other) noexcept + { + value *= static_cast(other); + return *this; + } + + constexpr LayerIndex& operator/=(const LayerIndex& other) + { + value /= other.value; + return *this; + } + + constexpr LayerIndex& operator/=(const utils::integral auto& other) + { + value /= static_cast(other); + return *this; + } + + constexpr LayerIndex operator+(const LayerIndex& other) const noexcept + { + return { value + other.value }; + } + + constexpr LayerIndex operator+(LayerIndex&& other) const noexcept + { + return { value + other.value }; + } + + constexpr LayerIndex operator+(const utils::integral auto& other) const noexcept + { + return { value + static_cast(other) }; + } + + constexpr LayerIndex operator-(const LayerIndex& other) const noexcept + { + return { value - other.value }; + } + + constexpr LayerIndex operator-(const utils::integral auto& other) const noexcept + { + return { value - static_cast(other) }; + } + + constexpr LayerIndex operator*(const LayerIndex& other) const noexcept + { + return { value * other.value }; + } + + constexpr LayerIndex operator*(const utils::integral auto& other) const noexcept + { + return { value * static_cast(other) }; + } + + constexpr LayerIndex operator/(const LayerIndex& other) const + { + return { value / other.value }; + } + + constexpr LayerIndex operator/(const utils::integral auto& other) const + { + return { value / static_cast(other) }; + } + + constexpr LayerIndex operator-() const noexcept + { + return { -value }; + } + + constexpr LayerIndex& operator++() noexcept + { + ++value; + return *this; + } + + LayerIndex operator++(int) noexcept + { + LayerIndex tmp{ *this }; + operator++(); + return tmp; + } + + constexpr LayerIndex& operator--() noexcept + { + --value; + return *this; + } + + LayerIndex operator--(int) noexcept + { + LayerIndex tmp{ *this }; + operator--(); + return tmp; + } }; } // namespace cura diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h index ae6a6074ec..6f70517199 100644 --- a/include/utils/types/numeric_facade.h +++ b/include/utils/types/numeric_facade.h @@ -9,11 +9,10 @@ namespace cura::utils { -template +template struct NumericFacade { using value_type = T; - using difference_type = std::ptrdiff_t; value_type value{}; @@ -22,30 +21,20 @@ struct NumericFacade constexpr NumericFacade(const NumericFacade& other) noexcept = default; constexpr NumericFacade(NumericFacade&& other) noexcept = default; - constexpr NumericFacade(const floating_point auto val) noexcept requires floating_point : value{ static_cast(val) } {}; - - constexpr NumericFacade(const integral auto val) noexcept requires integral : value{ static_cast(val) } {}; + constexpr NumericFacade(const floating_point auto val) noexcept : value{ static_cast(val) } {}; + constexpr explicit NumericFacade(const integral auto val) noexcept : value{ static_cast(val) } {}; constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; - constexpr NumericFacade& operator=(const floating_point auto& other) noexcept requires floating_point - { - this->value = static_cast(other); - return *this; - } - constexpr NumericFacade& operator=(const integral auto& other) noexcept requires integral + constexpr NumericFacade& operator=(const floating_point auto& other) noexcept { this->value = static_cast(other); return *this; } constexpr NumericFacade& operator=(NumericFacade&& other) noexcept = default; - constexpr NumericFacade& operator=(const integral auto&& other) noexcept requires integral - { - this->value = static_cast(other); - return *this; - } - constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept requires floating_point + + constexpr NumericFacade& operator=(const floating_point auto&& other) noexcept { this->value = static_cast(other); return *this; @@ -63,13 +52,13 @@ struct NumericFacade return value == other.value; } - constexpr bool operator==(const numeric auto& other) const noexcept + constexpr bool operator==(const floating_point auto& other) const noexcept { return value == static_cast(other); } constexpr auto operator<=>(const NumericFacade& other) const noexcept = default; - constexpr auto operator<=>(const numeric auto& other) const noexcept + constexpr auto operator<=>(const floating_point auto& other) const noexcept { return value <=> static_cast(other); }; @@ -80,7 +69,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator+=(const numeric auto& other) noexcept + constexpr NumericFacade& operator+=(const floating_point auto& other) noexcept { value += static_cast(other); return *this; @@ -92,7 +81,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator-=(const numeric auto& other) noexcept + constexpr NumericFacade& operator-=(const floating_point auto& other) noexcept { value -= static_cast(other); return *this; @@ -104,7 +93,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator*=(const numeric auto& other) noexcept + constexpr NumericFacade& operator*=(const floating_point auto& other) noexcept { value *= static_cast(other); return *this; @@ -116,7 +105,7 @@ struct NumericFacade return *this; } - constexpr NumericFacade& operator/=(const numeric auto& other) + constexpr NumericFacade& operator/=(const floating_point auto& other) { value /= static_cast(other); return *this; @@ -132,7 +121,7 @@ struct NumericFacade return { value + other.value }; } - constexpr NumericFacade operator+(const numeric auto& other) const noexcept + constexpr NumericFacade operator+(const floating_point auto& other) const noexcept { return { value + static_cast(other) }; } @@ -142,7 +131,7 @@ struct NumericFacade return { value - other.value }; } - constexpr NumericFacade operator-(const numeric auto& other) const noexcept + constexpr NumericFacade operator-(const floating_point auto& other) const noexcept { return { value - static_cast(other) }; } @@ -152,7 +141,7 @@ struct NumericFacade return { value * other.value }; } - constexpr NumericFacade operator*(const numeric auto& other) const noexcept + constexpr NumericFacade operator*(const floating_point auto& other) const noexcept { return { value * static_cast(other) }; } @@ -162,7 +151,7 @@ struct NumericFacade return { value / other.value }; } - constexpr NumericFacade operator/(const numeric auto& other) const + constexpr NumericFacade operator/(const floating_point auto& other) const { return { value / static_cast(other) }; } @@ -172,27 +161,6 @@ struct NumericFacade return { -value }; } - constexpr NumericFacade& operator++() noexcept requires integral - { - ++value; - return *this; - } - - constexpr NumericFacade operator++(int) noexcept requires integral - { - return { value++ }; - } - - constexpr NumericFacade& operator--() noexcept requires integral - { - --value; - return *this; - } - - constexpr NumericFacade operator--(int) noexcept requires integral - { - return { value-- }; - } }; } // namespace cura::utils From c6fd6f460c947ab45ffb0478ac4b58ba4ab69167 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 15:30:39 +0200 Subject: [PATCH 397/656] Make sure that COmmunication works with concrete int64 Contribute to CURA-10916 --- include/communication/ArcusCommunication.h | 4 ++-- include/communication/ArcusCommunicationPrivate.h | 2 +- include/communication/CommandLine.h | 4 ++-- include/communication/Communication.h | 4 ++-- src/communication/ArcusCommunication.cpp | 4 ++-- src/communication/ArcusCommunicationPrivate.cpp | 2 +- src/communication/CommandLine.cpp | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index 133c68268f..c3dfa02846 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -117,7 +117,7 @@ class ArcusCommunication : public Communication * \param z The z-coordinate of the top side of the layer. * \param thickness The thickness of the layer. */ - void sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) override; + void sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) override; /* * \brief Send a line to the front-end to display in layer view. @@ -192,7 +192,7 @@ class ArcusCommunication : public Communication * \param layer_nr The index of the layer to send data for. This is zero- * indexed but may be negative for raft layers. */ - void setLayerForSend(const LayerIndex& layer_nr) override; + void setLayerForSend(const LayerIndex::value_type& layer_nr) override; /* * \brief Slice the next scene that the front-end wants us to slice. diff --git a/include/communication/ArcusCommunicationPrivate.h b/include/communication/ArcusCommunicationPrivate.h index 48534deee4..5d2cebfed2 100644 --- a/include/communication/ArcusCommunicationPrivate.h +++ b/include/communication/ArcusCommunicationPrivate.h @@ -27,7 +27,7 @@ class ArcusCommunication::Private * \param layer_nr The layer number to get the optimised layer data for. * \return The optimised layer data for that layer. */ - std::shared_ptr getOptimizedLayerById(LayerIndex layer_nr); + std::shared_ptr getOptimizedLayerById(LayerIndex::value_type layer_nr); /* * Reads the global settings from a Protobuf message. diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index 321671fd1c..b949ec647f 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -86,7 +86,7 @@ class CommandLine : public Communication * The command line doesn't do anything with that information so this is * ignored. */ - void sendLayerComplete(const LayerIndex&, const coord_t&, const coord_t&) override; + void sendLayerComplete(const LayerIndex::value_type&, const coord_t&, const coord_t&) override; /* * \brief Send a line for display. @@ -143,7 +143,7 @@ class CommandLine : public Communication * This has no effect though because we don't shwo these three functions * because the command line doesn't show layer view. */ - void setLayerForSend(const LayerIndex&) override; + void setLayerForSend(const LayerIndex::value_type&) override; /* * \brief Slice the next scene that the command line commands us to slice. diff --git a/include/communication/Communication.h b/include/communication/Communication.h index 48cc6a3133..bdd68b9630 100644 --- a/include/communication/Communication.h +++ b/include/communication/Communication.h @@ -62,7 +62,7 @@ class Communication * \param z The z-coordinate of the top side of the layer. * \param thickness The thickness of the layer. */ - virtual void sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) = 0; + virtual void sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) = 0; /* * \brief Send polygons to the user to visualise. @@ -126,7 +126,7 @@ class Communication * \param layer_nr The index of the layer to send data for. This is zero- * indexed but may be negative for raft layers. */ - virtual void setLayerForSend(const LayerIndex& layer_nr) = 0; + virtual void setLayerForSend(const LayerIndex::value_type& layer_nr) = 0; /* * \brief Send the sliced layer data through this communication after the diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 9b0b04a4b1..8750ce2798 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -390,7 +390,7 @@ void ArcusCommunication::sendFinishedSlicing() const spdlog::debug("Sent slicing finished message."); } -void ArcusCommunication::sendLayerComplete(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness) +void ArcusCommunication::sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) { std::shared_ptr layer = private_data->getOptimizedLayerById(layer_nr); layer->set_height(z); @@ -491,7 +491,7 @@ void ArcusCommunication::sendProgress(const float& progress) const private_data->last_sent_progress = rounded_amount; } -void ArcusCommunication::setLayerForSend(const LayerIndex& layer_nr) +void ArcusCommunication::setLayerForSend(const LayerIndex::value_type& layer_nr) { path_compiler->setLayer(layer_nr); } diff --git a/src/communication/ArcusCommunicationPrivate.cpp b/src/communication/ArcusCommunicationPrivate.cpp index 9de1b6e2d3..fbeda21d21 100644 --- a/src/communication/ArcusCommunicationPrivate.cpp +++ b/src/communication/ArcusCommunicationPrivate.cpp @@ -20,7 +20,7 @@ ArcusCommunication::Private::Private() : socket(nullptr), object_count(0), last_ { } -std::shared_ptr ArcusCommunication::Private::getOptimizedLayerById(LayerIndex layer_nr) +std::shared_ptr ArcusCommunication::Private::getOptimizedLayerById(LayerIndex::value_type layer_nr) { layer_nr += optimized_layers.current_layer_offset; std::unordered_map>::iterator find_result = optimized_layers.slice_data.find(layer_nr); diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index cac3c7da6e..33410072ed 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -40,7 +40,7 @@ void CommandLine::sendCurrentPosition(const Point&) void CommandLine::sendFinishedSlicing() const { } -void CommandLine::sendLayerComplete(const LayerIndex&, const coord_t&, const coord_t&) +void CommandLine::sendLayerComplete(const LayerIndex::value_type&, const coord_t&, const coord_t&) { } void CommandLine::sendLineTo(const PrintFeatureType&, const Point&, const coord_t&, const coord_t&, const Velocity&) @@ -58,7 +58,7 @@ void CommandLine::sendPolygons(const PrintFeatureType&, const Polygons&, const c void CommandLine::setExtruderForSend(const ExtruderTrain&) { } -void CommandLine::setLayerForSend(const LayerIndex&) +void CommandLine::setLayerForSend(const LayerIndex::value_type&) { } From db4ff9715be9636368bafc9fc4e845945c17d6f9 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 10 Aug 2023 13:33:13 +0000 Subject: [PATCH 398/656] Applied clang-format. --- include/communication/ArcusCommunication.h | 16 ++--- include/communication/CommandLine.h | 14 ++--- include/settings/types/LayerIndex.h | 6 +- include/utils/types/numeric_facade.h | 7 ++- .../ArcusCommunicationPrivate.cpp | 16 +++-- src/communication/CommandLine.cpp | 62 +++++++++---------- 6 files changed, 65 insertions(+), 56 deletions(-) diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index c3dfa02846..7cc7679535 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -6,17 +6,17 @@ #ifdef ARCUS #ifdef BUILD_TESTS - #include +#include #endif -#include //For unique_ptr and shared_ptr. - #include "Communication.h" //The class we're implementing. #include "Cura.pb.h" //To create Protobuf messages for Cura's front-end. -//Forward declarations to speed up compilation. +#include //For unique_ptr and shared_ptr. + +// Forward declarations to speed up compilation. namespace Arcus { - class Socket; +class Socket; } namespace cura @@ -227,7 +227,7 @@ class ArcusCommunication : public Communication const std::unique_ptr path_compiler; }; -} //namespace cura +} // namespace cura -#endif //ARCUS -#endif //ARCUSCOMMUNICATION_H +#endif // ARCUS +#endif // ARCUSCOMMUNICATION_H diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index b949ec647f..2948241c51 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -4,13 +4,13 @@ #ifndef COMMANDLINE_H #define COMMANDLINE_H +#include "Communication.h" //The class we're implementing. + #include //Loading JSON documents to get settings from them. #include //To store the command line arguments. #include #include //To store the command line arguments. -#include "Communication.h" //The class we're implementing. - namespace cura { class Settings; @@ -188,14 +188,12 @@ class CommandLine : public Communication * \return Error code. If it's 0, the document was successfully loaded. If * it's 1, some inheriting file could not be opened. */ - int loadJSON - ( + int loadJSON( const rapidjson::Document& document, const std::unordered_set& search_directories, Settings& settings, bool force_read_parent = false, - bool force_read_nondefault = false - ); + bool force_read_nondefault = false); /* * \brief Load an element containing a list of settings. @@ -216,6 +214,6 @@ class CommandLine : public Communication const std::string findDefinitionFile(const std::string& definition_id, const std::unordered_set& search_directories); }; -} //namespace cura +} // namespace cura -#endif //COMMANDLINE_H \ No newline at end of file +#endif // COMMANDLINE_H \ No newline at end of file diff --git a/include/settings/types/LayerIndex.h b/include/settings/types/LayerIndex.h index 28822675ff..b1a7524428 100644 --- a/include/settings/types/LayerIndex.h +++ b/include/settings/types/LayerIndex.h @@ -23,9 +23,11 @@ struct LayerIndex constexpr LayerIndex(const LayerIndex& other) noexcept = default; constexpr LayerIndex(LayerIndex&& other) noexcept = default; - constexpr explicit LayerIndex(const utils::floating_point auto val) noexcept : value{ static_cast(val) } {}; + constexpr explicit LayerIndex(const utils::floating_point auto val) noexcept + : value{ static_cast(val) } {}; - constexpr LayerIndex(const utils::integral auto val) noexcept : value{ static_cast(val) } {}; + constexpr LayerIndex(const utils::integral auto val) noexcept + : value{ static_cast(val) } {}; constexpr LayerIndex& operator=(const LayerIndex& other) noexcept = default; diff --git a/include/utils/types/numeric_facade.h b/include/utils/types/numeric_facade.h index 6f70517199..ce986fcf25 100644 --- a/include/utils/types/numeric_facade.h +++ b/include/utils/types/numeric_facade.h @@ -21,8 +21,10 @@ struct NumericFacade constexpr NumericFacade(const NumericFacade& other) noexcept = default; constexpr NumericFacade(NumericFacade&& other) noexcept = default; - constexpr NumericFacade(const floating_point auto val) noexcept : value{ static_cast(val) } {}; - constexpr explicit NumericFacade(const integral auto val) noexcept : value{ static_cast(val) } {}; + constexpr NumericFacade(const floating_point auto val) noexcept + : value{ static_cast(val) } {}; + constexpr explicit NumericFacade(const integral auto val) noexcept + : value{ static_cast(val) } {}; constexpr NumericFacade& operator=(const NumericFacade& other) noexcept = default; @@ -160,7 +162,6 @@ struct NumericFacade { return { -value }; } - }; } // namespace cura::utils diff --git a/src/communication/ArcusCommunicationPrivate.cpp b/src/communication/ArcusCommunicationPrivate.cpp index fbeda21d21..90b23c1e64 100644 --- a/src/communication/ArcusCommunicationPrivate.cpp +++ b/src/communication/ArcusCommunicationPrivate.cpp @@ -3,20 +3,26 @@ #ifdef ARCUS -#include +#include "communication/ArcusCommunicationPrivate.h" #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" -#include "communication/ArcusCommunicationPrivate.h" #include "settings/types/LayerIndex.h" #include "utils/FMatrix4x3.h" //To convert vertices to integer-points. #include "utils/floatpoint.h" //To accept vertices (which are provided in floating point). +#include + namespace cura { -ArcusCommunication::Private::Private() : socket(nullptr), object_count(0), last_sent_progress(-1), slice_count(0), millisecUntilNextTry(100) +ArcusCommunication::Private::Private() + : socket(nullptr) + , object_count(0) + , last_sent_progress(-1) + , slice_count(0) + , millisecUntilNextTry(100) { } @@ -67,7 +73,9 @@ void ArcusCommunication::Private::readExtruderSettingsMessage(const google::prot spdlog::warn("Received extruder index that is out of range: {}", extruder_nr); continue; } - ExtruderTrain& extruder = slice->scene.extruders[extruder_nr]; // Extruder messages may arrive out of order, so don't iteratively get the next extruder but take the extruder_nr from this message. + ExtruderTrain& extruder + = slice->scene + .extruders[extruder_nr]; // Extruder messages may arrive out of order, so don't iteratively get the next extruder but take the extruder_nr from this message. for (const cura::proto::Setting& setting_message : extruder_message.settings().settings()) { extruder.settings.add(setting_message.name(), setting_message.value()); diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 33410072ed..7b7c0f7163 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -1,29 +1,32 @@ // Copyright (c) 2022 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher +#include "communication/CommandLine.h" + +#include "Application.h" //To get the extruders for material estimates. +#include "ExtruderTrain.h" +#include "FffProcessor.h" //To start a slice and get time estimates. +#include "Slice.h" +#include "utils/FMatrix4x3.h" //For the mesh_rotation_matrix setting. + +#include + #include //For strtok and strcopy. #include // error number when trying to read file #include #include //To check if files exist. #include //For std::accumulate. -#include - #include //Loading JSON documents to get settings from them. #include #include -#include - -#include "Application.h" //To get the extruders for material estimates. -#include "ExtruderTrain.h" -#include "FffProcessor.h" //To start a slice and get time estimates. -#include "Slice.h" -#include "communication/CommandLine.h" -#include "utils/FMatrix4x3.h" //For the mesh_rotation_matrix setting. +#include namespace cura { -CommandLine::CommandLine(const std::vector& arguments) : arguments(arguments), last_shown_progress(0) +CommandLine::CommandLine(const std::vector& arguments) + : arguments(arguments) + , last_shown_progress(0) { } @@ -165,7 +168,8 @@ void CommandLine::sliceNext() } else if (argument.find("--force-read-nondefault") == 0 || argument.find("--force_read_nondefault") == 0) { - spdlog::info("From this point on, if 'default_value' is not available, force the parser to read 'value' (instead of dropping it) to fill the used setting-values."); + spdlog::info( + "From this point on, if 'default_value' is not available, force the parser to read 'value' (instead of dropping it) to fill the used setting-values."); force_read_nondefault = true; } else if (argument.find("--end-force-read") == 0 || argument.find("--end_force_read") == 0) @@ -407,14 +411,12 @@ std::unordered_set CommandLine::defaultSearchDirectories() return result; } -int CommandLine::loadJSON -( +int CommandLine::loadJSON( const rapidjson::Document& document, const std::unordered_set& search_directories, Settings& settings, bool force_read_parent, - bool force_read_nondefault -) + bool force_read_nondefault) { // Inheritance from other JSON documents. if (document.HasMember("inherits") && document["inherits"].IsString()) @@ -433,7 +435,8 @@ int CommandLine::loadJSON } // Extruders defined from here, if any. - // Note that this always puts the extruder settings in the slice of the current extruder. It doesn't keep the nested structure of the JSON files, if extruders would have their own sub-extruders. + // Note that this always puts the extruder settings in the slice of the current extruder. It doesn't keep the nested structure of the JSON files, if extruders would have their + // own sub-extruders. Scene& scene = Application::getInstance().current_slice->scene; if (document.HasMember("metadata") && document["metadata"].IsObject()) { @@ -502,20 +505,17 @@ bool jsonValue2Str(const rapidjson::Value& value, std::string& value_string) } std::string temp; jsonValue2Str(value[0], temp); - value_string = - std::string("[") + - std::accumulate - ( - std::next(value.Begin()), - value.End(), - temp, - [&temp](std::string converted, const rapidjson::Value& next) - { - jsonValue2Str(next, temp); - return std::move(converted) + "," + temp; - } - ) + - std::string("]"); + value_string = std::string("[") + + std::accumulate( + std::next(value.Begin()), + value.End(), + temp, + [&temp](std::string converted, const rapidjson::Value& next) + { + jsonValue2Str(next, temp); + return std::move(converted) + "," + temp; + }) + + std::string("]"); } else { From c2b8cdfcae741211f7f6dcf655432a07f5c47851 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 10 Aug 2023 15:54:37 +0200 Subject: [PATCH 399/656] Fix UT Contributes to CURA-10916 --- tests/arcus/MockCommunication.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/arcus/MockCommunication.h b/tests/arcus/MockCommunication.h index 563c98ef31..1db835ffb7 100644 --- a/tests/arcus/MockCommunication.h +++ b/tests/arcus/MockCommunication.h @@ -8,6 +8,7 @@ #include "utils/Coord_t.h" #include "utils/polygon.h" //In the signature of Communication. #include +#include "settings/types/LayerIndex.h" namespace cura { @@ -21,7 +22,7 @@ class MockCommunication : public Communication MOCK_CONST_METHOD0(hasSlice, bool()); MOCK_CONST_METHOD0(isSequential, bool()); MOCK_CONST_METHOD1(sendProgress, void(const float& progress)); - MOCK_METHOD3(sendLayerComplete, void(const LayerIndex& layer_nr, const coord_t& z, const coord_t& thickness)); + MOCK_METHOD3(sendLayerComplete, void(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness)); MOCK_METHOD5(sendPolygons, void(const PrintFeatureType& type, const Polygons& polygons, @@ -42,7 +43,7 @@ class MockCommunication : public Communication const Velocity& velocity)); MOCK_METHOD1(sendCurrentPosition, void(const Point& position)); MOCK_METHOD1(setExtruderForSend, void(const ExtruderTrain& extruder)); - MOCK_METHOD1(setLayerForSend, void(const LayerIndex& layer_nr)); + MOCK_METHOD1(setLayerForSend, void(const LayerIndex::value_type& layer_nr)); MOCK_METHOD0(sendOptimizedLayerData, void()); MOCK_CONST_METHOD0(sendPrintTimeMaterialEstimates, void()); MOCK_METHOD0(beginGCode, void()); From dac24bcdd7d4479edb66c387609ce1db5860860e Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 11 Aug 2023 08:54:25 +0200 Subject: [PATCH 400/656] Divinde dist in terms of magnitude CURA-10811 --- include/utils/actions/smooth.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index a1d746b31e..7b0700a1bd 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -222,7 +222,8 @@ struct smooth_fn requires utils::point2d || utils::junction constexpr utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept { - return std::hypot(std::get<"X">(point_0) - std::get<"X">(point_1), std::get<"Y">(point_0) - std::get<"Y">(point_1)); + Point vector = { std::get<"X">(point_1) - std::get<"X">(point_0), std::get<"Y">(point_1) - std::get<"Y">(point_0) }; + return magnitude(vector); } template From a09eee9525e4a69e48a872c924b67a929c9b4fd1 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 11 Aug 2023 08:54:41 +0200 Subject: [PATCH 401/656] Inline utility functions CURA-10811 --- include/utils/actions/smooth.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 7b0700a1bd..5034c16145 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -126,14 +126,14 @@ struct smooth_fn */ template requires utils::point2d || utils::junction - constexpr auto cosAngle(Point& a, Point& b, Point& c) const noexcept + inline constexpr auto cosAngle(Point& a, Point& b, Point& c) const noexcept { return cosAngle(a, b, c, dist(a, b), dist(b, c)); } template requires utils::point2d || utils::junction - constexpr auto cosAngle(Point& a, Point& b, Point& c, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept + inline constexpr auto cosAngle(Point& a, Point& b, Point& c, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept { return cosAngle(a, b, b, c, ab_magnitude, bc_magnitude); } @@ -157,14 +157,14 @@ struct smooth_fn */ template requires utils::point2d || utils::junction - constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d) const noexcept + inline constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d) const noexcept { return cosAngle(a, b, c, d, dist(a, b), dist(c, d)); } template requires utils::point2d || utils::junction - constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept + inline constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept { Point vector_a = { std::get<"X">(b) - std::get<"X">(a), std::get<"Y">(b) - std::get<"Y">(a) }; Point vector_b = { std::get<"X">(d) - std::get<"X">(c), std::get<"Y">(d) - std::get<"Y">(c) }; @@ -183,14 +183,14 @@ struct smooth_fn */ template requires utils::point2d || utils::junction - constexpr auto cosAngle(Vector& a, Vector& b) const noexcept + inline constexpr auto cosAngle(Vector& a, Vector& b) const noexcept { return cosAngle(a, b, magnitude(a), magnitude(b)); } template requires utils::point2d || utils::junction - constexpr auto cosAngle(Vector& a, Vector& b, const utils::floating_point auto a_magnitude, const utils::floating_point auto b_magnitude) const noexcept + inline constexpr auto cosAngle(Vector& a, Vector& b, const utils::floating_point auto a_magnitude, const utils::floating_point auto b_magnitude) const noexcept { if (a_magnitude <= std::numeric_limits::epsilon() || b_magnitude <= std::numeric_limits::epsilon()) { @@ -201,14 +201,14 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance) const noexcept + inline constexpr Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance) const noexcept { return shiftPointTowards(p0, p1, move_distance, dist(p0, p1)); } template requires utils::point2d || utils::junction - constexpr Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) const noexcept + inline constexpr Point shiftPointTowards(Point& p0, Point& p1, const utils::numeric auto move_distance, const utils::floating_point auto p0p1_distance) const noexcept { using coord_type = std::remove_cvref_t(p0))>; const auto shift_distance = move_distance / p0p1_distance; @@ -220,7 +220,7 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept + inline constexpr utils::floating_point auto dist(Point& point_0, Point& point_1) const noexcept { Point vector = { std::get<"X">(point_1) - std::get<"X">(point_0), std::get<"Y">(point_1) - std::get<"Y">(point_0) }; return magnitude(vector); @@ -228,14 +228,14 @@ struct smooth_fn template requires utils::point2d || utils::junction - constexpr utils::floating_point auto magnitude(Vector& v) const noexcept + inline constexpr utils::floating_point auto magnitude(Vector& v) const noexcept { return std::hypot(std::get<"X">(v), std::get<"Y">(v)); } template requires utils::point2d || utils::junction - constexpr auto dotProduct(Vector& point_0, Vector& point_1) const noexcept + inline constexpr auto dotProduct(Vector& point_0, Vector& point_1) const noexcept { return std::get<"X">(point_0) * std::get<"X">(point_1) + std::get<"Y">(point_0) * std::get<"Y">(point_1); } From 9c24a9f042fb1200ba10b361aa04c2f1ac5d6808 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Fri, 11 Aug 2023 06:55:26 +0000 Subject: [PATCH 402/656] Applied clang-format. --- include/utils/actions/smooth.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 5034c16145..c04088c262 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -164,7 +164,8 @@ struct smooth_fn template requires utils::point2d || utils::junction - inline constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept + inline constexpr auto + cosAngle(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept { Point vector_a = { std::get<"X">(b) - std::get<"X">(a), std::get<"Y">(b) - std::get<"Y">(a) }; Point vector_b = { std::get<"X">(d) - std::get<"X">(c), std::get<"Y">(d) - std::get<"Y">(c) }; From bf42bbc83c0bd08b496c2cdf1170b9c5cd36f718 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 10:24:22 +0200 Subject: [PATCH 403/656] fix correct spaceship return type Contributes to CURA-10916 --- include/pathPlanning/TimeMaterialEstimates.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pathPlanning/TimeMaterialEstimates.h b/include/pathPlanning/TimeMaterialEstimates.h index eecc69348c..90a1cb3a76 100644 --- a/include/pathPlanning/TimeMaterialEstimates.h +++ b/include/pathPlanning/TimeMaterialEstimates.h @@ -58,7 +58,7 @@ struct TimeMaterialEstimates material - other.material }; } - constexpr bool operator<=>(const TimeMaterialEstimates& other) const noexcept = default; + constexpr auto operator<=>(const TimeMaterialEstimates& other) const noexcept = default; constexpr void reset() noexcept { From 3e2619af00fc3b40e1d09b08d5d01d9516022f29 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 11:15:17 +0200 Subject: [PATCH 404/656] Add missing includes Contributes to CURA-10916 --- include/plugins/components/broadcast.h | 3 +++ include/plugins/components/common.h | 4 ++++ include/plugins/components/invoke.h | 3 +++ include/plugins/exception.h | 1 + 4 files changed, 11 insertions(+) diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index 754eef03e1..bc534fcb2b 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -26,6 +26,9 @@ #include #include +#include +#include + namespace cura::plugins { namespace exceptions diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h index 80666a9b3e..ee292d95ad 100644 --- a/include/plugins/components/common.h +++ b/include/plugins/components/common.h @@ -11,6 +11,10 @@ #include #include +#include +#include +#include + namespace cura::plugins { diff --git a/include/plugins/components/invoke.h b/include/plugins/components/invoke.h index 1ea2a2e758..e233f38bd7 100644 --- a/include/plugins/components/invoke.h +++ b/include/plugins/components/invoke.h @@ -25,6 +25,9 @@ #include #include +#include +#include + namespace cura::plugins { namespace details diff --git a/include/plugins/exception.h b/include/plugins/exception.h index 93f05638eb..8cc4d9a19f 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -6,6 +6,7 @@ #include "plugins/metadata.h" #include "plugins/types.h" +#include "plugins/validator.h" #include From 017655d178453aa0f9ac721b4510bfd7b267f1bd Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 11 Aug 2023 11:36:04 +0200 Subject: [PATCH 405/656] Fix compiling Fix exceptions guard CURA-10811 --- include/plugins/components/broadcast.h | 4 ---- include/plugins/exception.h | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h index bc534fcb2b..c1c5c03db6 100644 --- a/include/plugins/components/broadcast.h +++ b/include/plugins/components/broadcast.h @@ -31,10 +31,6 @@ namespace cura::plugins { -namespace exceptions -{ -class RemoteException; // forward declaration probably needed due to us obfuscating some other classes with forward declarations -} // namespace exceptions template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. class PluginProxyBroadcastComponent diff --git a/include/plugins/exception.h b/include/plugins/exception.h index 8cc4d9a19f..a1816c7754 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef UTILS_CONCEPTS_GRAPH_H -#define UTILS_CONCEPTS_GRAPH_H +#ifndef PLUGINS_EXCEPTION_H +#define PLUGINS_EXCEPTION_H #include "plugins/metadata.h" #include "plugins/types.h" @@ -71,4 +71,4 @@ class RemoteException : public std::exception } // namespace cura::plugins::exceptions -#endif // UTILS_CONCEPTS_GRAPH_H \ No newline at end of file +#endif // PLUGINS_EXCEPTION_H \ No newline at end of file From a72d348197b82e75d7f132e6e189eb5803243585 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 11 Aug 2023 11:39:13 +0200 Subject: [PATCH 406/656] Don't use forward decls for AABB CURA-10619 --- include/infill.h | 2 +- include/utils/AABB3D.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/infill.h b/include/infill.h index 92bd5749d0..760de7d135 100644 --- a/include/infill.h +++ b/include/infill.h @@ -9,6 +9,7 @@ #include "settings/EnumSettings.h" //For infill types. #include "settings/Settings.h" #include "settings/types/Angle.h" +#include "utils/AABB.h" #include "utils/ExtrusionLine.h" #include "utils/IntPoint.h" #include "utils/section_type.h" @@ -18,7 +19,6 @@ namespace cura { -class AABB; class SierpinskiFillProvider; class SliceMeshStorage; diff --git a/include/utils/AABB3D.h b/include/utils/AABB3D.h index 7b344b292f..b889f52d86 100644 --- a/include/utils/AABB3D.h +++ b/include/utils/AABB3D.h @@ -5,12 +5,11 @@ #define UTILS_AABB3D_H #include "IntPoint.h" +#include "utils/AABB.h" namespace cura { -class AABB; - /*! An Axis Aligned Bounding Box. Has a min and max vector, representing minimal and maximal coordinates in the three axes. */ From afcbff89e4921dc4814854528df53f179f482fc6 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Fri, 11 Aug 2023 09:39:52 +0000 Subject: [PATCH 407/656] Applied clang-format. --- include/utils/AABB3D.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/utils/AABB3D.h b/include/utils/AABB3D.h index b889f52d86..c7fca9c486 100644 --- a/include/utils/AABB3D.h +++ b/include/utils/AABB3D.h @@ -1,5 +1,5 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef UTILS_AABB3D_H #define UTILS_AABB3D_H @@ -43,9 +43,9 @@ struct AABB3D /*! * Check whether this aabb overlaps with another. - * + * * In the boundary case false is returned. - * + * * \param other the aabb to check for overlaps with * \return Whether the two aabbs overlap */ @@ -90,7 +90,7 @@ struct AABB3D /*! * Offset the bounding box in the horizontal direction; outward or inward. - * + * * \param outset the distance (positive or negative) to expand the bounding box outward * \return this object (which has changed) */ @@ -98,13 +98,12 @@ struct AABB3D /*! * Offset the bounding box in the horizontal direction; outward or inward. - * + * * \param outset the distance (positive or negative) to expand the bounding box outward * \return this object (which has changed) */ AABB3D expandXY(coord_t outset); }; -}//namespace cura -#endif//UTILS_AABB3D_H - +} // namespace cura +#endif // UTILS_AABB3D_H From 8e279e0ad94ab95ec0dd2af4d0ceb58ef7aa2b1c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 11 Aug 2023 11:45:40 +0200 Subject: [PATCH 408/656] Attempt to fix cura builds --- include/infill.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/infill.h b/include/infill.h index 92bd5749d0..431bd15f37 100644 --- a/include/infill.h +++ b/include/infill.h @@ -66,7 +66,7 @@ class Infill } public: - constexpr Infill() noexcept = default; + Infill() noexcept = default; Infill( EFillMethod pattern, From 2a80b8e5e0797a9cdf1f8a15f415b03911dab085 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Fri, 11 Aug 2023 09:46:37 +0000 Subject: [PATCH 409/656] Applied clang-format. --- include/infill.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/infill.h b/include/infill.h index 431bd15f37..296e2dbed0 100644 --- a/include/infill.h +++ b/include/infill.h @@ -66,7 +66,7 @@ class Infill } public: - Infill() noexcept = default; + Infill() noexcept = default; Infill( EFillMethod pattern, From 7317a4960f9e9fdb61a37a7ea1443965370e6367 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 11 Aug 2023 12:55:36 +0200 Subject: [PATCH 410/656] Use updated concept for regular box --- include/infill.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/infill.h b/include/infill.h index fa5ace44d1..035781ccdb 100644 --- a/include/infill.h +++ b/include/infill.h @@ -14,6 +14,8 @@ #include "utils/IntPoint.h" #include "utils/section_type.h" +#include + #include namespace cura @@ -550,7 +552,7 @@ class Infill */ void connectLines(Polygons& result_lines); }; -static_assert(std::semiregular, "Infill should be semiregular"); +static_assert(concepts::semiregular, "Infill should be semiregular"); } // namespace cura From ae003e7a45c02ee7bc21eebb1c3ed8de125b2032 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Fri, 11 Aug 2023 13:22:36 +0200 Subject: [PATCH 411/656] Temporarily disable smooth function --- src/WallToolPaths.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index c46d4afbf5..d554be2df4 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -91,7 +91,7 @@ const std::vector& WallToolPaths::generate() scripta::log("prepared_outline_0", prepared_outline, section_type, layer_idx); prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); prepared_outline = Simplify(settings).polygon(prepared_outline); - if (settings.get("meshfix_fluid_motion_enabled") && section_type != SectionType::SUPPORT) + if (false && settings.get("meshfix_fluid_motion_enabled") && section_type != SectionType::SUPPORT) { // No need to smooth support walls auto smoother = actions::smooth(settings); From 1ab398a70d59e771dc4f14ed5c013e20e00ae1a4 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 13:45:51 +0200 Subject: [PATCH 412/656] resolved linking issues. Removed redundant plugin components and added Infill as a direct dependency. Also, updated the plugin proxy to handle the invocation and broadcasting tasks previously handled by the deleted plugin components. Updated and moved function 'prep_client_context' to be used within plugin proxy. Added a direct #include for "infill.h" in "broadcasts.h" and updated corresponding cmake to reflect the changes. Commented out the inclusion of "plugins/slots.h" in "LayerPlan.cpp" to resolve linking issues. These changes greatly simplify the code structure, improve clarity and ensure successful linking during project build. Contributes to CURA-10916 --- CMakeLists.txt | 1 + include/infill.h | 2 +- include/plugins/broadcasts.h | 11 +- include/plugins/components/broadcast.h | 105 --------------- include/plugins/components/common.h | 47 ------- include/plugins/components/invoke.h | 170 ------------------------- include/plugins/converters.h | 13 +- include/plugins/pluginproxy.h | 161 ++++++++++++++++++----- include/plugins/slots.h | 4 +- src/LayerPlan.cpp | 2 +- 10 files changed, 147 insertions(+), 369 deletions(-) delete mode 100644 include/plugins/components/broadcast.h delete mode 100644 include/plugins/components/common.h delete mode 100644 include/plugins/components/invoke.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ad72830e85..ef370f228b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,7 @@ set(engine_SRCS # Except main.cpp. src/pathPlanning/LinePolygonsCrossings.cpp src/pathPlanning/NozzleTempInsert.cpp + include/plugins/converters.h src/plugins/converters.cpp src/progress/Progress.cpp diff --git a/include/infill.h b/include/infill.h index 760de7d135..fa5ace44d1 100644 --- a/include/infill.h +++ b/include/infill.h @@ -66,7 +66,7 @@ class Infill } public: - constexpr Infill() noexcept = default; + Infill() noexcept = default; Infill( EFillMethod pattern, diff --git a/include/plugins/broadcasts.h b/include/plugins/broadcasts.h index acfaaa23ef..b73b753a64 100644 --- a/include/plugins/broadcasts.h +++ b/include/plugins/broadcasts.h @@ -9,6 +9,7 @@ #include "plugins/converters.h" #include +#include #include @@ -24,10 +25,9 @@ struct is_broadcast_channel template inline constexpr bool is_broadcast_channel_v = is_broadcast_channel::value; -template +template struct broadcast_stub { - using stub_type = slots::broadcast::v0::BroadcastService::Stub; using derived_type = T; friend derived_type; @@ -40,12 +40,11 @@ struct broadcast_stub C request_{}; }; -template +template requires is_broadcast_channel_v -struct broadcast_rpc : public broadcast_stub, broadcast_settings_request> +struct broadcast_rpc : public broadcast_stub, broadcast_settings_request> { - using base_type = broadcast_stub>; - using ClientRPC = agrpc::ClientRPC<&base_type::stub_type::PrepareAsyncBroadcastSettings>; + using ClientRPC = agrpc::ClientRPC<&Stub::PrepareAsyncBroadcastSettings>; }; diff --git a/include/plugins/components/broadcast.h b/include/plugins/components/broadcast.h deleted file mode 100644 index c1c5c03db6..0000000000 --- a/include/plugins/components/broadcast.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2023 UltiMaker -// CuraEngine is released under the terms of the AGPLv3 or higher - -#ifndef PLUGINS_BROADCASTCOMPONENT_H -#define PLUGINS_BROADCASTCOMPONENT_H - -#include "cura/plugins/v0/slot_id.pb.h" -#include "plugins/broadcasts.h" -#include "plugins/components/common.h" -#include "plugins/exception.h" -#include "plugins/metadata.h" -#include "utils/format/thread_id.h" -#include "utils/types/char_range_literal.h" -#include "utils/types/generic.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace cura::plugins -{ - -template // NOTE: Leave slot here (templated) for the case where we have to specialize broadcast-channels by slot. -class PluginProxyBroadcastComponent -{ -public: - constexpr PluginProxyBroadcastComponent() = default; - - PluginProxyBroadcastComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) - : slot_info_{ slot_info } - , plugin_info_{ plugin_info } - , broadcast_stub_{ channel } - { - } - - template - void broadcast(auto&&... args) - { - if (! plugin_info_->value().broadcast_subscriptions.contains(Subscription)) - { - return; - } - agrpc::GrpcContext grpc_context; - grpc::Status status; - - boost::asio::co_spawn( - grpc_context, - [this, &grpc_context, &status, &args...]() - { - return this->broadcastCall(grpc_context, status, std::forward(args)...); - }, - boost::asio::detached); - grpc_context.run(); - - if (! status.ok()) // TODO: handle different kind of status codes - { - if (plugin_info_->has_value()) - { - throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); - } - throw exceptions::RemoteException(*slot_info_, status.error_message()); - } - } - -private: - template - boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) - { - grpc::ClientContext client_context{}; - prep_client_context(client_context, *slot_info_); - - using request_type = details::broadcast_rpc; - request_type request{}; - - auto response = google::protobuf::Empty{}; - status = co_await request_type::ClientRPC::request( - grpc_context, - broadcast_stub_, - client_context, - request(std::forward(args)...), - response, - boost::asio::use_awaitable); - co_return; - } - - details::slot_info_ptr slot_info_; - details::plugin_info_ptr plugin_info_; - ranges::semiregular_box::stub_type> broadcast_stub_; ///< The gRPC Broadcast stub for communication. -}; - -} // namespace cura::plugins - -#endif // PLUGINS_BROADCASTCOMPONENT_H diff --git a/include/plugins/components/common.h b/include/plugins/components/common.h deleted file mode 100644 index ee292d95ad..0000000000 --- a/include/plugins/components/common.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2023 UltiMaker -// CuraEngine is released under the terms of the AGPLv3 or higher - -#ifndef PLUGINS_COMPONENTCOMMON_H -#define PLUGINS_COMPONENTCOMMON_H - -#include "cura/plugins/v0/slot_id.pb.h" -#include "plugins/metadata.h" -#include "utils/format/thread_id.h" - -#include -#include - -#include -#include -#include - -namespace cura::plugins -{ - -namespace details -{ -using slot_info_ptr = std::shared_ptr; -using plugin_info_ptr = std::shared_ptr>; -} // namespace details - -/** - * @brief Prepares client_context for the remote call. - * - * Sets timeout for the call and adds metadata to context. - * - * @param client_context - Client context to prepare - * @param timeout - Call timeout duration (optional, default = 500ms) - */ -inline static void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::minutes(5)) -{ - // Set time-out - client_context.set_deadline(std::chrono::system_clock::now() + timeout); - - // Metadata - client_context.AddMetadata("cura-engine-uuid", slot_info.engine_uuid.data()); - client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); -} - -} // namespace cura::plugins - -#endif // PLUGINS_COMPONENTCOMMON_H diff --git a/include/plugins/components/invoke.h b/include/plugins/components/invoke.h deleted file mode 100644 index e233f38bd7..0000000000 --- a/include/plugins/components/invoke.h +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2023 UltiMaker -// CuraEngine is released under the terms of the AGPLv3 or higher - -#ifndef PLUGINS_INVOKECOMPONENT_H -#define PLUGINS_INVOKECOMPONENT_H - -#include "cura/plugins/v0/slot_id.pb.h" -#include "plugins/broadcasts.h" -#include "plugins/components/common.h" -#include "plugins/exception.h" -#include "plugins/metadata.h" -#include "utils/format/thread_id.h" -#include "utils/types/char_range_literal.h" -#include "utils/types/generic.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace cura::plugins -{ -namespace details -{ - -template -concept plugin_invoker = requires(T value) -{ - requires std::is_member_function_pointer_v; -}; - -template -concept not_plugin_invoker = ! plugin_invoker; - -} // namespace details - -template -class PluginProxyInvokeComponent -{ -public: - constexpr PluginProxyInvokeComponent(); - PluginProxyInvokeComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel); - auto invoke(auto&&... args); -}; - -template -class PluginProxyInvokeComponent -{ -public: - constexpr PluginProxyInvokeComponent() = default; - - PluginProxyInvokeComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) - { - } - - auto invoke(auto&&... args) - { - assert(false); // Invoke called on a stub which it isn't meant for (for example, broadcast), should not actually be called. - return 0; - } -}; - -template -class PluginProxyInvokeComponent -{ - using value_type = typename ResponseTp::native_value_type; - - using req_converter_type = RequestTp; - using rsp_converter_type = ResponseTp; - using rsp_msg_type = typename ResponseTp::value_type; - - using invoke_stub_t = Stub; - -public: - constexpr PluginProxyInvokeComponent() = default; - - PluginProxyInvokeComponent(details::slot_info_ptr slot_info, details::plugin_info_ptr plugin_info, std::shared_ptr channel) - : slot_info_{ slot_info } - , plugin_info_{ plugin_info } - , invoke_stub_{ channel } - { - } - - /** - * @brief Executes to plugin Invoke (modify/generate) operation. - * - * As part of this operation, a request is sent to the plugin - * and the returned response is processed. - * - * @tparam Args - argument types for the plugin request - * @param args - arguments for the plugin request - * @return The converted response value from plugin. - * - * @throws std::runtime_error if communication with the plugin fails. - */ - auto invoke(auto&&... args) - { - agrpc::GrpcContext grpc_context; - value_type ret_value{}; - grpc::Status status; - - boost::asio::co_spawn( - grpc_context, - [this, &grpc_context, &status, &ret_value, &args...]() - { - return this->invokeCall(grpc_context, status, ret_value, std::forward(args)...); - }, - boost::asio::detached); - grpc_context.run(); - - if (! status.ok()) // TODO: handle different kind of status codes - { - if (plugin_info_->has_value()) - { - throw exceptions::RemoteException(*slot_info_, plugin_info_->value(), status.error_message()); - } - throw exceptions::RemoteException(*slot_info_, status.error_message()); - } - return ret_value; - } - -private: - /** - * @brief Executes the invokeCall operation with the plugin. - * - * Sends a request to the plugin and saves the response. - * - * @param grpc_context - The gRPC context to use for the call - * @param status - Status of the gRPC call which gets updated in this method - * @param ret_value - Reference to the value in which response to be stored - * @param args - Request arguments - * @return A boost::asio::awaitable indicating completion of the operation - */ - boost::asio::awaitable invokeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) - { - using RPC = agrpc::ClientRPC<&invoke_stub_t::PrepareAsyncCall>; - grpc::ClientContext client_context{}; - prep_client_context(client_context, *slot_info_); - - // Construct request - auto request{ req_(std::forward(args)...) }; - - // Make unary request - rsp_msg_type response; - status = co_await RPC::request(grpc_context, invoke_stub_, client_context, request, response, boost::asio::use_awaitable); - ret_value = rsp_(response); - co_return; - } - - req_converter_type req_{}; ///< The Invoke request converter object. - rsp_converter_type rsp_{}; ///< The Invoke response converter object. - - details::slot_info_ptr slot_info_; - details::plugin_info_ptr plugin_info_; - ranges::semiregular_box invoke_stub_; ///< The gRPC Invoke stub for communication. -}; - -} // namespace cura::plugins - -#endif // PLUGINS_INVOKECOMPONENT_H diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 083930599f..ced535407e 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -28,15 +28,12 @@ #include #include +#include "WallToolPaths.h" +#include "pathPlanning/GCodePath.h" +#include "settings/Settings.h" +#include "settings/types/LayerIndex.h" +#include "utils/polygon.h" -namespace cura -{ -class GCodePath; -class LayerIndex; -class ExtrusionLine; -class Polygons; -class Settings; -} // namespace cura namespace cura::plugins { diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index a7d0f6dac8..a7c813b48f 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -5,9 +5,6 @@ #define PLUGINS_PLUGINPROXY_H #include "Application.h" -#include "components/broadcast.h" -#include "components/common.h" -#include "components/invoke.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/handshake/v0/handshake.grpc.pb.h" @@ -34,6 +31,9 @@ #include #include +#include +#include +#include #include #include @@ -56,17 +56,18 @@ namespace cura::plugins template class PluginProxy { -public: // type aliases for easy use using value_type = typename ResponseTp::native_value_type; using validator_type = ValidatorTp; - using req_converter_type = RequestTp; using rsp_converter_type = ResponseTp; + using rsp_msg_type = typename ResponseTp::value_type; + using invoke_stub_t = Stub; + using broadcast_stub_t = slots::broadcast::v0::BroadcastService::Stub; - using invoke_component_t = PluginProxyInvokeComponent; - using broadcast_component_t = PluginProxyBroadcastComponent; - + ranges::semiregular_box invoke_stub_; ///< The gRPC Invoke stub for communication. + ranges::semiregular_box broadcast_stub_; ///< The gRPC Broadcast stub for communication. +public: /** * @brief Constructs a PluginProxy object. * @@ -81,8 +82,8 @@ class PluginProxy constexpr PluginProxy() = default; explicit PluginProxy(std::shared_ptr channel) - : invoke_component_{ slot_info_, plugin_info_, channel } - , broadcast_component_{ slot_info_, plugin_info_, channel } + : invoke_stub_{ channel } + , broadcast_stub_{ channel } { // Connect to the plugin and exchange a handshake agrpc::GrpcContext grpc_context; @@ -96,21 +97,21 @@ class PluginProxy { using RPC = agrpc::ClientRPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; grpc::ClientContext client_context{}; - prep_client_context(client_context, *slot_info_); + prep_client_context(client_context, slot_info_); // Construct request handshake_request handshake_req; - handshake_request::value_type request{ handshake_req(*slot_info_) }; + handshake_request::value_type request{ handshake_req(slot_info_) }; // Make unary request handshake_response::value_type response; status = co_await RPC::request(grpc_context, handshake_stub, client_context, request, response, boost::asio::use_awaitable); handshake_response handshake_rsp; plugin_info = handshake_rsp(response, client_context.peer()); - valid_ = validator_type{ *slot_info_, plugin_info }; + valid_ = validator_type{ slot_info_, plugin_info }; if (valid_) { - spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_->slot_id); + spdlog::info("Using plugin: '{}-{}' running at [{}] for slot {}", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info_.slot_id); if (! plugin_info.broadcast_subscriptions.empty()) { spdlog::info("Subscribing plugin '{}' to the following broadcasts {}", plugin_info.plugin_name, plugin_info.broadcast_subscriptions); @@ -122,11 +123,11 @@ class PluginProxy if (! status.ok()) // TODO: handle different kind of status codes { - throw exceptions::RemoteException(*slot_info_, status.error_message()); + throw exceptions::RemoteException(slot_info_, status.error_message()); } if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) { - plugin_info_->emplace(plugin_info); + plugin_info_.emplace(plugin_info); } }; @@ -136,21 +137,21 @@ class PluginProxy { if (this != &other) { + invoke_stub_ = other.invoke_stub_; + broadcast_stub_ = other.broadcast_stub_; valid_ = other.valid_; - invoke_component_ = other.invoke_component_; - broadcast_component_ = other.broadcast_component_; plugin_info_ = other.plugin_info_; slot_info_ = other.slot_info_; } return *this; } - constexpr PluginProxy& operator=(PluginProxy&& other) + constexpr PluginProxy& operator=(PluginProxy&& other) noexcept { if (this != &other) { + invoke_stub_ = std::move(other.invoke_stub_); + broadcast_stub_ = std::move(other.broadcast_stub_); valid_ = std::move(other.valid_); - invoke_component_ = std::move(other.invoke_component_); - broadcast_component_ = std::move(other.broadcast_component_); plugin_info_ = std::move(other.plugin_info_); slot_info_ = std::move(other.slot_info_); } @@ -160,25 +161,125 @@ class PluginProxy value_type invoke(auto&&... args) { - return invoke_component_.invoke(std::forward(args)...); + agrpc::GrpcContext grpc_context; + value_type ret_value{}; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &ret_value, &args...]() + { + return this->invokeCall(grpc_context, status, ret_value, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_.has_value()) + { + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + } + throw exceptions::RemoteException(slot_info_, status.error_message()); + } + return ret_value; } template void broadcast(auto&&... args) { - return broadcast_component_.template broadcast(std::forward(args)...); + if (! plugin_info_.value().broadcast_subscriptions.contains(Subscription)) + { + return; + } + agrpc::GrpcContext grpc_context; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &args...]() + { + return this->broadcastCall(grpc_context, status, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_.has_value()) + { + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + } + throw exceptions::RemoteException(slot_info_, status.error_message()); + } } private: - validator_type valid_{}; ///< The validator object for plugin validation. + inline static void prep_client_context(grpc::ClientContext& client_context, const slot_metadata& slot_info, const std::chrono::milliseconds& timeout = std::chrono::minutes(5)) + { + // Set time-out + client_context.set_deadline(std::chrono::system_clock::now() + timeout); - details::slot_info_ptr slot_info_ ///< Holds information about the plugin slot. - { std::make_shared(slot_metadata{ .slot_id = SlotID, .version_range = SlotVersionRng.value, .engine_uuid = Application::getInstance().instance_uuid }) }; - details::plugin_info_ptr plugin_info_{ std::make_shared>( - std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake + // Metadata + client_context.AddMetadata("cura-engine-uuid", slot_info.engine_uuid.data()); + client_context.AddMetadata("cura-thread-id", fmt::format("{}", std::this_thread::get_id())); + } - invoke_component_t invoke_component_; - broadcast_component_t broadcast_component_; + /** + * @brief Executes the invokeCall operation with the plugin. + * + * Sends a request to the plugin and saves the response. + * + * @param grpc_context - The gRPC context to use for the call + * @param status - Status of the gRPC call which gets updated in this method + * @param ret_value - Reference to the value in which response to be stored + * @param args - Request arguments + * @return A boost::asio::awaitable indicating completion of the operation + */ + boost::asio::awaitable invokeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) + { + using RPC = agrpc::ClientRPC<&invoke_stub_t::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context, slot_info_); + + // Construct request + auto request{ req_(std::forward(args)...) }; + + // Make unary request + rsp_msg_type response; + status = co_await RPC::request(grpc_context, invoke_stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = rsp_(response); + co_return; + } + + template + boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) + { + grpc::ClientContext client_context{}; + prep_client_context(client_context, slot_info_); + using RPC = agrpc::ClientRPC<&broadcast_stub_t::PrepareAsyncBroadcastSettings>; + + details::broadcast_rpc requester{}; + auto request = requester(std::forward(args)...); + + auto response = google::protobuf::Empty{}; + status = co_await RPC::request( + grpc_context, + broadcast_stub_, + client_context, + request, + response, + boost::asio::use_awaitable); + co_return; + } + + validator_type valid_{}; ///< The validator object for plugin validation. + req_converter_type req_{}; ///< The Invoke request converter object. + rsp_converter_type rsp_{}; ///< The Invoke response converter object. + slot_metadata slot_info_{ .slot_id = SlotID, + .version_range = SlotVersionRng.value, + .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. + std::optional plugin_info_{ std::optional(std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake }; } // namespace cura::plugins diff --git a/include/plugins/slots.h b/include/plugins/slots.h index bcd9040f71..d51d3d461f 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -11,6 +11,7 @@ #include "cura/plugins/slots/postprocess/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" #include "cura/plugins/v0/slot_id.pb.h" +#include "infill.h" #include "plugins/converters.h" #include "plugins/slotproxy.h" #include "plugins/types.h" @@ -22,6 +23,7 @@ #include #include +#include #include namespace cura @@ -49,7 +51,7 @@ struct simplify_default struct infill_generate_default { - std::tuple, Polygons, Polygons> operator()(Polygons _infill, auto&&... args) + std::tuple, Polygons, Polygons> operator()(auto&&... args) { // this code is only reachable when no slot is registered while the infill type is requested to be // generated by a plugin; this should not be possible to set up in the first place. Return an empty diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 273770a890..012d7ceb74 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -11,7 +11,7 @@ #include "communication/Communication.h" #include "pathPlanning/Comb.h" #include "pathPlanning/CombPaths.h" -#include "plugins/slots.h" +//#include "plugins/slots.h" #include "raft.h" // getTotalExtraLayers #include "settings/types/Ratio.h" #include "sliceDataStorage.h" From 927abbfe793c38c970f94b4e8797cb402a7ca065 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Fri, 11 Aug 2023 11:47:00 +0000 Subject: [PATCH 413/656] Applied clang-format. --- include/plugins/converters.h | 11 +++++------ include/plugins/pluginproxy.h | 8 +------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index ced535407e..74f6e0afc4 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -5,6 +5,7 @@ #define PLUGINS_CONVERTERS_H #include "Cura.pb.h" +#include "WallToolPaths.h" #include "cura/plugins/slots/broadcast/v0/broadcast.grpc.pb.h" #include "cura/plugins/slots/broadcast/v0/broadcast.pb.h" #include "cura/plugins/slots/gcode_paths/v0/modify.grpc.pb.h" @@ -17,8 +18,12 @@ #include "cura/plugins/slots/postprocess/v0/modify.pb.h" #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.pb.h" +#include "pathPlanning/GCodePath.h" #include "plugins/metadata.h" #include "plugins/types.h" +#include "settings/Settings.h" +#include "settings/types/LayerIndex.h" +#include "utils/polygon.h" #include #include @@ -28,12 +33,6 @@ #include #include -#include "WallToolPaths.h" -#include "pathPlanning/GCodePath.h" -#include "settings/Settings.h" -#include "settings/types/LayerIndex.h" -#include "utils/polygon.h" - namespace cura::plugins { diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index a7c813b48f..30a9965685 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -263,13 +263,7 @@ class PluginProxy auto request = requester(std::forward(args)...); auto response = google::protobuf::Empty{}; - status = co_await RPC::request( - grpc_context, - broadcast_stub_, - client_context, - request, - response, - boost::asio::use_awaitable); + status = co_await RPC::request(grpc_context, broadcast_stub_, client_context, request, response, boost::asio::use_awaitable); co_return; } From a9e8eaaa9d7441f6579cba814a2207a552388b07 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 14:11:36 +0200 Subject: [PATCH 414/656] push benchmark results to seperate repo --- .github/workflows/benchmark.yml | 4 +++- .github/workflows/gcodeanalyzer.yml | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 794294d01d..c020b7d86d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -155,9 +155,11 @@ jobs: with: name: C++ Benchmark output-file-path: build/Release/benchmark/benchmark_result.json + gh-repository: github.com/Ultimaker/CuraEngineBenchmarks + gh-pages-branch: main benchmark-data-dir-path: dev/bench tool: 'googlecpp' - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true alert-threshold: '175%' summary-always: true diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 94e13f053a..17ec5c436e 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -77,7 +77,7 @@ jobs: repository: 'Ultimaker/GCodeAnalyzer' ref: 'main' path: 'GCodeAnalyzer' - token: ${{ secrets.GCODEANALYZER_PAT }} + token: ${{ secrets.CURA_BENCHMARK_PAT }} - name: Checkout Test Models uses: actions/checkout@v3 @@ -297,9 +297,11 @@ jobs: with: name: CGcodeAnalyzer output-file-path: output.json + gh-repository: github.com/Ultimaker/CuraEngineBenchmarks + gh-pages-branch: main benchmark-data-dir-path: dev/gcodeanalyzer tool: customBiggerIsBetter - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true alert-threshold: '110%' summary-always: true From efefa16f613f0771854198a2476fcd4a76596a05 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 14:38:52 +0200 Subject: [PATCH 415/656] log error before throw Contribute to CURA-10619 --- include/plugins/pluginproxy.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 30a9965685..6b04941e14 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -178,8 +178,10 @@ class PluginProxy { if (plugin_info_.has_value()) { + spdlog::error("Plugin '{}' running at [{}] for slot {} failed with error: {}", plugin_info_.value().plugin_name, plugin_info_.value().peer, slot_info_.slot_id, status.error_message()); throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); } + spdlog::error("Plugin for slot {} failed with error: {}", slot_info_.slot_id, status.error_message()); throw exceptions::RemoteException(slot_info_, status.error_message()); } return ret_value; @@ -208,8 +210,10 @@ class PluginProxy { if (plugin_info_.has_value()) { + spdlog::error("Plugin '{}' running at [{}] for slot {} failed with error: {}", plugin_info_.value().plugin_name, plugin_info_.value().peer, slot_info_.slot_id, status.error_message()); throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); } + spdlog::error("Plugin for slot {} failed with error: {}", slot_info_.slot_id, status.error_message()); throw exceptions::RemoteException(slot_info_, status.error_message()); } } From d99d094769c54c419b790af223594d3cd1177d3b Mon Sep 17 00:00:00 2001 From: jellespijker Date: Fri, 11 Aug 2023 12:39:29 +0000 Subject: [PATCH 416/656] Applied clang-format. --- include/plugins/pluginproxy.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 6b04941e14..8c0ccd895c 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -178,7 +178,12 @@ class PluginProxy { if (plugin_info_.has_value()) { - spdlog::error("Plugin '{}' running at [{}] for slot {} failed with error: {}", plugin_info_.value().plugin_name, plugin_info_.value().peer, slot_info_.slot_id, status.error_message()); + spdlog::error( + "Plugin '{}' running at [{}] for slot {} failed with error: {}", + plugin_info_.value().plugin_name, + plugin_info_.value().peer, + slot_info_.slot_id, + status.error_message()); throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); } spdlog::error("Plugin for slot {} failed with error: {}", slot_info_.slot_id, status.error_message()); @@ -210,7 +215,12 @@ class PluginProxy { if (plugin_info_.has_value()) { - spdlog::error("Plugin '{}' running at [{}] for slot {} failed with error: {}", plugin_info_.value().plugin_name, plugin_info_.value().peer, slot_info_.slot_id, status.error_message()); + spdlog::error( + "Plugin '{}' running at [{}] for slot {} failed with error: {}", + plugin_info_.value().plugin_name, + plugin_info_.value().peer, + slot_info_.slot_id, + status.error_message()); throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); } spdlog::error("Plugin for slot {} failed with error: {}", slot_info_.slot_id, status.error_message()); From 33790412b61f33e4f4bfe1af2512f2f621e7aff5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 14:43:39 +0200 Subject: [PATCH 417/656] Update benchmark.yml --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index c020b7d86d..316a9e513f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -162,6 +162,6 @@ jobs: github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true alert-threshold: '175%' - summary-always: true - comment-on-alert: true +# summary-always: true +# comment-on-alert: true max-items-in-chart: 250 From b5feb01607e05738d12a9e9cc4ddd62cbef17077 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Fri, 11 Aug 2023 15:51:24 +0200 Subject: [PATCH 418/656] Revert "Temporarily disable smooth function" This reverts commit ae003e7a45c02ee7bc21eebb1c3ed8de125b2032. --- src/WallToolPaths.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index d554be2df4..c46d4afbf5 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -91,7 +91,7 @@ const std::vector& WallToolPaths::generate() scripta::log("prepared_outline_0", prepared_outline, section_type, layer_idx); prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); prepared_outline = Simplify(settings).polygon(prepared_outline); - if (false && settings.get("meshfix_fluid_motion_enabled") && section_type != SectionType::SUPPORT) + if (settings.get("meshfix_fluid_motion_enabled") && section_type != SectionType::SUPPORT) { // No need to smooth support walls auto smoother = actions::smooth(settings); From af3d7273656ed3a17bc02f8281467f2e5a82ccbd Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Sat, 12 Aug 2023 10:34:36 +0200 Subject: [PATCH 419/656] Fix potential buffer-underrun problems in infill. Infill is often printed way faster, and doesn't need these extra special fills -- it can violate the percentage of infill the user sets too. Can't believe it _was_ the infill this _whole time_, and that I reviewed but didn't register these extra bits. part of CURA-10670 (and CURA-10829 I guess) --- src/FffGcodeWriter.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fd836e57a8..1a1dc7fc46 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1618,7 +1618,7 @@ bool FffGcodeWriter::processMultiLayerInfill( } constexpr size_t wall_line_count = 0; // wall toolpaths are when gradual infill areas are determined - const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width = 0; constexpr coord_t infill_overlap = 0; // Overlap is handled when the wall toolpaths are generated constexpr bool skip_stitching = false; constexpr bool connected_zigzags = false; @@ -1835,7 +1835,7 @@ bool FffGcodeWriter::processSingleLayerInfill( // infill region with skin above has to have at least one infill wall line const size_t min_skin_below_wall_count = wall_line_count > 0 ? wall_line_count : 1; const size_t skin_below_wall_count = density_idx == last_idx ? min_skin_below_wall_count : 0; - const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width = 0; wall_tool_paths.emplace_back(std::vector()); const coord_t overlap = infill_overlap - (density_idx == last_idx ? 0 : wall_line_count * infill_line_width); Infill infill_comp( @@ -1899,7 +1899,7 @@ bool FffGcodeWriter::processSingleLayerInfill( in_outline.removeSmallAreas(minimum_small_area); constexpr size_t wall_line_count_here = 0; // Wall toolpaths were generated in generateGradualInfill for the sparsest density, denser parts don't have walls by default - const coord_t small_area_width = mesh.settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width = 0; constexpr coord_t overlap = 0; // overlap is already applied for the sparsest density in the generateGradualInfill wall_tool_paths.emplace_back(); @@ -3112,8 +3112,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const Polygons& area = part.infill_area_per_combine_per_density[density_idx][combine_idx]; constexpr size_t wall_count = 0; // Walls are generated somewhere else, so their layers aren't vertically combined. - const coord_t small_area_width - = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const coord_t small_area_width = 0; constexpr bool skip_stitching = false; const bool fill_gaps = density_idx == 0; // Only fill gaps for one of the densities. Infill infill_comp( From ac86c0338fc5620467d75095f64abd66110d9a8f Mon Sep 17 00:00:00 2001 From: rburema Date: Sat, 12 Aug 2023 08:57:29 +0000 Subject: [PATCH 420/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 705053e0a9..12d66b9c58 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2975,7 +2975,8 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { bool added_something = false; - const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; // account for negative layer numbers for raft filler layers + const SupportLayer& support_layer + = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; // account for negative layer numbers for raft filler layers if (gcode_layer.getLayerNr() > storage.support.layer_nr_max_filled_layer || support_layer.support_infill_parts.empty()) { From 19046816e37df728dfeb1c5e568baeb0ff04119b Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 14 Aug 2023 10:54:00 +0200 Subject: [PATCH 421/656] Fix compiling on mac --- include/plugins/exception.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/plugins/exception.h b/include/plugins/exception.h index 93f05638eb..7aeb14874d 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -1,8 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#ifndef UTILS_CONCEPTS_GRAPH_H -#define UTILS_CONCEPTS_GRAPH_H +#ifndef PLUGINS_EXCEPTIONS_H +#define PLUGINS_EXCEPTIONS_H #include "plugins/metadata.h" #include "plugins/types.h" @@ -70,4 +70,4 @@ class RemoteException : public std::exception } // namespace cura::plugins::exceptions -#endif // UTILS_CONCEPTS_GRAPH_H \ No newline at end of file +#endif // PLUGINS_EXCEPTIONS_H \ No newline at end of file From 2c43157b04c74424b40fd73f4499f380fd14bfdb Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 14 Aug 2023 13:26:58 +0200 Subject: [PATCH 422/656] Add missing slot string formats CURA-10446 --- include/plugins/types.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/plugins/types.h b/include/plugins/types.h index e46c2562b8..1585d5ff47 100644 --- a/include/plugins/types.h +++ b/include/plugins/types.h @@ -27,12 +27,24 @@ struct formatter switch (slot_id) { + case cura::plugins::v0::SlotID::SETTINGS_BROADCAST: + slot_name = "SettingsBroadcastService"; + break; case cura::plugins::v0::SlotID::SIMPLIFY_MODIFY: slot_name = "SimplifyService"; break; case cura::plugins::v0::SlotID::POSTPROCESS_MODIFY: slot_name = "PostprocessService"; break; + case cura::plugins::v0::SlotID::INFILL_MODIFY: + slot_name = "InfillModifyService"; + break; + case cura::plugins::v0::SlotID::GCODE_PATHS_MODIFY: + slot_name = "GcodePathsModifyService"; + break; + case cura::plugins::v0::SlotID::INFILL_GENERATE: + slot_name = "InfillGenerateService"; + break; default: slot_name = "Unknown"; break; From 57b47721092792c4341f1bb395d3dcf5d5a0e069 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 15 Aug 2023 11:11:59 +0200 Subject: [PATCH 423/656] Use precompiled grpc definitions Contribute to CURA-10619 --- CMakeLists.txt | 15 ++++----------- conanfile.py | 20 +++++--------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef370f228b..25471c12cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,25 +15,19 @@ option(ENABLE_REMOTE_PLUGINS "Build with all warnings" OFF) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) option(OLDER_APPLE_CLANG "Apple Clang <= 13 used" OFF) -#set(GRPC_PROTOS "List of all protobuf definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") -#set(GRPC_IMPORT_DIRS "List of all protobuf dirs definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") # Generate the plugin types find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) find_package(gRPC REQUIRED) -option(RETARDED_APPLE_CLANG "Apple Clang <= 13 used" OFF) +find_package(curaengine_grpc_definitions REQUIRED) +option(OLDER_APPLE_CLANG "Apple Clang <= 13 used" OFF) MESSAGE(STATUS "Compiling with plugins support: ${ENABLE_PLUGINS}") if (${ENABLE_PLUGINS}) MESSAGE(STATUS "Plugin secure remotes allowed: ${ENABLE_REMOTE_PLUGINS}") endif () -asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" IMPORT_DIRS ${GRPC_IMPORT_DIRS} - OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" - OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" - GENERATE_GRPC GENERATE_MOCK_CODE) - if (ENABLE_ARCUS) message(STATUS "Building with Arcus") find_package(arcus REQUIRED) @@ -115,7 +109,6 @@ set(engine_SRCS # Except main.cpp. src/pathPlanning/LinePolygonsCrossings.cpp src/pathPlanning/NozzleTempInsert.cpp - include/plugins/converters.h src/plugins/converters.cpp src/progress/Progress.cpp @@ -157,7 +150,7 @@ set(engine_SRCS # Except main.cpp. src/utils/VoxelUtils.cpp ) -add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS} ${ASIO_GRPC_PLUGIN_PROTO_SOURCES}) +add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) use_threads(_CuraEngine) target_include_directories(_CuraEngine @@ -206,7 +199,6 @@ find_package(fmt REQUIRED) find_package(range-v3 REQUIRED) find_package(scripta REQUIRED) find_package(neargye-semver REQUIRED) -find_package(gRPC REQUIRED) if (ENABLE_TESTING) find_package(GTest REQUIRED) @@ -223,6 +215,7 @@ target_link_libraries(_CuraEngine boost::boost scripta::scripta neargye-semver::neargye-semver + curaengine_grpc_definitions::curaengine_grpc_definitions asio-grpc::asio-grpc grpc::grpc protobuf::libprotobuf diff --git a/conanfile.py b/conanfile.py index 3f48b98d9d..48a1345cca 100644 --- a/conanfile.py +++ b/conanfile.py @@ -65,14 +65,6 @@ def configure(self): self.options["clipper"].shared = True self.options["protobuf"].shared = False - self.options["grpc"].csharp_plugin = False - self.options["grpc"].node_plugin = False - self.options["grpc"].objective_c_plugin = False - self.options["grpc"].php_plugin = False - self.options["grpc"].python_plugin = False - self.options["grpc"].ruby_plugin = False - self.options["asio-grpc"].backend = "boost" - self.options["asio-grpc"].local_allocator = "recycling_allocator" if self.options.enable_arcus: self.options["arcus"].shared = True @@ -94,8 +86,11 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: self.requires("arcus/(latest)@ultimaker/cura_10475") # TODO: point to `testing` once the CURA-10475 from libArcus is main + self.requires("asio-grpc/2.6.0") + self.requires("grpc/1.50.1") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") self.requires("clipper/6.4.2") - self.requires("boost/1.81.0") + self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") self.requires("stb/20200203") self.requires("spdlog/1.10.0") @@ -106,8 +101,7 @@ def requirements(self): self.requires("protobuf/3.21.9") self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") - self.requires("asio-grpc/2.6.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") + def generate(self): deps = CMakeDeps(self) @@ -125,10 +119,6 @@ def generate(self): tc.variables["ENABLE_REMOTE_PLUGINS"] = self.options.enable_remote_plugins else: tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins - cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info - tc.variables["GRPC_IMPORT_DIRS"] = cpp_info.resdirs[0].replace("\\", "/") - tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).rglob("*.proto")]) - tc.generate() for dep in self.dependencies.values(): From 286a80418d19816b6d9e0d8fb0e000cd1b5a2858 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 15 Aug 2023 11:11:59 +0200 Subject: [PATCH 424/656] Use precompiled grpc definitions Contribute to CURA-10619 --- CMakeLists.txt | 14 ++++---------- conanfile.py | 20 +++++--------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad72830e85..25471c12cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,25 +15,19 @@ option(ENABLE_REMOTE_PLUGINS "Build with all warnings" OFF) option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON) option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF) option(OLDER_APPLE_CLANG "Apple Clang <= 13 used" OFF) -#set(GRPC_PROTOS "List of all protobuf definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") -#set(GRPC_IMPORT_DIRS "List of all protobuf dirs definitions (see: https://github.com/Ultimaker/curaengine_grpc_definitions)") # Generate the plugin types find_package(protobuf REQUIRED) find_package(asio-grpc REQUIRED) find_package(gRPC REQUIRED) -option(RETARDED_APPLE_CLANG "Apple Clang <= 13 used" OFF) +find_package(curaengine_grpc_definitions REQUIRED) +option(OLDER_APPLE_CLANG "Apple Clang <= 13 used" OFF) MESSAGE(STATUS "Compiling with plugins support: ${ENABLE_PLUGINS}") if (${ENABLE_PLUGINS}) MESSAGE(STATUS "Plugin secure remotes allowed: ${ENABLE_REMOTE_PLUGINS}") endif () -asio_grpc_protobuf_generate(PROTOS "${GRPC_PROTOS}" IMPORT_DIRS ${GRPC_IMPORT_DIRS} - OUT_VAR "ASIO_GRPC_PLUGIN_PROTO_SOURCES" - OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated" - GENERATE_GRPC GENERATE_MOCK_CODE) - if (ENABLE_ARCUS) message(STATUS "Building with Arcus") find_package(arcus REQUIRED) @@ -156,7 +150,7 @@ set(engine_SRCS # Except main.cpp. src/utils/VoxelUtils.cpp ) -add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS} ${ASIO_GRPC_PLUGIN_PROTO_SOURCES}) +add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) use_threads(_CuraEngine) target_include_directories(_CuraEngine @@ -205,7 +199,6 @@ find_package(fmt REQUIRED) find_package(range-v3 REQUIRED) find_package(scripta REQUIRED) find_package(neargye-semver REQUIRED) -find_package(gRPC REQUIRED) if (ENABLE_TESTING) find_package(GTest REQUIRED) @@ -222,6 +215,7 @@ target_link_libraries(_CuraEngine boost::boost scripta::scripta neargye-semver::neargye-semver + curaengine_grpc_definitions::curaengine_grpc_definitions asio-grpc::asio-grpc grpc::grpc protobuf::libprotobuf diff --git a/conanfile.py b/conanfile.py index 3f48b98d9d..48a1345cca 100644 --- a/conanfile.py +++ b/conanfile.py @@ -65,14 +65,6 @@ def configure(self): self.options["clipper"].shared = True self.options["protobuf"].shared = False - self.options["grpc"].csharp_plugin = False - self.options["grpc"].node_plugin = False - self.options["grpc"].objective_c_plugin = False - self.options["grpc"].php_plugin = False - self.options["grpc"].python_plugin = False - self.options["grpc"].ruby_plugin = False - self.options["asio-grpc"].backend = "boost" - self.options["asio-grpc"].local_allocator = "recycling_allocator" if self.options.enable_arcus: self.options["arcus"].shared = True @@ -94,8 +86,11 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: self.requires("arcus/(latest)@ultimaker/cura_10475") # TODO: point to `testing` once the CURA-10475 from libArcus is main + self.requires("asio-grpc/2.6.0") + self.requires("grpc/1.50.1") + self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") self.requires("clipper/6.4.2") - self.requires("boost/1.81.0") + self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") self.requires("stb/20200203") self.requires("spdlog/1.10.0") @@ -106,8 +101,7 @@ def requirements(self): self.requires("protobuf/3.21.9") self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") - self.requires("asio-grpc/2.6.0") - self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") + def generate(self): deps = CMakeDeps(self) @@ -125,10 +119,6 @@ def generate(self): tc.variables["ENABLE_REMOTE_PLUGINS"] = self.options.enable_remote_plugins else: tc.variables["ENABLE_PLUGINS"] = self.options.enable_plugins - cpp_info = self.dependencies["curaengine_grpc_definitions"].cpp_info - tc.variables["GRPC_IMPORT_DIRS"] = cpp_info.resdirs[0].replace("\\", "/") - tc.variables["GRPC_PROTOS"] = ";".join([str(p).replace("\\", "/") for p in Path(cpp_info.resdirs[0]).rglob("*.proto")]) - tc.generate() for dep in self.dependencies.values(): From d64b33682934dbb42c41f91c253c10d10c1465d1 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 15 Aug 2023 12:40:32 +0200 Subject: [PATCH 425/656] Update converter and proxy classes to include name and version of plugins Modified the operator() in converter classes to include arguments for plugin name and version. Updated SlotProxy and PluginProxy constructors to accommodate these changes. Also updated the handshake request to include the plugin name and version in its request. This change is necessary to facilitate identification and version control for plugins during the handshake process. Contributes to CURA-10619 --- Cura.proto | 6 ++++-- include/plugins/converters.h | 2 +- include/plugins/pluginproxy.h | 6 +++--- include/plugins/slotproxy.h | 4 ++-- include/plugins/slots.h | 15 +++++++-------- src/communication/ArcusCommunication.cpp | 7 ++----- src/plugins/converters.cpp | 8 +++----- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/Cura.proto b/Cura.proto index ceb294f619..960396c5a8 100644 --- a/Cura.proto +++ b/Cura.proto @@ -24,8 +24,10 @@ enum SlotID { message EnginePlugin { SlotID id = 1; - optional string address = 2; - optional uint32 port = 3; + string address = 2; + uint32 port = 3; + string plugin_name = 4; + string plugin_version = 5; } message Slice diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 74f6e0afc4..f25f6ea1ce 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -71,7 +71,7 @@ struct broadcast_settings_request : public details::converter { - value_type operator()(const native_value_type& slot_info) const; + value_type operator()(const std::string& name, const std::string& version, const native_value_type& slot_info) const; }; struct handshake_response : public details::converter diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 8c0ccd895c..88908c0fe5 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -81,7 +81,7 @@ class PluginProxy */ constexpr PluginProxy() = default; - explicit PluginProxy(std::shared_ptr channel) + PluginProxy(const std::string& name, const std::string& version, std::shared_ptr channel) : invoke_stub_{ channel } , broadcast_stub_{ channel } { @@ -93,7 +93,7 @@ class PluginProxy boost::asio::co_spawn( grpc_context, - [this, &grpc_context, &status, &plugin_info, &handshake_stub]() -> boost::asio::awaitable + [this, &grpc_context, &status, &plugin_info, &handshake_stub, &name, &version]() -> boost::asio::awaitable { using RPC = agrpc::ClientRPC<&slots::handshake::v0::HandshakeService::Stub::PrepareAsyncCall>; grpc::ClientContext client_context{}; @@ -101,7 +101,7 @@ class PluginProxy // Construct request handshake_request handshake_req; - handshake_request::value_type request{ handshake_req(slot_info_) }; + handshake_request::value_type request{ handshake_req(name, version, slot_info_) }; // Make unary request handshake_response::value_type response; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index d51533881e..6562f7660a 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -60,8 +60,8 @@ class SlotProxy * * @param channel A shared pointer to the gRPC channel for communication with the plugin. */ - SlotProxy(std::shared_ptr channel) - : plugin_{ std::move(channel) } {}; + SlotProxy(const std::string& name, const std::string& version, std::shared_ptr channel) + : plugin_{ value_type{ name, version, channel } } {}; /** * @brief Executes the plugin operation. diff --git a/include/plugins/slots.h b/include/plugins/slots.h index d51d3d461f..97849c49fa 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -124,13 +124,12 @@ template class Unit> class Registry, Unit> { public: - void connect(const v0::SlotID& slot_id, auto&& channel) + constexpr void connect(auto&&... args) noexcept { - assert(false); - } // Base case, should not be executed + } template - void broadcast(auto&&... args) + constexpr void broadcast(auto&&... args) noexcept { } // Base case, do nothing }; @@ -162,15 +161,15 @@ class Registry, Unit> : public Registry return get().invoke(std::forward(args)...); } - void connect(const v0::SlotID& slot_id, auto&& channel) + void connect(const v0::SlotID& slot_id, auto name, auto& version, auto&& channel) { if (slot_id == T::slot_id) { - using Tp = decltype(get_type().proxy); - get_type().proxy = Tp{ std::forward(std::move(channel)) }; + using Tp = Unit::value_type; + value_.proxy = Tp{ name, version, std::forward(channel) }; return; } - Base::connect(slot_id, channel); + Base::connect(slot_id, name, version, std::forward(channel)); } template diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 0df3ac998d..c01222bd94 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -519,11 +519,8 @@ void ArcusCommunication::sliceNext() #ifdef ENABLE_PLUGINS for (const auto& plugin : slice_message->engine_plugins()) { - if (plugin.has_address() && plugin.has_port()) - { - const auto slot_id = static_cast(plugin.id()); - slots::instance().connect(slot_id, utils::createChannel({ plugin.address(), plugin.port() })); - } + const auto slot_id = static_cast(plugin.id()); + slots::instance().connect(slot_id, plugin.plugin_name(), plugin.plugin_version(), utils::createChannel({ plugin.address(), plugin.port() })); } #endif // ENABLE_PLUGINS diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index e022497894..1ba7d5cb6f 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -60,11 +60,13 @@ broadcast_settings_request::value_type broadcast_settings_request::operator()(co return message; } -handshake_request::value_type handshake_request::operator()(const handshake_request::native_value_type& slot_info) const +handshake_request::value_type handshake_request::operator()(const std::string& name, const std::string& version, const handshake_request::native_value_type& slot_info) const { value_type message{}; message.set_slot_id(slot_info.slot_id); message.set_version_range(slot_info.version_range.data()); + message.set_plugin_name(name); + message.set_plugin_version(version); return message; } @@ -276,10 +278,6 @@ gcode_paths_modify_request::value_type points->set_x(point.X); points->set_y(point.Y); } - - // Construct the estimations for the GCodePath - auto* estimations = gcode_path->mutable_estimates(); - estimations->set_extrude_time(path.estimates.extrude_time); } From 2bfee59cea7ed7d5ead6eb1e144cbba43a05ba92 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 15 Aug 2023 12:58:24 +0200 Subject: [PATCH 426/656] Fix compiling CURA-10446 --- include/plugins/slots.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 97849c49fa..6e0f81b949 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -165,7 +165,7 @@ class Registry, Unit> : public Registry { if (slot_id == T::slot_id) { - using Tp = Unit::value_type; + using Tp = typename Unit::value_type; value_.proxy = Tp{ name, version, std::forward(channel) }; return; } From b82b8d00bec3069e17f83e31ff68554c63868e72 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 15 Aug 2023 12:58:24 +0200 Subject: [PATCH 427/656] Fix compiling CURA-10446 --- include/plugins/slots.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 97849c49fa..6e0f81b949 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -165,7 +165,7 @@ class Registry, Unit> : public Registry { if (slot_id == T::slot_id) { - using Tp = Unit::value_type; + using Tp = typename Unit::value_type; value_.proxy = Tp{ name, version, std::forward(channel) }; return; } From 9292a0f0380296db2c9cd5539f94459ef2b5c04c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 15 Aug 2023 13:05:40 +0200 Subject: [PATCH 428/656] Fix benchmark test Contributes to CURA-10619 --- benchmark/simplify_benchmark.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index 6320bb7b36..ce28ee1985 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -79,7 +79,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::St try { - slots::instance().connect(plugins::v0::SlotID::SIMPLIFY_MODIFY, utils::createChannel({ host, port })); + slots::instance().connect(plugins::v0::SlotID::SIMPLIFY_MODIFY, "", "", utils::createChannel({ host, port })); } catch (std::runtime_error e) { From 6a1c241d7bf8f9fb9eec1436de0c9aa100c7a89c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 15 Aug 2023 15:07:58 +0200 Subject: [PATCH 429/656] Implement basic gcode modify slot Still contains some todo's CURA-10446 --- include/pathPlanning/GCodePath.h | 29 +++-- src/LayerPlan.cpp | 96 +++++++++-------- src/pathPlanning/GCodePath.cpp | 12 +-- src/plugins/converters.cpp | 176 ++++++++++++++++++++++++++++++- src/utils/polygonUtils.cpp | 2 +- tests/ExtruderPlanTest.cpp | 4 +- 6 files changed, 249 insertions(+), 70 deletions(-) diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index e845fd1df4..4cd76687fe 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -1,41 +1,40 @@ -//Copyright (c) 2022 Ultimaker B.V. +//Copyright (c) 2023 UltiMaker //CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef PATH_PLANNING_G_CODE_PATH_H #define PATH_PLANNING_G_CODE_PATH_H #include "../SpaceFillType.h" -#include "../sliceDataStorage.h" #include "../settings/types/Ratio.h" +#include "../sliceDataStorage.h" #include "../utils/IntPoint.h" +#include "GCodePathConfig.h" #include "TimeMaterialEstimates.h" -namespace cura +namespace cura { -class GCodePathConfig; - /*! * A class for representing a planned path. - * + * * A path consists of several segments of the same type of movement: retracted travel, infill extrusion, etc. - * + * * This is a compact premature representation in which are line segments have the same config, i.e. the config of this path. - * - * In the final representation (gcode) each line segment may have different properties, + * + * In the final representation (gcode) each line segment may have different properties, * which are added when the generated GCodePaths are processed. */ class GCodePath { public: - const GCodePathConfig* config; //!< The configuration settings of the path. + const GCodePathConfig config; //!< The configuration settings of the path. const SliceMeshStorage* mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part Ratio flow; //!< A type-independent flow configuration Ratio width_factor; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. Ratio speed_factor; //!< A speed factor that is multiplied with the travel speed. This factor can be used to change the travel speed. Ratio speed_back_pressure_factor; // #include @@ -89,8 +90,8 @@ void ExtruderPlan::applyBackPressureCompensation(const Ratio back_pressure_compe constexpr double epsilon_speed_factor = 0.001; // Don't put on actual 'limit double minimum', because we don't want printers to stall. for (auto& path : paths) { - const double nominal_width_for_path = static_cast(path.config->getLineWidth()); - if (path.width_factor <= 0.0 || nominal_width_for_path <= 0.0 || path.config->isTravelPath() || path.config->isBridgePath()) + const double nominal_width_for_path = static_cast(path.config.getLineWidth()); + if (path.width_factor <= 0.0 || nominal_width_for_path <= 0.0 || path.config.isTravelPath() || path.config.isBridgePath()) { continue; } @@ -108,8 +109,9 @@ GCodePath* LayerPlan::getLatestPathWithConfig( const Ratio speed_factor) { std::vector& paths = extruder_plans.back().paths; - if (paths.size() > 0 && paths.back().config == &config && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor - && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between + // TODO put back config equality check + if (paths.size() > 0 /*&& paths.back().config == config*/ && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor + && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } @@ -575,7 +577,7 @@ void LayerPlan::addExtrusionMove( path->setFanSpeed(fan_speed); if (! static_cast(first_extrusion_acc_jerk)) { - first_extrusion_acc_jerk = std::make_pair(path->config->getAcceleration(), path->config->getJerk()); + first_extrusion_acc_jerk = std::make_pair(path->config.getAcceleration(), path->config.getJerk()); } last_planned_position = p; } @@ -1612,7 +1614,7 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ double target_speed = 0.0; std::function slow_down_func{ [&target_speed](const GCodePath& path) { - return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); + return std::min(target_speed / (path.config.getSpeed() * path.speed_factor), 1.0); } }; if (minExtrudeTime >= total_extrude_time_at_minimum_speed) @@ -1647,8 +1649,8 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double time_other_extr_ factor = (1 / total_extrude_time_at_slowest_speed - 1 / minExtrudeTime) / (1 / total_extrude_time_at_slowest_speed - 1 / extrudeTime); slow_down_func = [&slowest_path_speed = slowest_path_speed, &factor](const GCodePath& path) { - const double target_speed = slowest_path_speed * (1.0 - factor) + (path.config->getSpeed() * path.speed_factor) * factor; - return std::min(target_speed / (path.config->getSpeed() * path.speed_factor), 1.0); + const double target_speed = slowest_path_speed * (1.0 - factor) + (path.config.getSpeed() * path.speed_factor) * factor; + return std::min(target_speed / (path.config.getSpeed() * path.speed_factor), 1.0); }; // Update stored naive time estimates @@ -1676,7 +1678,7 @@ double ExtruderPlan::getRetractTime(const GCodePath& path) std::pair ExtruderPlan::getPointToPointTime(const Point& p0, const Point& p1, const GCodePath& path) { const double length = vSizeMM(p0 - p1); - return { length, length / (path.config->getSpeed() * path.speed_factor) }; + return { length, length / (path.config.getSpeed() * path.speed_factor) }; } TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point starting_position) @@ -1690,7 +1692,7 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point starting_pos std::numeric_limits::max(), [](double value, const GCodePath& path) { - return path.isTravelPath() ? value : std::min(value, path.config->getSpeed().value * path.speed_factor); + return path.isTravelPath() ? value : std::min(value, path.config.getSpeed().value * path.speed_factor); }); bool was_retracted = false; // wrong assumption; won't matter that much. (TODO) @@ -1743,9 +1745,9 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point starting_pos path.estimates.extrude_time_at_minimum_speed += length / min_path_speed; path.estimates.extrude_time_at_slowest_path_speed += length / slowest_path_speed; } - material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth()); + material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config.getLineWidth()); } - double thisTime = length / (path.config->getSpeed() * path.speed_factor); + double thisTime = length / (path.config.getSpeed() * path.speed_factor); *path_time_estimate += thisTime; p0 = p1; } @@ -1896,7 +1898,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.setZ(z); - const GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode. + std::optional last_extrusion_config = std::nullopt; // used to check whether we need to insert a TYPE comment in the gcode. size_t extruder_nr = gcode.getExtruderNr(); const bool acceleration_enabled = mesh_group_settings.get("acceleration_enabled"); @@ -1909,7 +1911,14 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; - // TODO: Insert modify slot for the gcodepaths CURA-10446 + extruder_plan.paths = slots::instance().generate( + extruder_plan.paths, + extruder_plan.extruder_nr, + layer_nr + ); + + // Since the time/material estimates _may_ have changed during the plugin modify step we recalculate it + extruder_plan.computeNaiveTimeEstimates(gcode.getPositionXY()); const RetractionAndWipeConfig* retraction_config = current_mesh ? ¤t_mesh->retraction_wipe_config : &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; @@ -2005,7 +2014,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeRetraction(retraction_config->retraction_config); } - if (! path.retract && path.config->isTravelPath() && path.points.size() == 1 && path.points[0] == gcode.getPositionXY() && z == gcode.getPositionZ()) + if (! path.retract && path.config.isTravelPath() && path.points.size() == 1 && path.points[0] == gcode.getPositionXY() && z == gcode.getPositionZ()) { // ignore travel moves to the current location to avoid needless change of acceleration/jerk continue; @@ -2015,7 +2024,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) size_t next_extrusion_idx = path_idx + 1; if ((acceleration_enabled && ! acceleration_travel_enabled) || (jerk_enabled && ! jerk_travel_enabled)) { - while (next_extrusion_idx < paths.size() && paths[next_extrusion_idx].config->isTravelPath()) + while (next_extrusion_idx < paths.size() && paths[next_extrusion_idx].config.isTravelPath()) { ++next_extrusion_idx; } @@ -2023,11 +2032,11 @@ void LayerPlan::writeGCode(GCodeExport& gcode) if (acceleration_enabled) { - if (path.config->isTravelPath()) + if (path.config.isTravelPath()) { if (acceleration_travel_enabled) { - gcode.writeTravelAcceleration(path.config->getAcceleration()); + gcode.writeTravelAcceleration(path.config.getAcceleration()); } else { @@ -2041,20 +2050,20 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } else { - gcode.writeTravelAcceleration(paths[next_extrusion_idx].config->getAcceleration()); + gcode.writeTravelAcceleration(paths[next_extrusion_idx].config.getAcceleration()); } } } else { - gcode.writePrintAcceleration(path.config->getAcceleration()); + gcode.writePrintAcceleration(path.config.getAcceleration()); } } if (jerk_enabled) { if (jerk_travel_enabled) { - gcode.writeJerk(path.config->getJerk()); + gcode.writeJerk(path.config.getJerk()); } else { @@ -2068,7 +2077,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } else { - gcode.writeJerk(paths[next_extrusion_idx].config->getJerk()); + gcode.writeJerk(paths[next_extrusion_idx].config.getJerk()); } } } @@ -2088,14 +2097,17 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeZhopEnd(); } } - if (! path.config->isTravelPath() && last_extrusion_config != path.config) + // TODO re-enable the config_changed check + const auto& extruder_changed = !last_extrusion_config.has_value() /* || last_extrusion_config.value() != path.config */; + if (! path.config.isTravelPath() && extruder_changed) { - gcode.writeTypeComment(path.config->type); - if (path.config->isBridgePath()) + gcode.writeTypeComment(path.config.type); + if (path.config.isBridgePath()) { gcode.writeComment("BRIDGE"); } - last_extrusion_config = path.config; + // TODO uncomment next line, make path.config copyable +// last_extrusion_config = path.config; update_extrusion_offset = true; } else @@ -2103,7 +2115,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) update_extrusion_offset = false; } - double speed = path.config->getSpeed(); + double speed = path.config.getSpeed(); // for some movements such as prime tower purge, the speed may get changed by this factor speed *= path.speed_factor; @@ -2116,7 +2128,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) ss << "MESH:" << (current_mesh ? current_mesh->mesh_name : "NONMESH"); gcode.writeComment(ss.str()); } - if (path.config->isTravelPath()) + if (path.config.isTravelPath()) { // early comp for travel paths, which are handled more simply if (! path.perform_z_hop && final_travel_z != z && extruder_plan_idx == (extruder_plans.size() - 1) && path_idx == (paths.size() - 1)) { @@ -2162,8 +2174,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) insertTempOnTime(time, path_idx); const double extrude_speed = speed * path.speed_back_pressure_factor; - communication->sendLineTo(path.config->type, path.points[point_idx], path.getLineWidthForLayerView(), path.config->getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config->type, update_extrusion_offset); + communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type, update_extrusion_offset); prev_point = path.points[point_idx]; } @@ -2199,8 +2211,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.setZ(std::round(z + layer_thickness * length / totalLength)); const double extrude_speed = speed * path.speed_back_pressure_factor; - communication->sendLineTo(path.config->type, path.points[point_idx], path.getLineWidthForLayerView(), path.config->getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config->type, update_extrusion_offset); + communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type, update_extrusion_offset); } // for layer display only - the loop finished at the seam vertex but as we started from // the location of the previous layer's seam vertex the loop may have a gap if this layer's @@ -2211,7 +2223,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // vertex would not be shifted (as it's the last vertex in the sequence). The smoother the model, // the less the vertices are shifted and the less obvious is the ridge. If the layer display // really displayed a spiral rather than slices of a spiral, this would not be required. - communication->sendLineTo(path.config->type, path.points[0], path.getLineWidthForLayerView(), path.config->getLayerThickness(), speed); + communication->sendLineTo(path.config.type, path.points[0], path.getLineWidthForLayerView(), path.config.getLayerThickness(), speed); } path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path } @@ -2289,20 +2301,20 @@ bool LayerPlan::writePathWithCoasting( } const std::vector& paths = extruder_plan.paths; const GCodePath& path = paths[path_idx]; - if (path_idx + 1 >= paths.size() || (path.isTravelPath() || ! paths[path_idx + 1].config->isTravelPath()) || path.points.size() < 2) + if (path_idx + 1 >= paths.size() || (path.isTravelPath() || ! paths[path_idx + 1].config.isTravelPath()) || path.points.size() < 2) { return false; } coord_t coasting_min_dist_considered = MM2INT(0.1); // hardcoded setting for when to not perform coasting - const double extrude_speed = path.config->getSpeed() * path.speed_factor * path.speed_back_pressure_factor; + const double extrude_speed = path.config.getSpeed() * 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 + = 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 + / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues // /\ 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...) @@ -2379,13 +2391,13 @@ bool LayerPlan::writePathWithCoasting( auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); insertTempOnTime(time, path_idx); - communication->sendLineTo(path.config->type, path.points[point_idx], path.getLineWidthForLayerView(), path.config->getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config->type); + communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type); prev_pt = path.points[point_idx]; } - communication->sendLineTo(path.config->type, start, path.getLineWidthForLayerView(), path.config->getLayerThickness(), extrude_speed); - gcode.writeExtrusion(start, extrude_speed, path.getExtrusionMM3perMM(), path.config->type); + communication->sendLineTo(path.config.type, start, path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + gcode.writeExtrusion(start, extrude_speed, path.getExtrusionMM3perMM(), path.config.type); } // write coasting path @@ -2395,7 +2407,7 @@ bool LayerPlan::writePathWithCoasting( insertTempOnTime(time, path_idx); const Ratio coasting_speed_modifier = extruder.settings.get("coasting_speed"); - const Velocity speed = Velocity(coasting_speed_modifier * path.config->getSpeed()); + const Velocity speed = Velocity(coasting_speed_modifier * path.config.getSpeed()); gcode.writeTravel(path.points[point_idx], speed); prev_pt = path.points[point_idx]; diff --git a/src/pathPlanning/GCodePath.cpp b/src/pathPlanning/GCodePath.cpp index 3c4ed61633..99cefde899 100644 --- a/src/pathPlanning/GCodePath.cpp +++ b/src/pathPlanning/GCodePath.cpp @@ -6,8 +6,8 @@ namespace cura { -GCodePath::GCodePath(const GCodePathConfig& config, const SliceMeshStorage* mesh, const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor) : - config(&config), +GCodePath::GCodePath(const GCodePathConfig config, const SliceMeshStorage* mesh, const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor) : + config(config), mesh(mesh), space_fill_type(space_fill_type), flow(flow), @@ -29,17 +29,17 @@ GCodePath::GCodePath(const GCodePathConfig& config, const SliceMeshStorage* mesh bool GCodePath::isTravelPath() const { - return config->isTravelPath(); + return config.isTravelPath(); } double GCodePath::getExtrusionMM3perMM() const { - return flow * width_factor * config->getExtrusionMM3perMM(); + return flow * width_factor * config.getExtrusionMM3perMM(); } coord_t GCodePath::getLineWidthForLayerView() const { - return flow * width_factor * config->getLineWidth() * config->getFlowRatio(); + return flow * width_factor * config.getLineWidth() * config.getFlowRatio(); } void GCodePath::setFanSpeed(double fan_speed) @@ -49,7 +49,7 @@ void GCodePath::setFanSpeed(double fan_speed) double GCodePath::getFanSpeed() const { - return (fan_speed >= 0 && fan_speed <= 100) ? fan_speed : config->getFanSpeed(); + return (fan_speed >= 0 && fan_speed <= 100) ? fan_speed : config.getFanSpeed(); } } diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 1ba7d5cb6f..6f123d45a4 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -4,12 +4,16 @@ #include "plugins/converters.h" +#include "GCodePathConfig.h" #include "WallToolPaths.h" #include "pathPlanning/GCodePath.h" #include "settings/Settings.h" #include "settings/types/LayerIndex.h" #include "utils/polygon.h" +#include +#include + namespace cura::plugins { @@ -271,22 +275,186 @@ gcode_paths_modify_request::value_type { auto* gcode_path = gcode_paths->Add(); + switch (path.space_fill_type){ + case SpaceFillType::None: + gcode_path->set_space_fill_type(v0::SpaceFillType::NONE); + break; + case SpaceFillType::Polygons: + gcode_path->set_space_fill_type(v0::SpaceFillType::POLYGONS); + break; + case SpaceFillType::PolyLines: + gcode_path->set_space_fill_type(v0::SpaceFillType::POLY_LINES); + break; + case SpaceFillType::Lines: + gcode_path->set_space_fill_type(v0::SpaceFillType::LINES); + break; + default: + gcode_path->set_space_fill_type(v0::SpaceFillType::NONE); + } + + gcode_path->set_flow(path.flow); + gcode_path->set_width_factor(path.width_factor); + spdlog::info("path.spiralize: {}", path.spiralize); + gcode_path->set_spiralize(path.spiralize); + gcode_path->set_speed_factor(path.speed_factor); + // Construct the OpenPath from the points in a GCodePath - auto* points = gcode_path->mutable_path()->add_path(); for (const auto& point : path.points) { + auto* points = gcode_path->mutable_path()->add_path(); points->set_x(point.X); points->set_y(point.Y); } - } + auto* config_msg = gcode_path->mutable_config(); + + switch (path.config.getPrintFeatureType()) + { + case PrintFeatureType::NoneType: + config_msg->set_feature(v0::PrintFeature::NONETYPE); + break; + case PrintFeatureType::OuterWall: + config_msg->set_feature(v0::PrintFeature::OUTERWALL); + break; + case PrintFeatureType::InnerWall: + config_msg->set_feature(v0::PrintFeature::INNERWALL); + break; + case PrintFeatureType::Skin: + config_msg->set_feature(v0::PrintFeature::SKIN); + break; + case PrintFeatureType::Support: + config_msg->set_feature(v0::PrintFeature::SUPPORT); + break; + case PrintFeatureType::SkirtBrim: + config_msg->set_feature(v0::PrintFeature::SKIRTBRIM); + break; + case PrintFeatureType::Infill: + config_msg->set_feature(v0::PrintFeature::INFILL); + break; + case PrintFeatureType::SupportInfill: + config_msg->set_feature(v0::PrintFeature::SUPPORTINFILL); + break; + case PrintFeatureType::MoveCombing: + config_msg->set_feature(v0::PrintFeature::MOVECOMBING); + break; + case PrintFeatureType::MoveRetraction: + config_msg->set_feature(v0::PrintFeature::MOVERETRACTION); + break; + case PrintFeatureType::SupportInterface: + config_msg->set_feature(v0::PrintFeature::SUPPORTINTERFACE); + break; + case PrintFeatureType::PrimeTower: + config_msg->set_feature(v0::PrintFeature::PRIMETOWER); + break; + case PrintFeatureType::NumPrintFeatureTypes: + config_msg->set_feature(v0::PrintFeature::NUMPRINTFEATURETYPES); + break; + default: + config_msg->set_feature(v0::PrintFeature::NONETYPE); + break; + } + + config_msg->set_line_width(path.config.getLineWidth()); + config_msg->set_layer_thickness(path.config.getLayerThickness()); + config_msg->set_flow_ratio(path.config.getFlowRatio()); + config_msg->set_is_bridge_path(path.config.isBridgePath()); + config_msg->set_fan_speed(path.config.getFanSpeed()); + config_msg->mutable_speed_derivatives()->set_velocity(path.config.getSpeed()); + config_msg->mutable_speed_derivatives()->set_acceleration(path.config.getAcceleration()); + config_msg->mutable_speed_derivatives()->set_jerk(path.config.getJerk()); + } return message; } - gcode_paths_modify_response::native_value_type gcode_paths_modify_response::operator()(const gcode_paths_modify_response::value_type& message) const { - return gcode_paths_modify_response::native_value_type(); + std::vector paths; + for (const auto& gcode_path_msg : message.gcode_paths()) + { + const auto config = [gcode_path_msg]() + { + const auto type = [gcode_path_msg]() { + switch (gcode_path_msg.config().feature()) { + case v0::PrintFeature::NONETYPE: + return PrintFeatureType::NoneType; + case v0::PrintFeature::OUTERWALL: + return PrintFeatureType::OuterWall; + case v0::PrintFeature::INNERWALL: + return PrintFeatureType::InnerWall; + case v0::PrintFeature::SKIN: + return PrintFeatureType::Skin; + case v0::PrintFeature::SUPPORT: + return PrintFeatureType::Support; + case v0::PrintFeature::SKIRTBRIM: + return PrintFeatureType::SkirtBrim; + case v0::PrintFeature::INFILL: + return PrintFeatureType::Infill; + case v0::PrintFeature::SUPPORTINFILL: + return PrintFeatureType::SupportInfill; + case v0::PrintFeature::MOVECOMBING: + return PrintFeatureType::MoveCombing; + case v0::PrintFeature::MOVERETRACTION: + return PrintFeatureType::MoveRetraction; + case v0::PrintFeature::SUPPORTINTERFACE: + return PrintFeatureType::SupportInterface; + case v0::PrintFeature::PRIMETOWER: + return PrintFeatureType::PrimeTower; + case v0::PrintFeature::NUMPRINTFEATURETYPES: + return PrintFeatureType::NumPrintFeatureTypes; + default: + return PrintFeatureType::NoneType; + } + }(); + + const coord_t line_width = gcode_path_msg.config().line_width(); + const coord_t layer_height = gcode_path_msg.config().layer_thickness(); + const Ratio flow = gcode_path_msg.config().flow_ratio(); + const GCodePathConfig::SpeedDerivatives speed_derivatives = { + gcode_path_msg.config().speed_derivatives().velocity(), + gcode_path_msg.config().speed_derivatives().acceleration(), + gcode_path_msg.config().speed_derivatives().jerk() + }; + const bool is_bridge_path = gcode_path_msg.config().is_bridge_path(); + const double fan_speed = gcode_path_msg.config().fan_speed(); + return GCodePathConfig(type, line_width, layer_height, flow, speed_derivatives, is_bridge_path, fan_speed); + }(); + + const auto gcode_path = [config, gcode_path_msg]() + { + // TODO get actual mesh_id from message + const SliceMeshStorage* mesh_id = nullptr; + const SpaceFillType space_fill_type = [gcode_path_msg]() + { + switch (gcode_path_msg.space_fill_type()) { + case v0::SpaceFillType::NONE: + return SpaceFillType::None; + case v0::SpaceFillType::POLYGONS: + return SpaceFillType::Polygons; + case v0::SpaceFillType::POLY_LINES: + return SpaceFillType::PolyLines; + case v0::SpaceFillType::LINES: + return SpaceFillType::Lines; + default: + return SpaceFillType::None; + } + }(); + const Ratio flow = gcode_path_msg.flow(); + const Ratio width_factor = gcode_path_msg.width_factor(); + const bool spiralize = gcode_path_msg.spiralize(); + const Ratio speed_factor = gcode_path_msg.speed_factor(); + + const auto path = GCodePath(config, mesh_id, space_fill_type, flow, width_factor, spiralize, speed_factor); + GCodePath gcode_path(path); + gcode_path.points = gcode_path_msg.path().path() + | ranges::views::transform([](const auto& point_msg) { return Point{ point_msg.x(), point_msg.y() }; }) + | ranges::to_vector; + return gcode_path; + }(); + + paths.emplace_back(gcode_path); + } + + return paths; } } // namespace cura::plugins \ No newline at end of file diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 8135507834..634328ee66 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1497,7 +1497,7 @@ Polygons PolygonUtils::unionManySmall(const Polygons& p) Polygons a, b; a.paths.reserve(p.paths.size() / 2); b.paths.reserve(a.paths.size() + 1); - for (const auto& [i, path] : p.paths | ranges::view::enumerate) + for (const auto& [i, path] : p.paths | ranges::views::enumerate) { (i % 2 == 0 ? b : a).paths.push_back(path); } diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index 190381ada3..b1a49a344a 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -198,12 +198,12 @@ class ExtruderPlanPathsParameterizedTest : public testing::TestWithParamgetFlowRatio() / path.flow * path.config->getSpeed() * path.speed_back_pressure_factor; + return path.getExtrusionMM3perMM() / path.config.getFlowRatio() / path.flow * path.config.getSpeed() * path.speed_back_pressure_factor; } [[nodiscard]] static bool shouldCountPath(const GCodePath& path) { - return path.flow > 0.0 && path.width_factor > 0.0 && path.config->getFlowRatio() > 0.0 && path.config->getLineWidth() > 0 && ! path.config->isTravelPath() && ! path.config->isBridgePath(); + return path.flow > 0.0 && path.width_factor > 0.0 && path.config.getFlowRatio() > 0.0 && path.config.getLineWidth() > 0 && ! path.config.isTravelPath() && ! path.config.isBridgePath(); } }; From 634cb8ae59615cb322bf9492f945d92ed8a93d1b Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 15 Aug 2023 13:08:37 +0000 Subject: [PATCH 430/656] Applied clang-format. --- include/pathPlanning/GCodePath.h | 25 ++++--- src/LayerPlan.cpp | 18 ++--- src/pathPlanning/GCodePath.cpp | 50 ++++++++------ src/plugins/converters.cpp | 26 ++++--- src/utils/polygonUtils.cpp | 115 ++++++++++++++++++++----------- 5 files changed, 145 insertions(+), 89 deletions(-) diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 4cd76687fe..3681e67e65 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -1,5 +1,5 @@ -//Copyright (c) 2023 UltiMaker -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef PATH_PLANNING_G_CODE_PATH_H #define PATH_PLANNING_G_CODE_PATH_H @@ -35,14 +35,16 @@ class GCodePath Ratio speed_factor; //!< A speed factor that is multiplied with the travel speed. This factor can be used to change the travel speed. Ratio speed_back_pressure_factor; // points; //!< The points constituting this path. bool done; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. - bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher. + bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and + //!< end in one layer higher. double fan_speed; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise @@ -61,7 +63,14 @@ class GCodePath * \param speed_factor The factor that the travel speed will be multiplied with * this path. */ - GCodePath(const GCodePathConfig config, const SliceMeshStorage* mesh_id, const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor = 1.0); + GCodePath( + const GCodePathConfig config, + const SliceMeshStorage* mesh_id, + const SpaceFillType space_fill_type, + const Ratio flow, + const Ratio width_factor, + const bool spiralize, + const Ratio speed_factor = 1.0); /*! * Whether this config is the config of a travel path. @@ -90,7 +99,7 @@ class GCodePath * * \param fan_speed the fan speed to use for this path */ - void setFanSpeed(double fan_speed); + void setFanSpeed(double fan_speed); /*! * Get the fan speed for this path @@ -99,6 +108,6 @@ class GCodePath double getFanSpeed() const; }; -}//namespace cura +} // namespace cura -#endif//PATH_PLANNING_G_CODE_PATH_H +#endif // PATH_PLANNING_G_CODE_PATH_H diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index f4df7e8072..743283025b 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -12,13 +12,13 @@ #include "pathPlanning/Comb.h" #include "pathPlanning/CombPaths.h" //#include "plugins/slots.h" +#include "plugins/slots.h" #include "raft.h" // getTotalExtraLayers #include "settings/types/Ratio.h" #include "sliceDataStorage.h" #include "utils/Simplify.h" #include "utils/linearAlg2D.h" #include "utils/polygonUtils.h" -#include "plugins/slots.h" #include #include @@ -111,7 +111,7 @@ GCodePath* LayerPlan::getLatestPathWithConfig( std::vector& paths = extruder_plans.back().paths; // TODO put back config equality check if (paths.size() > 0 /*&& paths.back().config == config*/ && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor - && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between + && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } @@ -1911,11 +1911,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; - extruder_plan.paths = slots::instance().generate( - extruder_plan.paths, - extruder_plan.extruder_nr, - layer_nr - ); + extruder_plan.paths = slots::instance().generate(extruder_plan.paths, extruder_plan.extruder_nr, layer_nr); // Since the time/material estimates _may_ have changed during the plugin modify step we recalculate it extruder_plan.computeNaiveTimeEstimates(gcode.getPositionXY()); @@ -2098,7 +2094,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } } // TODO re-enable the config_changed check - const auto& extruder_changed = !last_extrusion_config.has_value() /* || last_extrusion_config.value() != path.config */; + const auto& extruder_changed = ! last_extrusion_config.has_value() /* || last_extrusion_config.value() != path.config */; if (! path.config.isTravelPath() && extruder_changed) { gcode.writeTypeComment(path.config.type); @@ -2107,7 +2103,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeComment("BRIDGE"); } // TODO uncomment next line, make path.config copyable -// last_extrusion_config = path.config; + // last_extrusion_config = path.config; update_extrusion_offset = true; } else @@ -2313,8 +2309,8 @@ bool LayerPlan::writePathWithCoasting( 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 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 // /\ 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/pathPlanning/GCodePath.cpp b/src/pathPlanning/GCodePath.cpp index 99cefde899..5710f4e18d 100644 --- a/src/pathPlanning/GCodePath.cpp +++ b/src/pathPlanning/GCodePath.cpp @@ -1,29 +1,37 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "pathPlanning/GCodePath.h" + #include "GCodePathConfig.h" namespace cura { -GCodePath::GCodePath(const GCodePathConfig config, const SliceMeshStorage* mesh, const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor) : - config(config), - mesh(mesh), - space_fill_type(space_fill_type), - flow(flow), - width_factor(width_factor), - speed_factor(speed_factor), - speed_back_pressure_factor(1.0), - retract(false), - unretract_before_last_travel_move(false), - perform_z_hop(false), - perform_prime(false), - skip_agressive_merge_hint(false), - points(std::vector()), - done(false), - spiralize(spiralize), - fan_speed(GCodePathConfig::FAN_SPEED_DEFAULT), - estimates(TimeMaterialEstimates()) +GCodePath::GCodePath( + const GCodePathConfig config, + const SliceMeshStorage* mesh, + const SpaceFillType space_fill_type, + const Ratio flow, + const Ratio width_factor, + const bool spiralize, + const Ratio speed_factor) + : config(config) + , mesh(mesh) + , space_fill_type(space_fill_type) + , flow(flow) + , width_factor(width_factor) + , speed_factor(speed_factor) + , speed_back_pressure_factor(1.0) + , retract(false) + , unretract_before_last_travel_move(false) + , perform_z_hop(false) + , perform_prime(false) + , skip_agressive_merge_hint(false) + , points(std::vector()) + , done(false) + , spiralize(spiralize) + , fan_speed(GCodePathConfig::FAN_SPEED_DEFAULT) + , estimates(TimeMaterialEstimates()) { } @@ -52,4 +60,4 @@ double GCodePath::getFanSpeed() const return (fan_speed >= 0 && fan_speed <= 100) ? fan_speed : config.getFanSpeed(); } -} +} // namespace cura diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 6f123d45a4..23779507f9 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -275,7 +275,8 @@ gcode_paths_modify_request::value_type { auto* gcode_path = gcode_paths->Add(); - switch (path.space_fill_type){ + switch (path.space_fill_type) + { case SpaceFillType::None: gcode_path->set_space_fill_type(v0::SpaceFillType::NONE); break; @@ -374,8 +375,10 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper { const auto config = [gcode_path_msg]() { - const auto type = [gcode_path_msg]() { - switch (gcode_path_msg.config().feature()) { + const auto type = [gcode_path_msg]() + { + switch (gcode_path_msg.config().feature()) + { case v0::PrintFeature::NONETYPE: return PrintFeatureType::NoneType; case v0::PrintFeature::OUTERWALL: @@ -410,11 +413,9 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper const coord_t line_width = gcode_path_msg.config().line_width(); const coord_t layer_height = gcode_path_msg.config().layer_thickness(); const Ratio flow = gcode_path_msg.config().flow_ratio(); - const GCodePathConfig::SpeedDerivatives speed_derivatives = { - gcode_path_msg.config().speed_derivatives().velocity(), - gcode_path_msg.config().speed_derivatives().acceleration(), - gcode_path_msg.config().speed_derivatives().jerk() - }; + const GCodePathConfig::SpeedDerivatives speed_derivatives = { gcode_path_msg.config().speed_derivatives().velocity(), + gcode_path_msg.config().speed_derivatives().acceleration(), + gcode_path_msg.config().speed_derivatives().jerk() }; const bool is_bridge_path = gcode_path_msg.config().is_bridge_path(); const double fan_speed = gcode_path_msg.config().fan_speed(); return GCodePathConfig(type, line_width, layer_height, flow, speed_derivatives, is_bridge_path, fan_speed); @@ -426,7 +427,8 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper const SliceMeshStorage* mesh_id = nullptr; const SpaceFillType space_fill_type = [gcode_path_msg]() { - switch (gcode_path_msg.space_fill_type()) { + switch (gcode_path_msg.space_fill_type()) + { case v0::SpaceFillType::NONE: return SpaceFillType::None; case v0::SpaceFillType::POLYGONS: @@ -447,7 +449,11 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper const auto path = GCodePath(config, mesh_id, space_fill_type, flow, width_factor, spiralize, speed_factor); GCodePath gcode_path(path); gcode_path.points = gcode_path_msg.path().path() - | ranges::views::transform([](const auto& point_msg) { return Point{ point_msg.x(), point_msg.y() }; }) + | ranges::views::transform( + [](const auto& point_msg) + { + return Point{ point_msg.x(), point_msg.y() }; + }) | ranges::to_vector; return gcode_path; }(); diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 634328ee66..0a8f627d53 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1,27 +1,33 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include "utils/polygonUtils.h" + #include "infill.h" #include "utils/SparsePointGridInclusive.h" #include "utils/linearAlg2D.h" -#include "utils/polygonUtils.h" + +#include #include #include #include #include -#include #ifdef DEBUG #include "utils/AABB.h" #include "utils/SVG.h" + #include #endif namespace cura { -const std::function PolygonUtils::no_penalty_function = [](Point) { return 0; }; +const std::function PolygonUtils::no_penalty_function = [](Point) +{ + return 0; +}; int64_t PolygonUtils::segmentLength(PolygonsPointIndex start, PolygonsPointIndex end) { @@ -98,7 +104,7 @@ std::vector PolygonUtils::spreadDotsArea(const Polygons& polygons, Point Infill infill_gen(EFillMethod::LINES, false, false, polygons, 0, grid_size.X, 0, 1, 0, 0, 0, 0, 0); Polygons result_polygons; Polygons result_lines; - infill_gen.generate(dummy_toolpaths, result_polygons, result_lines, dummy_settings, 0, SectionType::DOTS); // FIXME: @jellespijker make sure the propper layer nr is used + infill_gen.generate(dummy_toolpaths, result_polygons, result_lines, dummy_settings, 0, SectionType::DOTS); // FIXME: @jellespijker make sure the propper layer nr is used std::vector result; for (PolygonRef line : result_lines) { @@ -120,7 +126,13 @@ std::vector PolygonUtils::spreadDotsArea(const Polygons& polygons, Point return result; } -bool PolygonUtils::lineSegmentPolygonsIntersection(const Point& a, const Point& b, const Polygons& current_outlines, const LocToLineGrid& outline_locator, Point& result, const coord_t within_max_dist) +bool PolygonUtils::lineSegmentPolygonsIntersection( + const Point& a, + const Point& b, + const Polygons& current_outlines, + const LocToLineGrid& outline_locator, + Point& result, + const coord_t within_max_dist) { const coord_t within_max_dist2 = within_max_dist * within_max_dist; @@ -129,7 +141,8 @@ bool PolygonUtils::lineSegmentPolygonsIntersection(const Point& a, const Point& const auto processOnIntersect = [&result, &closest_dist2, &a, &b, &coll](const Point& p_start, const Point& p_end) { - if (LinearAlg2D::lineLineIntersection(a, b, p_start, p_end, coll) && LinearAlg2D::pointIsProjectedBeyondLine(coll, p_start, p_end) == 0 && LinearAlg2D::pointIsProjectedBeyondLine(coll, a, b) == 0) + if (LinearAlg2D::lineLineIntersection(a, b, p_start, p_end, coll) && LinearAlg2D::pointIsProjectedBeyondLine(coll, p_start, p_end) == 0 + && LinearAlg2D::pointIsProjectedBeyondLine(coll, a, b) == 0) { const coord_t dist2 = vSize2(b - coll); if (dist2 < closest_dist2) @@ -233,13 +246,14 @@ unsigned int PolygonUtils::moveOutside(const Polygons& polygons, Point& from, in return moveInside(polygons, from, -distance, maxDist2); } -ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& polygons, - Point& from, - const int distance, - const int64_t max_dist2, - const Polygons* loc_to_line_polygons, - const LocToLineGrid* loc_to_line_grid, - const std::function& penalty_function) +ClosestPolygonPoint PolygonUtils::moveInside2( + const Polygons& polygons, + Point& from, + const int distance, + const int64_t max_dist2, + const Polygons* loc_to_line_polygons, + const LocToLineGrid* loc_to_line_grid, + const std::function& penalty_function) { std::optional closest_polygon_point; if (loc_to_line_grid) @@ -253,8 +267,14 @@ ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& polygons, return _moveInside2(*closest_polygon_point, distance, from, max_dist2); } -ClosestPolygonPoint - PolygonUtils::moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance, const int64_t max_dist2, const LocToLineGrid* loc_to_line_grid, const std::function& penalty_function) +ClosestPolygonPoint PolygonUtils::moveInside2( + const Polygons& loc_to_line_polygons, + ConstPolygonRef polygon, + Point& from, + const int distance, + const int64_t max_dist2, + const LocToLineGrid* loc_to_line_grid, + const std::function& penalty_function) { std::optional closest_polygon_point; if (loc_to_line_grid) @@ -596,25 +616,27 @@ Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distanc } } -ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, - Point& from, - int preferred_dist_inside, - int64_t max_dist2, - const Polygons* loc_to_line_polygons, - const LocToLineGrid* loc_to_line_grid, - const std::function& penalty_function) +ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside( + const Polygons& polygons, + Point& from, + int preferred_dist_inside, + int64_t max_dist2, + const Polygons* loc_to_line_polygons, + const LocToLineGrid* loc_to_line_grid, + const std::function& penalty_function) { const ClosestPolygonPoint closest_polygon_point = moveInside2(polygons, from, preferred_dist_inside, max_dist2, loc_to_line_polygons, loc_to_line_grid, penalty_function); return ensureInsideOrOutside(polygons, from, closest_polygon_point, preferred_dist_inside, loc_to_line_polygons, loc_to_line_grid, penalty_function); } -ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, - Point& from, - const ClosestPolygonPoint& closest_polygon_point, - int preferred_dist_inside, - const Polygons* loc_to_line_polygons, - const LocToLineGrid* loc_to_line_grid, - const std::function& penalty_function) +ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside( + const Polygons& polygons, + Point& from, + const ClosestPolygonPoint& closest_polygon_point, + int preferred_dist_inside, + const Polygons* loc_to_line_polygons, + const LocToLineGrid* loc_to_line_grid, + const std::function& penalty_function) { if (! closest_polygon_point.isValid()) { @@ -646,7 +668,8 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons else { const coord_t offset = (is_outside_boundary) ? -preferred_dist_inside : preferred_dist_inside; // perform inset on outer boundary and outset on holes - Polygons insetted = closest_poly.offset(offset / 2); // perform less inset, because chances are (thin parts of) the polygon will disappear, given that moveInside did an overshoot + Polygons insetted + = closest_poly.offset(offset / 2); // perform less inset, because chances are (thin parts of) the polygon will disappear, given that moveInside did an overshoot if (insetted.size() == 0) { return ClosestPolygonPoint(); // we couldn't move inside @@ -666,7 +689,7 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons { #ifdef DEBUG static bool has_run = false; - if ( ! has_run) + if (! has_run) { try { @@ -755,7 +778,8 @@ void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_re // \-'| // o o >> should find connection here coord_t best_distance2 = vSize2(poly1_result.p() - poly2_result.p()); - auto check_neighboring_vert = [&best_distance2](ConstPolygonRef from_poly, ConstPolygonRef to_poly, ClosestPolygonPoint& from_poly_result, ClosestPolygonPoint& to_poly_result, bool vertex_after) + auto check_neighboring_vert + = [&best_distance2](ConstPolygonRef from_poly, ConstPolygonRef to_poly, ClosestPolygonPoint& from_poly_result, ClosestPolygonPoint& to_poly_result, bool vertex_after) { const Point after_poly2_result = to_poly[(to_poly_result.point_idx + vertex_after) % to_poly.size()]; const ClosestPolygonPoint poly1_after_poly2_result = findNearestClosest(after_poly2_result, from_poly, from_poly_result.point_idx); @@ -978,7 +1002,8 @@ std::unique_ptr PolygonUtils::createLocToLineGrid(const Polygons& * * We could skip the duplication by keeping a vector of vectors of bools. */ -std::optional PolygonUtils::findClose(Point from, const Polygons& polygons, const LocToLineGrid& loc_to_line, const std::function& penalty_function) +std::optional + PolygonUtils::findClose(Point from, const Polygons& polygons, const LocToLineGrid& loc_to_line, const std::function& penalty_function) { std::vector near_lines = loc_to_line.getNearby(from, loc_to_line.getCellSize()); @@ -1011,7 +1036,8 @@ std::optional PolygonUtils::findClose(Point from, const Pol } } -std::vector> PolygonUtils::findClose(ConstPolygonRef from, const Polygons& destination, const LocToLineGrid& destination_loc_to_line, const std::function& penalty_function) +std::vector> + PolygonUtils::findClose(ConstPolygonRef from, const Polygons& destination, const LocToLineGrid& destination_loc_to_line, const std::function& penalty_function) { std::vector> ret; int p0_idx = from.size() - 1; @@ -1166,7 +1192,8 @@ std::optional PolygonUtils::getNextParallelIntersection(con coord_t prev_projected = 0; for (unsigned int next_point_nr = 0; next_point_nr < poly.size(); next_point_nr++) { - const unsigned int next_point_idx = forward ? (start.point_idx + 1 + next_point_nr) % poly.size() : (static_cast(start.point_idx) - next_point_nr + poly.size()) % poly.size(); // cast in order to accomodate subtracting + const unsigned int next_point_idx = forward ? (start.point_idx + 1 + next_point_nr) % poly.size() + : (static_cast(start.point_idx) - next_point_nr + poly.size()) % poly.size(); // cast in order to accomodate subtracting const Point next_vert = poly[next_point_idx]; const Point so = next_vert - s; const coord_t projected = dot(shift, so) / dist; @@ -1176,7 +1203,8 @@ std::optional PolygonUtils::getNextParallelIntersection(con const coord_t segment_length = vSize(segment_vector); const coord_t projected_segment_length = std::abs(projected - prev_projected); const int16_t sign = (projected > 0) ? 1 : -1; - const coord_t projected_inter_segment_length = dist - sign * prev_projected; // add the prev_projected to dist if it is projected to the other side of the input line than where the intersection occurs. + const coord_t projected_inter_segment_length + = dist - sign * prev_projected; // add the prev_projected to dist if it is projected to the other side of the input line than where the intersection occurs. const coord_t inter_segment_length = segment_length * projected_inter_segment_length / projected_segment_length; const Point intersection = prev_vert + normal(next_vert - prev_vert, inter_segment_length); @@ -1212,7 +1240,8 @@ bool PolygonUtils::polygonCollidesWithLineSegment(const Point from, const Point PolygonsPointIndex result; - std::function process_elem_func = [transformed_from, transformed_to, &transformation_matrix, &result, &ret](const PolygonsPointIndex& line_start) + std::function process_elem_func + = [transformed_from, transformed_to, &transformation_matrix, &result, &ret](const PolygonsPointIndex& line_start) { Point p0 = transformation_matrix.apply(line_start.p()); Point p1 = transformation_matrix.apply(line_start.next().p()); @@ -1331,7 +1360,11 @@ bool PolygonUtils::polygonOutlinesAdjacent(const ConstPolygonRef inner_poly, con return false; } -void PolygonUtils::findAdjacentPolygons(std::vector& adjacent_poly_indices, const ConstPolygonRef& poly, const std::vector& possible_adjacent_polys, const coord_t max_gap) +void PolygonUtils::findAdjacentPolygons( + std::vector& adjacent_poly_indices, + const ConstPolygonRef& poly, + const std::vector& possible_adjacent_polys, + const coord_t max_gap) { // given a polygon, and a vector of polygons, return a vector containing the indices of the polygons that are adjacent to the given polygon for (unsigned poly_idx = 0; poly_idx < possible_adjacent_polys.size(); ++poly_idx) @@ -1526,7 +1559,11 @@ Polygons PolygonUtils::clipPolygonWithAABB(const Polygons& src, const AABB& aabb Bottom = 8 }; - auto sides = [aabb](const Point& p) { return int(p.X < aabb.min.X) * int(Side::Left) + int(p.X > aabb.max.X) * int(Side::Right) + int(p.Y < aabb.min.Y) * int(Side::Bottom) + int(p.Y > aabb.max.Y) * int(Side::Top); }; + auto sides = [aabb](const Point& p) + { + return int(p.X < aabb.min.X) * int(Side::Left) + int(p.X > aabb.max.X) * int(Side::Right) + int(p.Y < aabb.min.Y) * int(Side::Bottom) + + int(p.Y > aabb.max.Y) * int(Side::Top); + }; int sides_prev = sides(path.back()); int sides_this = sides(path.front()); From 80e0872cd9fbb7f5224997557dcfbf4dfc51991d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 15 Aug 2023 15:49:42 +0200 Subject: [PATCH 431/656] message to show if handshake failed CURA-10619 --- include/plugins/pluginproxy.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 88908c0fe5..d5340db80e 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -123,6 +123,7 @@ class PluginProxy if (! status.ok()) // TODO: handle different kind of status codes { + spdlog::error(status.error_message()); throw exceptions::RemoteException(slot_info_, status.error_message()); } if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) From ab988c85b68d0e31a72de14eb0dc46467c117f86 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 15 Aug 2023 16:10:55 +0200 Subject: [PATCH 432/656] Create FUNDING.yml --- FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 FUNDING.yml diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000000..2d108a74e1 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1 @@ +github: [ultimaker] From f8818e13776a4a2368280a00a7f61cf5e66c3575 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 15 Aug 2023 16:32:36 +0200 Subject: [PATCH 433/656] Also possible to exclude bottom from small skin behaviour. Small skin areas replace the normal fill pattern and use walls. Previously in this branch, the possibility was added to not do that to areas exposed to air. It turns out this means in general, not just on the buildplate (also, bottoms can happen away from the buildplate as well of course). As such, take skin bottoms into account of when to exclude areas for consideration when 'Small Top/Bottom On Surface' is off. part of CURA-10829 --- include/sliceDataStorage.h | 7 +++++++ src/FffGcodeWriter.cpp | 3 ++- src/FffPolygonGenerator.cpp | 10 ++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index b34c722fbf..626334b10c 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -174,6 +174,13 @@ class SliceLayer */ TopSurface top_surface; + /*! + * \brief The parts of the model that are exposed at the bottom(s) of the model. + * + * Note: Filled only when needed. + */ + Polygons bottom_surface; + /*! * Get the all outlines of all layer parts in this layer. * diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 12d66b9c58..b165e4e931 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2764,7 +2764,8 @@ void FffGcodeWriter::processSkinPrintFeature( constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; const bool small_areas_on_surface = mesh.settings.get("small_skin_on_surface"); - const auto& exposed_to_air = mesh.layers[gcode_layer.getLayerNr()].top_surface.areas; + const auto& current_layer = mesh.layers[gcode_layer.getLayerNr()]; + const auto& exposed_to_air = current_layer.top_surface.areas.unionPolygons(current_layer.bottom_surface); Infill infill_comp( pattern, diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 75996887a5..055e81803e 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -830,6 +830,16 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, const La // Generate the top surface to iron over. mesh.layers[layer_nr].top_surface.setAreasFromMeshAndLayerNumber(mesh, layer_nr); } + + if (layer_nr >= 0 && ! mesh.settings.get("small_skin_on_surface")) + { + // Generate the bottom surface. + mesh.layers[layer_nr].bottom_surface = mesh.layers[layer_nr].getOutlines(); + if (layer_nr > 0) + { + mesh.layers[layer_nr].bottom_surface = mesh.layers[layer_nr].bottom_surface.difference(mesh.layers[layer_nr - 1].getOutlines()); + } + } } void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage) From 3e8d340f87c9970b6ab1714f0984a8e3ae92af93 Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 15 Aug 2023 14:33:13 +0000 Subject: [PATCH 434/656] Applied clang-format. --- include/sliceDataStorage.h | 66 ++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 626334b10c..0d20a90f14 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -1,16 +1,14 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SLICE_DATA_STORAGE_H #define SLICE_DATA_STORAGE_H -#include -#include - #include "PrimeTower.h" #include "RetractionConfig.h" #include "SupportInfillPart.h" #include "TopSurface.h" +#include "WipeScriptConfig.h" #include "settings/Settings.h" //For MAX_EXTRUDERS. #include "settings/types/Angle.h" //Infill angles. #include "settings/types/LayerIndex.h" @@ -19,7 +17,9 @@ #include "utils/IntPoint.h" #include "utils/NoCopy.h" #include "utils/polygon.h" -#include "WipeScriptConfig.h" + +#include +#include // libArachne #include "utils/ExtrusionLine.h" @@ -32,14 +32,15 @@ class SierpinskiFillProvider; class LightningGenerator; /*! - * A SkinPart is a connected area designated as top and/or bottom skin. + * A SkinPart is a connected area designated as top and/or bottom skin. * Surrounding each non-bridged skin area with an outline may result in better top skins. * It's filled during FffProcessor.processSliceData(.) and used in FffProcessor.writeGCode(.) to generate the final gcode. */ class SkinPart { public: - PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module. Includes both roofing and non-roofing. + PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module. Includes both + //!< roofing and non-roofing. Polygons skin_fill; //!< The part of the skin which is not roofing. Polygons roofing_fill; //!< The inner infill which has air directly above Polygons top_most_surface_fill; //!< The inner infill of the uppermost top layer which has air directly above. @@ -64,7 +65,7 @@ class SliceLayerPart //!< Too small parts will be omitted compared to the outline. Polygons spiral_wall; //!< The centerline of the wall used by spiralize mode. Only computed if spiralize mode is enabled. Polygons inner_area; //!< The area of the outline, minus the walls. This will be filled with either skin or infill. - std::vector skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets. + std::vector skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets. std::vector wall_toolpaths; //!< toolpaths for walls, will replace(?) the insets. Binned by inset_idx. std::vector infill_wall_toolpaths; //!< toolpaths for the walls of the infill areas. Binned by inset_idx. @@ -125,7 +126,7 @@ class SliceLayerPart * This maximum number of layers we can combine is a user setting. This number, say "n", means the maximum number of layers we can combine into one. * On the combined layers, the extrusion amount will be higher than the normal extrusion amount because it needs to extrude for multiple layers instead of one. * - * infill_area[x][n] is infill_area of (n+1) layers thick. + * infill_area[x][n] is infill_area of (n+1) layers thick. * * infill_area[0] corresponds to the most dense infill area. * infill_area[x] will lie fully inside infill_area[x+1]. @@ -161,9 +162,9 @@ class SliceLayerPart class SliceLayer { public: - coord_t printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft. - coord_t thickness; //!< The thickness of this layer. Can be different when using variable layer heights. - std::vector parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model. + coord_t printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft. + coord_t thickness; //!< The thickness of this layer. Can be different when using variable layer heights. + std::vector parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model. Polygons openPolyLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation) /*! @@ -183,7 +184,7 @@ class SliceLayer /*! * Get the all outlines of all layer parts in this layer. - * + * * \param external_polys_only Whether to only include the outermost outline of each layer part * \return A collection of all the outline polygons */ @@ -192,7 +193,7 @@ class SliceLayer /*! * Get the all outlines of all layer parts in this layer. * Add those polygons to @p result. - * + * * \param external_polys_only Whether to only include the outermost outline of each layer part * \param result The result: a collection of all the outline polygons */ @@ -204,12 +205,10 @@ class SliceLayer /******************/ - - class SupportLayer { public: - std::vector support_infill_parts; //!< a list of support infill parts + std::vector support_infill_parts; //!< a list of support infill parts Polygons support_bottom; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof. Polygons support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. Polygons support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support @@ -260,8 +259,10 @@ class SliceMeshStorage std::vector roofing_angles; //!< a list of angle values which is cycled through to determine the roofing angle of each layer std::vector skin_angles; //!< a list of angle values which is cycled through to determine the skin angle of each layer std::vector overhang_areas; //!< For each layer the areas that are classified as overhang on this mesh. - std::vector full_overhang_areas; //!< For each layer the full overhang without the tangent of the overhang angle removed, such that the overhang area adjoins the areas of the next layers. - std::vector> overhang_points; //!< For each layer a list of points where point-overhang is detected. This is overhang that hasn't got any surface area, such as a corner pointing downwards. + std::vector full_overhang_areas; //!< For each layer the full overhang without the tangent of the overhang angle removed, such that the overhang area adjoins the + //!< areas of the next layers. + std::vector> overhang_points; //!< For each layer a list of points where point-overhang is detected. This is overhang that hasn't got any surface area, + //!< such as a corner pointing downwards. AABB3D bounding_box; //!< the mesh's bounding box SubDivCube* base_subdiv_cube; @@ -329,22 +330,23 @@ class SliceDataStorage : public NoCopy std::vector retraction_wipe_config_per_extruder; //!< Config for retractions, extruder switch retractions, and wipes, per extruder. SupportStorage support; - + std::vector skirt_brim[MAX_EXTRUDERS]; //!< Skirt/brim polygons per extruder, ordered from inner to outer polygons. Polygons support_brim; //!< brim lines for support, going from the edge of the support inward. \note Not ordered by inset. - Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated. - Polygons primeRaftOutline; // ... the raft underneath the prime-tower will have to be printed first, if there is one. (When the raft has top layers with a different extruder for example.) + Polygons raftOutline; // Storage for the outline of the raft. Will be filled with lines when the GCode is generated. + Polygons primeRaftOutline; // ... the raft underneath the prime-tower will have to be printed first, if there is one. (When the raft has top layers with a different extruder + // for example.) int max_print_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder std::vector max_print_height_per_extruder; //!< For each extruder the highest layer number at which it is used. std::vector max_print_height_order; //!< Ordered indices into max_print_height_per_extruder: back() will return the extruder number with the highest print height. std::vector spiralize_seam_vertex_indices; //!< the index of the seam vertex for each layer - std::vector spiralize_wall_outlines; //!< the wall outline polygons for each layer + std::vector spiralize_wall_outlines; //!< the wall outline polygons for each layer PrimeTower primeTower; - std::vector oozeShield; //oozeShield per layer + std::vector oozeShield; // oozeShield per layer Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber. /*! @@ -359,7 +361,7 @@ class SliceDataStorage : public NoCopy /*! * Get all outlines within a given layer. - * + * * \param layer_nr The index of the layer for which to get the outlines * (negative layer numbers indicate the raft). * \param include_support Whether to include support in the outline. @@ -368,11 +370,13 @@ class SliceDataStorage : public NoCopy * \param external_polys_only Whether to disregard all hole polygons. * \param extruder_nr (optional) only give back outlines for this extruder (where the walls are printed with this extruder) */ - Polygons getLayerOutlines(const LayerIndex layer_nr, const bool include_support, const bool include_prime_tower, const bool external_polys_only = false, const int extruder_nr = -1) const; + Polygons + getLayerOutlines(const LayerIndex layer_nr, const bool include_support, const bool include_prime_tower, const bool external_polys_only = false, const int extruder_nr = -1) + const; /*! * Get the extruders used. - * + * * \return A vector of booleans indicating whether the extruder with the * corresponding index is used in the mesh group. */ @@ -380,7 +384,7 @@ class SliceDataStorage : public NoCopy /*! * Get the extruders used on a particular layer. - * + * * \param layer_nr the layer for which to check * \return a vector of bools indicating whether the extruder with corresponding index is used in this layer. */ @@ -409,6 +413,6 @@ class SliceDataStorage : public NoCopy std::vector initializeRetractionAndWipeConfigs(); }; -}//namespace cura +} // namespace cura -#endif//SLICE_DATA_STORAGE_H +#endif // SLICE_DATA_STORAGE_H From b85dd9f20b63da829a5c8915b53f728189b7099d Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 15 Aug 2023 16:56:14 +0200 Subject: [PATCH 435/656] Correct typo: to -> too. Was still left over from the code review for CURA-10670, committed as part of CURA-10829. --- src/infill.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index f5c7e6cc26..279e1ceff3 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -104,12 +104,12 @@ void Infill::generate( // infill pattern is concentric or if the small_area_width is zero. if (pattern != EFillMethod::CONCENTRIC && small_area_width > 0) { - const auto to_small_length = INT2MM(static_cast(infill_line_width) / 2.0); + const auto too_small_length = INT2MM(static_cast(infill_line_width) / 2.0); // Split the infill region in a narrow region and the normal region. Polygons small_infill = inner_contour; inner_contour = inner_contour.offset(-small_area_width / 2); - inner_contour.removeSmallAreas(to_small_length * to_small_length, true); + inner_contour.removeSmallAreas(too_small_length * too_small_length, true); inner_contour = inner_contour.offset(small_area_width / 2); if (prevent_small_exposed_to_air.area() > 0) { From 4998bfc0323ff189830d29c48062103677ac8798 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 15 Aug 2023 16:56:24 +0200 Subject: [PATCH 436/656] Removed benchmark for remote plugin CURA-10619 --- benchmark/simplify_benchmark.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index ce28ee1985..d3726bce65 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -72,29 +72,5 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_noplugin); -BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_localplugin)(benchmark::State& st) -{ - auto host = "localhost"; - auto port = 33700UL; - - try - { - slots::instance().connect(plugins::v0::SlotID::SIMPLIFY_MODIFY, "", "", utils::createChannel({ host, port })); - } - catch (std::runtime_error e) - { - st.SkipWithError(e.what()); - } - for (auto _ : st) - { - Polygons simplified; - for (const auto& polys : shapes) - { - benchmark::DoNotOptimize(simplified = slots::instance().modify(polys, MM2INT(0.25), MM2INT(0.025), 50000)); - } - } -} - -BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_localplugin); } // namespace cura #endif // CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H From 2dc28d85a2134f91bd793c67ddc28cddba290745 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Wed, 16 Aug 2023 11:26:55 +0200 Subject: [PATCH 437/656] Fix comment CURA-10811 Co-authored-by: Saumya Jain <70144862+saumyaj3@users.noreply.github.com> --- include/utils/actions/smooth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index c04088c262..7e4a83b15b 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -261,7 +261,7 @@ struct smooth_fn const utils::floating_point auto dist_cd) const noexcept { /* - * Move points A and B, so they are both at equal distance from C and D + * Move points A and D, so they are both at equal distance from B and C * * B--C * / \ From 396f5e3fb5c798f4af0fcaff230d3d7c789ec0e5 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Wed, 16 Aug 2023 11:27:11 +0200 Subject: [PATCH 438/656] Update variable names CURA-10811 Co-authored-by: Saumya Jain <70144862+saumyaj3@users.noreply.github.com> --- include/utils/actions/smooth.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/utils/actions/smooth.h b/include/utils/actions/smooth.h index 7e4a83b15b..67e4adebe6 100644 --- a/include/utils/actions/smooth.h +++ b/include/utils/actions/smooth.h @@ -167,10 +167,10 @@ struct smooth_fn inline constexpr auto cosAngle(Point& a, Point& b, Point& c, Point& d, const utils::floating_point auto ab_magnitude, const utils::floating_point auto bc_magnitude) const noexcept { - Point vector_a = { std::get<"X">(b) - std::get<"X">(a), std::get<"Y">(b) - std::get<"Y">(a) }; - Point vector_b = { std::get<"X">(d) - std::get<"X">(c), std::get<"Y">(d) - std::get<"Y">(c) }; + Point vector_ab = { std::get<"X">(b) - std::get<"X">(a), std::get<"Y">(b) - std::get<"Y">(a) }; + Point vector_cd = { std::get<"X">(d) - std::get<"X">(c), std::get<"Y">(d) - std::get<"Y">(c) }; - return cosAngle(vector_a, vector_b, ab_magnitude, bc_magnitude); + return cosAngle(vector_ab, vector_cd, ab_magnitude, bc_magnitude); } /* From 0a1bdf0b0d11e51ec1edf0735207769a5c69fa4f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 16 Aug 2023 14:37:19 +0200 Subject: [PATCH 439/656] Fixed compiler warnings for unused parameters CURA-10446 --- include/plugins/slots.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 6e0f81b949..c02f50ece2 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -124,12 +124,12 @@ template class Unit> class Registry, Unit> { public: - constexpr void connect(auto&&... args) noexcept + constexpr void connect([[maybe_unused]] auto&&... args) noexcept { } template - constexpr void broadcast(auto&&... args) noexcept + constexpr void broadcast([[maybe_unused]] auto&&... args) noexcept { } // Base case, do nothing }; From f2cd4057246644dd3e362b0a9dc2cf89fd9da182 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 16 Aug 2023 16:47:39 +0200 Subject: [PATCH 440/656] Moved SpeedDerivatives to its own file This commit primarily adds a new custom struct, SpeedDerivatives, for managing speed derivatives for path planning in the slicer. This structure has fields for speed, acceleration, and jerk which contain movement speed, acceleration for head movements, and jerk for instantaneous speed changes, respectively. Also, imports in several files have been streamlined by removing unnecessary imports to simplify code and improve readability. CURA-10446 --- CMakeLists.txt | 1 + include/GCodePathConfig.h | 20 +----- include/pathPlanning/GCodePath.h | 10 +-- include/pathPlanning/SpeedDerivatives.h | 24 +++++++ include/settings/PathConfigStorage.h | 7 ++- src/GCodePathConfig.cpp | 55 +++++++++------- src/LayerPlan.cpp | 1 - src/pathPlanning/SpeedDerivatives.cpp | 20 ++++++ src/plugins/converters.cpp | 7 ++- src/settings/PathConfigStorage.cpp | 63 ++++++++++--------- tests/ExtruderPlanTest.cpp | 83 ++++++++++++------------- 11 files changed, 163 insertions(+), 128 deletions(-) create mode 100644 include/pathPlanning/SpeedDerivatives.h create mode 100644 src/pathPlanning/SpeedDerivatives.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 25471c12cb..42a6ea97fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ set(engine_SRCS # Except main.cpp. src/pathPlanning/GCodePath.cpp src/pathPlanning/LinePolygonsCrossings.cpp src/pathPlanning/NozzleTempInsert.cpp + src/pathPlanning/SpeedDerivatives.cpp src/plugins/converters.cpp diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 39cac0852a..be02d75053 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -1,10 +1,11 @@ -// Copyright (c) 2018 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef G_CODE_PATH_CONFIG_H #define G_CODE_PATH_CONFIG_H #include "PrintFeature.h" +#include "pathPlanning/SpeedDerivatives.h" #include "settings/types/LayerIndex.h" #include "settings/types/Ratio.h" #include "settings/types/Velocity.h" @@ -19,21 +20,6 @@ namespace cura class GCodePathConfig { public: - /*! - * A simple wrapper class for all derivatives of position which are used when printing a line - */ - struct SpeedDerivatives - { - Velocity speed; //!< movement speed (mm/s) - Acceleration acceleration; //!< acceleration of head movement (mm/s^2) - Velocity jerk; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s) - SpeedDerivatives(Velocity speed, Acceleration acceleration, Velocity jerk) - : speed(speed) - , acceleration(acceleration) - , jerk(jerk) - { - } - }; const PrintFeatureType type; //!< name of the feature type static constexpr double FAN_SPEED_DEFAULT = -1; diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 3681e67e65..412f7b49b6 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -1,15 +1,15 @@ // Copyright (c) 2023 UltiMaker -// CuraEngine is released under the terms of the AGPLv3 or higher. +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef PATH_PLANNING_G_CODE_PATH_H #define PATH_PLANNING_G_CODE_PATH_H -#include "../SpaceFillType.h" -#include "../settings/types/Ratio.h" -#include "../sliceDataStorage.h" -#include "../utils/IntPoint.h" #include "GCodePathConfig.h" +#include "SpaceFillType.h" #include "TimeMaterialEstimates.h" +#include "settings/types/Ratio.h" +#include "sliceDataStorage.h" +#include "utils/IntPoint.h" namespace cura { diff --git a/include/pathPlanning/SpeedDerivatives.h b/include/pathPlanning/SpeedDerivatives.h new file mode 100644 index 0000000000..47c6cab0a6 --- /dev/null +++ b/include/pathPlanning/SpeedDerivatives.h @@ -0,0 +1,24 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef PATHPLANNING_SPEEDDERIVATIVES_H +#define PATHPLANNING_SPEEDDERIVATIVES_H + +#include "settings/types/LayerIndex.h" +#include "settings/types/Velocity.h" + +namespace cura +{ + +struct SpeedDerivatives +{ + Velocity speed{}; //!< movement speed (mm/s) + Acceleration acceleration{}; //!< acceleration of head movement (mm/s^2) + Velocity jerk{}; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s) + + constexpr void smoothSpeed(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer_nr); +}; + +} // namespace cura + +#endif // PATHPLANNING_SPEEDDERIVATIVES_H diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index 150f1a9fcb..b57fa0e8c7 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -1,10 +1,11 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef SETTINGS_PATH_CONFIGS_H #define SETTINGS_PATH_CONFIGS_H #include "GCodePathConfig.h" +#include "pathPlanning/SpeedDerivatives.h" #include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" @@ -53,7 +54,7 @@ class PathConfigStorage GCodePathConfig ironing_config; MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder); - void smoothAllSpeeds(GCodePathConfig::SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer); + void smoothAllSpeeds(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer); }; GCodePathConfig raft_base_config; diff --git a/src/GCodePathConfig.cpp b/src/GCodePathConfig.cpp index aeb9ab3933..0b2ff5a972 100644 --- a/src/GCodePathConfig.cpp +++ b/src/GCodePathConfig.cpp @@ -1,40 +1,47 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#include "GCodePathConfig.h" #include "settings/types/LayerIndex.h" #include "utils/IntPoint.h" // INT2MM -#include "GCodePathConfig.h" -namespace cura +namespace cura { GCodePathConfig::GCodePathConfig(const GCodePathConfig& other) -: type(other.type) -, speed_derivatives(other.speed_derivatives) -, line_width(other.line_width) -, layer_thickness(other.layer_thickness) -, flow(other.flow) -, extrusion_mm3_per_mm(other.extrusion_mm3_per_mm) -, is_bridge_path(other.is_bridge_path) -, fan_speed(other.fan_speed) + : type(other.type) + , speed_derivatives(other.speed_derivatives) + , line_width(other.line_width) + , layer_thickness(other.layer_thickness) + , flow(other.flow) + , extrusion_mm3_per_mm(other.extrusion_mm3_per_mm) + , is_bridge_path(other.is_bridge_path) + , fan_speed(other.fan_speed) { } - -GCodePathConfig::GCodePathConfig(const PrintFeatureType& type, const coord_t line_width, const coord_t layer_height, const Ratio& flow, const GCodePathConfig::SpeedDerivatives speed_derivatives, const bool is_bridge_path, const double fan_speed) -: type(type) -, speed_derivatives(speed_derivatives) -, line_width(line_width) -, layer_thickness(layer_height) -, flow(flow) -, extrusion_mm3_per_mm(calculateExtrusion()) -, is_bridge_path(is_bridge_path) -, fan_speed(fan_speed) +GCodePathConfig::GCodePathConfig( + const PrintFeatureType& type, + const coord_t line_width, + const coord_t layer_height, + const Ratio& flow, + const SpeedDerivatives speed_derivatives, + const bool is_bridge_path, + const double fan_speed) + : type(type) + , speed_derivatives(speed_derivatives) + , line_width(line_width) + , layer_thickness(layer_height) + , flow(flow) + , extrusion_mm3_per_mm(calculateExtrusion()) + , is_bridge_path(is_bridge_path) + , fan_speed(fan_speed) { } -void GCodePathConfig::smoothSpeed(GCodePathConfig::SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer_nr) +void GCodePathConfig::smoothSpeed(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer_nr) { double max_speed_layer = max_speed_layer_nr; double first_layer_speed = std::min(speed_derivatives.speed, first_layer_config.speed); @@ -106,4 +113,4 @@ double GCodePathConfig::calculateExtrusion() const } -}//namespace cura +} // namespace cura diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 743283025b..5b9c3770d3 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -11,7 +11,6 @@ #include "communication/Communication.h" #include "pathPlanning/Comb.h" #include "pathPlanning/CombPaths.h" -//#include "plugins/slots.h" #include "plugins/slots.h" #include "raft.h" // getTotalExtraLayers #include "settings/types/Ratio.h" diff --git a/src/pathPlanning/SpeedDerivatives.cpp b/src/pathPlanning/SpeedDerivatives.cpp new file mode 100644 index 0000000000..0b623c8766 --- /dev/null +++ b/src/pathPlanning/SpeedDerivatives.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#include "pathPlanning/SpeedDerivatives.h" + +namespace cura +{ + +constexpr void SpeedDerivatives::smoothSpeed(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer_nr) +{ + double max_speed_layer = max_speed_layer_nr; + double first_layer_speed = std::min(speed, first_layer_config.speed); + double first_layer_acceleration = std::min(acceleration, first_layer_config.acceleration); + double first_layer_jerk = std::min(jerk, first_layer_config.jerk); + speed = (speed * layer_nr) / max_speed_layer + (first_layer_speed * (max_speed_layer - layer_nr) / max_speed_layer); + acceleration = (acceleration * layer_nr) / max_speed_layer + (first_layer_acceleration * (max_speed_layer - layer_nr) / max_speed_layer); + jerk = (jerk * layer_nr) / max_speed_layer + (first_layer_jerk * (max_speed_layer - layer_nr) / max_speed_layer); +} + +} // namespace cura \ No newline at end of file diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 23779507f9..8fb4a91805 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -7,6 +7,7 @@ #include "GCodePathConfig.h" #include "WallToolPaths.h" #include "pathPlanning/GCodePath.h" +#include "pathPlanning/SpeedDerivatives.h" #include "settings/Settings.h" #include "settings/types/LayerIndex.h" #include "utils/polygon.h" @@ -413,9 +414,9 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper const coord_t line_width = gcode_path_msg.config().line_width(); const coord_t layer_height = gcode_path_msg.config().layer_thickness(); const Ratio flow = gcode_path_msg.config().flow_ratio(); - const GCodePathConfig::SpeedDerivatives speed_derivatives = { gcode_path_msg.config().speed_derivatives().velocity(), - gcode_path_msg.config().speed_derivatives().acceleration(), - gcode_path_msg.config().speed_derivatives().jerk() }; + const SpeedDerivatives speed_derivatives = { gcode_path_msg.config().speed_derivatives().velocity(), + gcode_path_msg.config().speed_derivatives().acceleration(), + gcode_path_msg.config().speed_derivatives().jerk() }; const bool is_bridge_path = gcode_path_msg.config().is_bridge_path(); const double fan_speed = gcode_path_msg.config().fan_speed(); return GCodePathConfig(type, line_width, layer_height, flow, speed_derivatives, is_bridge_path, fan_speed); diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 9ab4b7704c..54913752f0 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -1,6 +1,7 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher +#include "pathPlanning/SpeedDerivatives.h" #include "settings/PathConfigStorage.h" #include "settings/Settings.h" // MAX_INFILL_COMBINE #include "Application.h" @@ -37,21 +38,21 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("wall_0_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("speed_wall_0"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0")} + , SpeedDerivatives{mesh.settings.get("speed_wall_0"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0")} ) , insetX_config( PrintFeatureType::InnerWall , mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("wall_x_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("speed_wall_x"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x")} + , SpeedDerivatives{mesh.settings.get("speed_wall_x"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x")} ) , bridge_inset0_config( PrintFeatureType::OuterWall , mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("bridge_wall_material_flow") - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0")} + , SpeedDerivatives{mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0")} , true // is_bridge_path , mesh.settings.get("bridge_fan_speed") * 100.0 ) @@ -60,7 +61,7 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("bridge_wall_material_flow") - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x")} + , SpeedDerivatives{mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x")} , true // is_bridge_path , mesh.settings.get("bridge_fan_speed") * 100.0 ) @@ -69,14 +70,14 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("skin_material_flow") * ((layer_nr == 0) ? mesh.settings.get("skin_material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("speed_topbottom"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} + , SpeedDerivatives{mesh.settings.get("speed_topbottom"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} ) , bridge_skin_config( // use bridge skin flow, speed and fan PrintFeatureType::Skin , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("bridge_skin_material_flow") - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("bridge_skin_speed"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} + , SpeedDerivatives{mesh.settings.get("bridge_skin_speed"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} , true // is_bridge_path , mesh.settings.get("bridge_fan_speed") * 100.0 ) @@ -85,7 +86,7 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("bridge_skin_material_flow_2") - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("bridge_skin_speed_2"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} + , SpeedDerivatives{mesh.settings.get("bridge_skin_speed_2"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} , true // is_bridge_path , mesh.settings.get("bridge_fan_speed_2") * 100.0 ) @@ -94,7 +95,7 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("bridge_skin_material_flow_3") - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("bridge_skin_speed_3"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} + , SpeedDerivatives{mesh.settings.get("bridge_skin_speed_3"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} , true // is_bridge_path , mesh.settings.get("bridge_fan_speed_3") * 100.0 ) @@ -103,14 +104,14 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("roofing_line_width") , layer_thickness , mesh.settings.get("roofing_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("speed_roofing"), mesh.settings.get("acceleration_roofing"), mesh.settings.get("jerk_roofing")} + , SpeedDerivatives{mesh.settings.get("speed_roofing"), mesh.settings.get("acceleration_roofing"), mesh.settings.get("jerk_roofing")} ) , ironing_config( PrintFeatureType::Skin , mesh.settings.get("ironing_line_spacing") , layer_thickness , mesh.settings.get("ironing_flow") - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("speed_ironing"), mesh.settings.get("acceleration_ironing"), mesh.settings.get("jerk_ironing")} + , SpeedDerivatives{mesh.settings.get("speed_ironing"), mesh.settings.get("acceleration_ironing"), mesh.settings.get("jerk_ironing")} ) { @@ -123,7 +124,7 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh , mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr] , layer_thickness , mesh.settings.get("infill_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1) - , GCodePathConfig::SpeedDerivatives{mesh.settings.get("speed_infill"), mesh.settings.get("acceleration_infill"), mesh.settings.get("jerk_infill")} + , SpeedDerivatives{mesh.settings.get("speed_infill"), mesh.settings.get("acceleration_infill"), mesh.settings.get("jerk_infill")} ); } } @@ -144,35 +145,35 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye , raft_base_train.settings.get("raft_base_line_width") , raft_base_train.settings.get("raft_base_thickness") , Ratio(1.0) - , GCodePathConfig::SpeedDerivatives{raft_base_train.settings.get("raft_base_speed"), raft_base_train.settings.get("raft_base_acceleration"), raft_base_train.settings.get("raft_base_jerk")} + , SpeedDerivatives{raft_base_train.settings.get("raft_base_speed"), raft_base_train.settings.get("raft_base_acceleration"), raft_base_train.settings.get("raft_base_jerk")} ) , raft_interface_config( PrintFeatureType::Support , raft_interface_train.settings.get("raft_interface_line_width") , raft_interface_train.settings.get("raft_interface_thickness") , Ratio(1.0) - , GCodePathConfig::SpeedDerivatives{raft_interface_train.settings.get("raft_interface_speed"), raft_interface_train.settings.get("raft_interface_acceleration"), raft_interface_train.settings.get("raft_interface_jerk")} + , SpeedDerivatives{raft_interface_train.settings.get("raft_interface_speed"), raft_interface_train.settings.get("raft_interface_acceleration"), raft_interface_train.settings.get("raft_interface_jerk")} ) , raft_surface_config( PrintFeatureType::SupportInterface , raft_surface_train.settings.get("raft_surface_line_width") , raft_surface_train.settings.get("raft_surface_thickness") , Ratio(1.0) - , GCodePathConfig::SpeedDerivatives{raft_surface_train.settings.get("raft_surface_speed"), raft_surface_train.settings.get("raft_surface_acceleration"), raft_surface_train.settings.get("raft_surface_jerk")} + , SpeedDerivatives{raft_surface_train.settings.get("raft_surface_speed"), raft_surface_train.settings.get("raft_surface_acceleration"), raft_surface_train.settings.get("raft_surface_jerk")} ) , support_roof_config( PrintFeatureType::SupportInterface , support_roof_train.settings.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr] , layer_thickness , support_roof_train.settings.get("support_roof_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{support_roof_train.settings.get("speed_support_roof"), support_roof_train.settings.get("acceleration_support_roof"), support_roof_train.settings.get("jerk_support_roof")} + , SpeedDerivatives{support_roof_train.settings.get("speed_support_roof"), support_roof_train.settings.get("acceleration_support_roof"), support_roof_train.settings.get("jerk_support_roof")} ) , support_bottom_config( PrintFeatureType::SupportInterface , support_bottom_train.settings.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr] , layer_thickness , support_roof_train.settings.get("support_bottom_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{support_bottom_train.settings.get("speed_support_bottom"), support_bottom_train.settings.get("acceleration_support_bottom"), support_bottom_train.settings.get("jerk_support_bottom")} + , SpeedDerivatives{support_bottom_train.settings.get("speed_support_bottom"), support_bottom_train.settings.get("acceleration_support_bottom"), support_bottom_train.settings.get("jerk_support_bottom")} ) { const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size(); @@ -188,7 +189,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye , 0 , 0 , 0.0 - , GCodePathConfig::SpeedDerivatives{train.settings.get("speed_travel"), train.settings.get("acceleration_travel"), train.settings.get("jerk_travel")} + , SpeedDerivatives{train.settings.get("speed_travel"), train.settings.get("acceleration_travel"), train.settings.get("jerk_travel")} ); skirt_brim_config_per_extruder.emplace_back( PrintFeatureType::SkirtBrim @@ -196,7 +197,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]) // cause it's also used for the draft/ooze shield , layer_thickness , train.settings.get("skirt_brim_material_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{train.settings.get("skirt_brim_speed"), train.settings.get("acceleration_skirt_brim"), train.settings.get("jerk_skirt_brim")} + , SpeedDerivatives{train.settings.get("skirt_brim_speed"), train.settings.get("acceleration_skirt_brim"), train.settings.get("jerk_skirt_brim")} ); prime_tower_config_per_extruder.emplace_back( PrintFeatureType::PrimeTower @@ -204,7 +205,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]) , layer_thickness , train.settings.get("prime_tower_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , GCodePathConfig::SpeedDerivatives{train.settings.get("speed_prime_tower"), train.settings.get("acceleration_prime_tower"), train.settings.get("jerk_prime_tower")} + , SpeedDerivatives{train.settings.get("speed_prime_tower"), train.settings.get("acceleration_prime_tower"), train.settings.get("jerk_prime_tower")} ); } @@ -223,7 +224,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye , support_infill_train.settings.get("support_line_width") * support_infill_line_width_factor , layer_thickness , support_infill_train.settings.get("support_material_flow") * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1) - , GCodePathConfig::SpeedDerivatives{support_infill_train.settings.get("speed_support_infill"), support_infill_train.settings.get("acceleration_support_infill"), support_infill_train.settings.get("jerk_support_infill")} + , SpeedDerivatives{support_infill_train.settings.get("speed_support_infill"), support_infill_train.settings.get("acceleration_support_infill"), support_infill_train.settings.get("jerk_support_infill")} ); } @@ -234,7 +235,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } } -void PathConfigStorage::MeshPathConfigs::smoothAllSpeeds(GCodePathConfig::SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer) +void PathConfigStorage::MeshPathConfigs::smoothAllSpeeds(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer) { inset0_config.smoothSpeed( first_layer_config, layer_nr, max_speed_layer); insetX_config.smoothSpeed( first_layer_config, layer_nr, max_speed_layer); @@ -249,12 +250,12 @@ void PathConfigStorage::MeshPathConfigs::smoothAllSpeeds(GCodePathConfig::SpeedD void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storage, const LayerIndex& layer_nr, const size_t initial_speedup_layer_count) { - std::vector global_first_layer_config_per_extruder; + std::vector global_first_layer_config_per_extruder; global_first_layer_config_per_extruder.reserve(Application::getInstance().current_slice->scene.extruders.size()); for (const ExtruderTrain& extruder : Application::getInstance().current_slice->scene.extruders) { global_first_layer_config_per_extruder.emplace_back( - GCodePathConfig::SpeedDerivatives{ + SpeedDerivatives{ extruder.settings.get("speed_print_layer_0") , extruder.settings.get("acceleration_print_layer_0") , extruder.settings.get("jerk_print_layer_0") @@ -266,17 +267,17 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const size_t extruder_nr_support_infill = mesh_group_settings.get((layer_nr <= 0) ? "support_extruder_nr_layer_0" : "support_infill_extruder_nr").extruder_nr; - GCodePathConfig::SpeedDerivatives& first_layer_config_infill = global_first_layer_config_per_extruder[extruder_nr_support_infill]; + SpeedDerivatives& first_layer_config_infill = global_first_layer_config_per_extruder[extruder_nr_support_infill]; for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++) { support_infill_config[idx].smoothSpeed(first_layer_config_infill, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); } const size_t extruder_nr_support_roof = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; - GCodePathConfig::SpeedDerivatives& first_layer_config_roof = global_first_layer_config_per_extruder[extruder_nr_support_roof]; + SpeedDerivatives& first_layer_config_roof = global_first_layer_config_per_extruder[extruder_nr_support_roof]; support_roof_config.smoothSpeed(first_layer_config_roof, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); const size_t extruder_nr_support_bottom = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - GCodePathConfig::SpeedDerivatives& first_layer_config_bottom = global_first_layer_config_per_extruder[extruder_nr_support_bottom]; + SpeedDerivatives& first_layer_config_bottom = global_first_layer_config_per_extruder[extruder_nr_support_bottom]; support_bottom_config.smoothSpeed(first_layer_config_bottom, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); } } @@ -285,7 +286,7 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; - GCodePathConfig::SpeedDerivatives initial_layer_travel_speed_config{ + SpeedDerivatives initial_layer_travel_speed_config{ train.settings.get("speed_travel_layer_0") , train.settings.get("acceleration_travel_layer_0") , train.settings.get("jerk_travel_layer_0") @@ -297,7 +298,7 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& // don't smooth speed for the skirt/brim! // NOTE: not smoothing skirt/brim means the speeds are also not smoothed for the draft/ooze shield - const GCodePathConfig::SpeedDerivatives& initial_layer_print_speed_config = global_first_layer_config_per_extruder[extruder_nr]; + const SpeedDerivatives& initial_layer_print_speed_config = global_first_layer_config_per_extruder[extruder_nr]; GCodePathConfig& prime_tower = prime_tower_config_per_extruder[extruder_nr]; prime_tower.smoothSpeed(initial_layer_print_speed_config, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); @@ -310,7 +311,7 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& { const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; - GCodePathConfig::SpeedDerivatives initial_layer_speed_config{ + SpeedDerivatives initial_layer_speed_config{ mesh.settings.get("speed_print_layer_0") , mesh.settings.get("acceleration_print_layer_0") , mesh.settings.get("jerk_print_layer_0") diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index b1a49a344a..f5e1223816 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -2,6 +2,8 @@ // CuraEngine is released under the terms of the AGPLv3 or higher #include "LayerPlan.h" //Code under test. +#include "pathPlanning/SpeedDerivatives.h" + #include #include //For calculating averages. @@ -69,8 +71,8 @@ class ExtruderPlanTestPathCollection GCodePathConfig travel_config; ExtruderPlanTestPathCollection() - : extrusion_config(PrintFeatureType::OuterWall, 400, 100, 1.0_r, GCodePathConfig::SpeedDerivatives(50.0, 1000.0, 10.0)) - , travel_config(PrintFeatureType::MoveCombing, 0, 100, 0.0_r, GCodePathConfig::SpeedDerivatives(120.0, 5000.0, 30.0)) + : extrusion_config(PrintFeatureType::OuterWall, 400, 100, 1.0_r, SpeedDerivatives(50.0, 1000.0, 10.0)) + , travel_config(PrintFeatureType::MoveCombing, 0, 100, 0.0_r, SpeedDerivatives(120.0, 5000.0, 30.0)) { const SliceMeshStorage* mesh = nullptr; constexpr Ratio flow_1 = 1.0_r; @@ -78,23 +80,13 @@ class ExtruderPlanTestPathCollection constexpr bool no_spiralize = false; constexpr Ratio speed_1 = 1.0_r; square.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::PolyLines, flow_1, width_1, no_spiralize, speed_1) }); - square.back().points = - { - Point(0, 0), - Point(1000, 0), - Point(1000, 1000), - Point(0, 1000), - Point(0, 0) - }; - - lines.assign - ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1) - }); + square.back().points = { Point(0, 0), Point(1000, 0), Point(1000, 1000), Point(0, 1000), Point(0, 0) }; + + lines.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1) }); lines[0].points = { Point(0, 0), Point(1000, 0) }; lines[1].points = { Point(1000, 0), Point(1000, 400) }; lines[2].points = { Point(1000, 400), Point(0, 400) }; @@ -104,14 +96,11 @@ class ExtruderPlanTestPathCollection constexpr Ratio flow_12 = 1.2_r; constexpr Ratio flow_08 = 0.8_r; constexpr Ratio flow_04 = 0.4_r; - decreasing_flow.assign - ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_12, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_08, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_04, width_1, no_spiralize, speed_1) - }); + decreasing_flow.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_12, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_08, width_1, no_spiralize, speed_1), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_04, width_1, no_spiralize, speed_1) }); decreasing_flow[0].points = { Point(0, 0), Point(1000, 0) }; decreasing_flow[1].points = { Point(1000, 0), Point(1000, 400) }; decreasing_flow[2].points = { Point(1000, 400), Point(0, 400) }; @@ -121,22 +110,18 @@ class ExtruderPlanTestPathCollection constexpr Ratio speed_12 = 1.2_r; constexpr Ratio speed_08 = 0.8_r; constexpr Ratio speed_04 = 0.4_r; - decreasing_speed.assign - ({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_12), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_08), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_04) - }); + decreasing_speed.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_12), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_08), + GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), + GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_04) }); decreasing_speed[0].points = { Point(0, 0), Point(1000, 0) }; decreasing_speed[1].points = { Point(1000, 0), Point(1000, 400) }; decreasing_speed[2].points = { Point(1000, 400), Point(0, 400) }; decreasing_speed[3].points = { Point(0, 400), Point(0, 800) }; decreasing_speed[4].points = { Point(0, 800), Point(1000, 800) }; - variable_width.assign - ({ + variable_width.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.8_r, width_1, no_spiralize, speed_1), GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.6_r, width_1, no_spiralize, speed_1), @@ -203,13 +188,15 @@ class ExtruderPlanPathsParameterizedTest : public testing::TestWithParam 0.0 && path.width_factor > 0.0 && path.config.getFlowRatio() > 0.0 && path.config.getLineWidth() > 0 && ! path.config.isTravelPath() && ! path.config.isBridgePath(); + return path.flow > 0.0 && path.width_factor > 0.0 && path.config.getFlowRatio() > 0.0 && path.config.getLineWidth() > 0 && ! path.config.isTravelPath() + && ! path.config.isBridgePath(); } }; -INSTANTIATE_TEST_SUITE_P(ExtruderPlanTestInstantiation, - ExtruderPlanPathsParameterizedTest, - testing::Values(path_collection.square, path_collection.lines, path_collection.decreasing_flow, path_collection.decreasing_speed, path_collection.variable_width)); +INSTANTIATE_TEST_SUITE_P( + ExtruderPlanTestInstantiation, + ExtruderPlanPathsParameterizedTest, + testing::Values(path_collection.square, path_collection.lines, path_collection.decreasing_flow, path_collection.decreasing_speed, path_collection.variable_width)); /*! * A fixture for general test cases involving extruder plans. @@ -271,7 +258,13 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationFull) extruder_plan.paths = GetParam(); extruder_plan.applyBackPressureCompensation(1.0_r); - auto first_extrusion = std::find_if(extruder_plan.paths.begin(), extruder_plan.paths.end(), [&](GCodePath& path) { return shouldCountPath(path); }); + auto first_extrusion = std::find_if( + extruder_plan.paths.begin(), + extruder_plan.paths.end(), + [&](GCodePath& path) + { + return shouldCountPath(path); + }); if (first_extrusion == extruder_plan.paths.end()) // Only travel moves in this plan. { return; @@ -286,7 +279,8 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationFull) continue; // Ignore travel moves. } const double flow_mm3_per_sec = calculatePathWidth(path); - EXPECT_NEAR(flow_mm3_per_sec, first_flow_mm3_per_sec, error_margin) << "Every path must have a flow rate equal to the first, since the flow changes were completely compensated for."; + EXPECT_NEAR(flow_mm3_per_sec, first_flow_mm3_per_sec, error_margin) + << "Every path must have a flow rate equal to the first, since the flow changes were completely compensated for."; } } @@ -330,7 +324,8 @@ TEST_P(ExtruderPlanPathsParameterizedTest, BackPressureCompensationHalf) ASSERT_EQ(original_flows.size(), new_flows.size()) << "We need to have the same number of extrusion moves."; for (size_t i = 0; i < new_flows.size(); ++i) { - EXPECT_NEAR((original_flows[i] - original_average) / 2.0, new_flows[i] - new_average, error_margin) << "The differences in flow rate needs to be approximately halved, within margin of rounding errors."; + EXPECT_NEAR((original_flows[i] - original_average) / 2.0, new_flows[i] - new_average, error_margin) + << "The differences in flow rate needs to be approximately halved, within margin of rounding errors."; } } From b4db13e55b11ac0cc219895911d61444aed260b7 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 16 Aug 2023 14:48:21 +0000 Subject: [PATCH 441/656] Applied clang-format. --- src/settings/PathConfigStorage.cpp | 386 +++++++++++++++-------------- 1 file changed, 199 insertions(+), 187 deletions(-) diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 54913752f0..5a190f308c 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -1,15 +1,16 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include "pathPlanning/SpeedDerivatives.h" #include "settings/PathConfigStorage.h" -#include "settings/Settings.h" // MAX_INFILL_COMBINE + #include "Application.h" #include "ExtruderTrain.h" -#include "raft.h" #include "Slice.h" -#include "sliceDataStorage.h" // SliceDataStorage +#include "pathPlanning/SpeedDerivatives.h" +#include "raft.h" #include "settings/EnumSettings.h" //For EPlatformAdhesion. +#include "settings/Settings.h" // MAX_INFILL_COMBINE +#include "sliceDataStorage.h" // SliceDataStorage namespace cura { @@ -32,87 +33,94 @@ std::vector PathConfigStorage::getLineWidthFactorPerExtruder(const LayerI return ret; } -PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder) -: inset0_config( - PrintFeatureType::OuterWall - , mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("wall_0_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{mesh.settings.get("speed_wall_0"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0")} -) -, insetX_config( - PrintFeatureType::InnerWall - , mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("wall_x_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{mesh.settings.get("speed_wall_x"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x")} -) -, bridge_inset0_config( - PrintFeatureType::OuterWall - , mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("bridge_wall_material_flow") - , SpeedDerivatives{mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0")} - , true // is_bridge_path - , mesh.settings.get("bridge_fan_speed") * 100.0 -) -, bridge_insetX_config( - PrintFeatureType::InnerWall - , mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("bridge_wall_material_flow") - , SpeedDerivatives{mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x")} - , true // is_bridge_path - , mesh.settings.get("bridge_fan_speed") * 100.0 -) -, skin_config( - PrintFeatureType::Skin - , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("skin_material_flow") * ((layer_nr == 0) ? mesh.settings.get("skin_material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{mesh.settings.get("speed_topbottom"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} -) -, bridge_skin_config( // use bridge skin flow, speed and fan - PrintFeatureType::Skin - , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("bridge_skin_material_flow") - , SpeedDerivatives{mesh.settings.get("bridge_skin_speed"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} - , true // is_bridge_path - , mesh.settings.get("bridge_fan_speed") * 100.0 -) -, bridge_skin_config2( // use bridge skin 2 flow, speed and fan - PrintFeatureType::Skin - , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("bridge_skin_material_flow_2") - , SpeedDerivatives{mesh.settings.get("bridge_skin_speed_2"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} - , true // is_bridge_path - , mesh.settings.get("bridge_fan_speed_2") * 100.0 -) -, bridge_skin_config3( // use bridge skin 3 flow, speed and fan - PrintFeatureType::Skin - , mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("bridge_skin_material_flow_3") - , SpeedDerivatives{mesh.settings.get("bridge_skin_speed_3"), mesh.settings.get("acceleration_topbottom"), mesh.settings.get("jerk_topbottom")} - , true // is_bridge_path - , mesh.settings.get("bridge_fan_speed_3") * 100.0 -) -, roofing_config( - PrintFeatureType::Skin - , mesh.settings.get("roofing_line_width") - , layer_thickness - , mesh.settings.get("roofing_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{mesh.settings.get("speed_roofing"), mesh.settings.get("acceleration_roofing"), mesh.settings.get("jerk_roofing")} -) -, ironing_config( - PrintFeatureType::Skin - , mesh.settings.get("ironing_line_spacing") - , layer_thickness - , mesh.settings.get("ironing_flow") - , SpeedDerivatives{mesh.settings.get("speed_ironing"), mesh.settings.get("acceleration_ironing"), mesh.settings.get("jerk_ironing")} -) +PathConfigStorage::MeshPathConfigs::MeshPathConfigs( + const SliceMeshStorage& mesh, + const coord_t layer_thickness, + const LayerIndex& layer_nr, + const std::vector& line_width_factor_per_extruder) + : inset0_config( + PrintFeatureType::OuterWall, + mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("wall_0_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ mesh.settings.get("speed_wall_0"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0") }) + , insetX_config( + PrintFeatureType::InnerWall, + mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("wall_x_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ mesh.settings.get("speed_wall_x"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x") }) + , bridge_inset0_config( + PrintFeatureType::OuterWall, + mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("bridge_wall_material_flow"), + SpeedDerivatives{ mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0") }, + true // is_bridge_path + , + mesh.settings.get("bridge_fan_speed") * 100.0) + , bridge_insetX_config( + PrintFeatureType::InnerWall, + mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("bridge_wall_material_flow"), + SpeedDerivatives{ mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x") }, + true // is_bridge_path + , + mesh.settings.get("bridge_fan_speed") * 100.0) + , skin_config( + PrintFeatureType::Skin, + mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("skin_material_flow") * ((layer_nr == 0) ? mesh.settings.get("skin_material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ mesh.settings.get("speed_topbottom"), + mesh.settings.get("acceleration_topbottom"), + mesh.settings.get("jerk_topbottom") }) + , bridge_skin_config( // use bridge skin flow, speed and fan + PrintFeatureType::Skin, + mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("bridge_skin_material_flow"), + SpeedDerivatives{ mesh.settings.get("bridge_skin_speed"), + mesh.settings.get("acceleration_topbottom"), + mesh.settings.get("jerk_topbottom") }, + true // is_bridge_path + , + mesh.settings.get("bridge_fan_speed") * 100.0) + , bridge_skin_config2( // use bridge skin 2 flow, speed and fan + PrintFeatureType::Skin, + mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("bridge_skin_material_flow_2"), + SpeedDerivatives{ mesh.settings.get("bridge_skin_speed_2"), + mesh.settings.get("acceleration_topbottom"), + mesh.settings.get("jerk_topbottom") }, + true // is_bridge_path + , + mesh.settings.get("bridge_fan_speed_2") * 100.0) + , bridge_skin_config3( // use bridge skin 3 flow, speed and fan + PrintFeatureType::Skin, + mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("bridge_skin_material_flow_3"), + SpeedDerivatives{ mesh.settings.get("bridge_skin_speed_3"), + mesh.settings.get("acceleration_topbottom"), + mesh.settings.get("jerk_topbottom") }, + true // is_bridge_path + , + mesh.settings.get("bridge_fan_speed_3") * 100.0) + , roofing_config( + PrintFeatureType::Skin, + mesh.settings.get("roofing_line_width"), + layer_thickness, + mesh.settings.get("roofing_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ mesh.settings.get("speed_roofing"), mesh.settings.get("acceleration_roofing"), mesh.settings.get("jerk_roofing") }) + , ironing_config( + PrintFeatureType::Skin, + mesh.settings.get("ironing_line_spacing"), + layer_thickness, + mesh.settings.get("ironing_flow"), + SpeedDerivatives{ mesh.settings.get("speed_ironing"), mesh.settings.get("acceleration_ironing"), mesh.settings.get("jerk_ironing") }) { infill_config.reserve(MAX_INFILL_COMBINE); @@ -120,61 +128,65 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++) { infill_config.emplace_back( - PrintFeatureType::Infill - , mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr] - , layer_thickness - , mesh.settings.get("infill_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1) - , SpeedDerivatives{mesh.settings.get("speed_infill"), mesh.settings.get("acceleration_infill"), mesh.settings.get("jerk_infill")} - ); + PrintFeatureType::Infill, + mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr], + layer_thickness, + mesh.settings.get("infill_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1), + SpeedDerivatives{ mesh.settings.get("speed_infill"), mesh.settings.get("acceleration_infill"), mesh.settings.get("jerk_infill") }); } } PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const LayerIndex& layer_nr, const coord_t layer_thickness) -: support_infill_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_infill_extruder_nr").extruder_nr) -, support_roof_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_roof_extruder_nr").extruder_nr) -, support_bottom_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_bottom_extruder_nr").extruder_nr) -, raft_base_train(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_base_extruder_nr")) -, raft_interface_train(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_interface_extruder_nr")) -, raft_surface_train(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_surface_extruder_nr")) -, support_infill_train(Application::getInstance().current_slice->scene.extruders[support_infill_extruder_nr]) -, support_roof_train(Application::getInstance().current_slice->scene.extruders[support_roof_extruder_nr]) -, support_bottom_train(Application::getInstance().current_slice->scene.extruders[support_bottom_extruder_nr]) -, line_width_factor_per_extruder(PathConfigStorage::getLineWidthFactorPerExtruder(layer_nr)) -, raft_base_config( - PrintFeatureType::SupportInterface - , raft_base_train.settings.get("raft_base_line_width") - , raft_base_train.settings.get("raft_base_thickness") - , Ratio(1.0) - , SpeedDerivatives{raft_base_train.settings.get("raft_base_speed"), raft_base_train.settings.get("raft_base_acceleration"), raft_base_train.settings.get("raft_base_jerk")} - ) -, raft_interface_config( - PrintFeatureType::Support - , raft_interface_train.settings.get("raft_interface_line_width") - , raft_interface_train.settings.get("raft_interface_thickness") - , Ratio(1.0) - , SpeedDerivatives{raft_interface_train.settings.get("raft_interface_speed"), raft_interface_train.settings.get("raft_interface_acceleration"), raft_interface_train.settings.get("raft_interface_jerk")} - ) -, raft_surface_config( - PrintFeatureType::SupportInterface - , raft_surface_train.settings.get("raft_surface_line_width") - , raft_surface_train.settings.get("raft_surface_thickness") - , Ratio(1.0) - , SpeedDerivatives{raft_surface_train.settings.get("raft_surface_speed"), raft_surface_train.settings.get("raft_surface_acceleration"), raft_surface_train.settings.get("raft_surface_jerk")} - ) -, support_roof_config( - PrintFeatureType::SupportInterface - , support_roof_train.settings.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr] - , layer_thickness - , support_roof_train.settings.get("support_roof_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{support_roof_train.settings.get("speed_support_roof"), support_roof_train.settings.get("acceleration_support_roof"), support_roof_train.settings.get("jerk_support_roof")} - ) -, support_bottom_config( - PrintFeatureType::SupportInterface - , support_bottom_train.settings.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr] - , layer_thickness - , support_roof_train.settings.get("support_bottom_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{support_bottom_train.settings.get("speed_support_bottom"), support_bottom_train.settings.get("acceleration_support_bottom"), support_bottom_train.settings.get("jerk_support_bottom")} - ) + : support_infill_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_infill_extruder_nr").extruder_nr) + , support_roof_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_roof_extruder_nr").extruder_nr) + , support_bottom_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_bottom_extruder_nr").extruder_nr) + , raft_base_train(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_base_extruder_nr")) + , raft_interface_train(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_interface_extruder_nr")) + , raft_surface_train(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_surface_extruder_nr")) + , support_infill_train(Application::getInstance().current_slice->scene.extruders[support_infill_extruder_nr]) + , support_roof_train(Application::getInstance().current_slice->scene.extruders[support_roof_extruder_nr]) + , support_bottom_train(Application::getInstance().current_slice->scene.extruders[support_bottom_extruder_nr]) + , line_width_factor_per_extruder(PathConfigStorage::getLineWidthFactorPerExtruder(layer_nr)) + , raft_base_config( + PrintFeatureType::SupportInterface, + raft_base_train.settings.get("raft_base_line_width"), + raft_base_train.settings.get("raft_base_thickness"), + Ratio(1.0), + SpeedDerivatives{ raft_base_train.settings.get("raft_base_speed"), + raft_base_train.settings.get("raft_base_acceleration"), + raft_base_train.settings.get("raft_base_jerk") }) + , raft_interface_config( + PrintFeatureType::Support, + raft_interface_train.settings.get("raft_interface_line_width"), + raft_interface_train.settings.get("raft_interface_thickness"), + Ratio(1.0), + SpeedDerivatives{ raft_interface_train.settings.get("raft_interface_speed"), + raft_interface_train.settings.get("raft_interface_acceleration"), + raft_interface_train.settings.get("raft_interface_jerk") }) + , raft_surface_config( + PrintFeatureType::SupportInterface, + raft_surface_train.settings.get("raft_surface_line_width"), + raft_surface_train.settings.get("raft_surface_thickness"), + Ratio(1.0), + SpeedDerivatives{ raft_surface_train.settings.get("raft_surface_speed"), + raft_surface_train.settings.get("raft_surface_acceleration"), + raft_surface_train.settings.get("raft_surface_jerk") }) + , support_roof_config( + PrintFeatureType::SupportInterface, + support_roof_train.settings.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr], + layer_thickness, + support_roof_train.settings.get("support_roof_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ support_roof_train.settings.get("speed_support_roof"), + support_roof_train.settings.get("acceleration_support_roof"), + support_roof_train.settings.get("jerk_support_roof") }) + , support_bottom_config( + PrintFeatureType::SupportInterface, + support_bottom_train.settings.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr], + layer_thickness, + support_roof_train.settings.get("support_bottom_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ support_bottom_train.settings.get("speed_support_bottom"), + support_bottom_train.settings.get("acceleration_support_bottom"), + support_bottom_train.settings.get("jerk_support_bottom") }) { const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size(); travel_config_per_extruder.reserve(extruder_count); @@ -185,28 +197,32 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye { const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; travel_config_per_extruder.emplace_back( - PrintFeatureType::MoveCombing - , 0 - , 0 - , 0.0 - , SpeedDerivatives{train.settings.get("speed_travel"), train.settings.get("acceleration_travel"), train.settings.get("jerk_travel")} - ); + PrintFeatureType::MoveCombing, + 0, + 0, + 0.0, + SpeedDerivatives{ train.settings.get("speed_travel"), train.settings.get("acceleration_travel"), train.settings.get("jerk_travel") }); skirt_brim_config_per_extruder.emplace_back( - PrintFeatureType::SkirtBrim - , train.settings.get("skirt_brim_line_width") - * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]) // cause it's also used for the draft/ooze shield - , layer_thickness - , train.settings.get("skirt_brim_material_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{train.settings.get("skirt_brim_speed"), train.settings.get("acceleration_skirt_brim"), train.settings.get("jerk_skirt_brim")} - ); + PrintFeatureType::SkirtBrim, + train.settings.get("skirt_brim_line_width") + * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) + ? 1.0_r + : line_width_factor_per_extruder[extruder_nr]) // cause it's also used for the draft/ooze shield + , + layer_thickness, + train.settings.get("skirt_brim_material_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ train.settings.get("skirt_brim_speed"), + train.settings.get("acceleration_skirt_brim"), + train.settings.get("jerk_skirt_brim") }); prime_tower_config_per_extruder.emplace_back( - PrintFeatureType::PrimeTower - , train.settings.get("prime_tower_line_width") - * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]) - , layer_thickness - , train.settings.get("prime_tower_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)) - , SpeedDerivatives{train.settings.get("speed_prime_tower"), train.settings.get("acceleration_prime_tower"), train.settings.get("jerk_prime_tower")} - ); + PrintFeatureType::PrimeTower, + train.settings.get("prime_tower_line_width") + * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]), + layer_thickness, + train.settings.get("prime_tower_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), + SpeedDerivatives{ train.settings.get("speed_prime_tower"), + train.settings.get("acceleration_prime_tower"), + train.settings.get("jerk_prime_tower") }); } mesh_configs.reserve(storage.meshes.size()); @@ -216,16 +232,19 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } support_infill_config.reserve(MAX_INFILL_COMBINE); - const float support_infill_line_width_factor = (mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[support_infill_extruder_nr]; + const float support_infill_line_width_factor + = (mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[support_infill_extruder_nr]; for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++) { support_infill_config.emplace_back( - PrintFeatureType::Support - , support_infill_train.settings.get("support_line_width") * support_infill_line_width_factor - , layer_thickness - , support_infill_train.settings.get("support_material_flow") * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1) - , SpeedDerivatives{support_infill_train.settings.get("speed_support_infill"), support_infill_train.settings.get("acceleration_support_infill"), support_infill_train.settings.get("jerk_support_infill")} - ); + PrintFeatureType::Support, + support_infill_train.settings.get("support_line_width") * support_infill_line_width_factor, + layer_thickness, + support_infill_train.settings.get("support_material_flow") * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) + * (combine_idx + 1), + SpeedDerivatives{ support_infill_train.settings.get("speed_support_infill"), + support_infill_train.settings.get("acceleration_support_infill"), + support_infill_train.settings.get("jerk_support_infill") }); } const size_t initial_speedup_layer_count = mesh_group_settings.get("speed_slowdown_layers"); @@ -237,13 +256,13 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye void PathConfigStorage::MeshPathConfigs::smoothAllSpeeds(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer) { - inset0_config.smoothSpeed( first_layer_config, layer_nr, max_speed_layer); - insetX_config.smoothSpeed( first_layer_config, layer_nr, max_speed_layer); - skin_config.smoothSpeed( first_layer_config, layer_nr, max_speed_layer); - ironing_config.smoothSpeed( first_layer_config, layer_nr, max_speed_layer); + inset0_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + insetX_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + skin_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + ironing_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); for (size_t idx = 0; idx < MAX_INFILL_COMBINE; idx++) { - //Infill speed (per combine part per mesh). + // Infill speed (per combine part per mesh). infill_config[idx].smoothSpeed(first_layer_config, layer_nr, max_speed_layer); } } @@ -254,19 +273,17 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& global_first_layer_config_per_extruder.reserve(Application::getInstance().current_slice->scene.extruders.size()); for (const ExtruderTrain& extruder : Application::getInstance().current_slice->scene.extruders) { - global_first_layer_config_per_extruder.emplace_back( - SpeedDerivatives{ - extruder.settings.get("speed_print_layer_0") - , extruder.settings.get("acceleration_print_layer_0") - , extruder.settings.get("jerk_print_layer_0") - }); + global_first_layer_config_per_extruder.emplace_back(SpeedDerivatives{ extruder.settings.get("speed_print_layer_0"), + extruder.settings.get("acceleration_print_layer_0"), + extruder.settings.get("jerk_print_layer_0") }); } { // support if (layer_nr < static_cast(initial_speedup_layer_count)) { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const size_t extruder_nr_support_infill = mesh_group_settings.get((layer_nr <= 0) ? "support_extruder_nr_layer_0" : "support_infill_extruder_nr").extruder_nr; + const size_t extruder_nr_support_infill + = mesh_group_settings.get((layer_nr <= 0) ? "support_extruder_nr_layer_0" : "support_infill_extruder_nr").extruder_nr; SpeedDerivatives& first_layer_config_infill = global_first_layer_config_per_extruder[extruder_nr_support_infill]; for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++) { @@ -286,11 +303,9 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; - SpeedDerivatives initial_layer_travel_speed_config{ - train.settings.get("speed_travel_layer_0") - , train.settings.get("acceleration_travel_layer_0") - , train.settings.get("jerk_travel_layer_0") - }; + SpeedDerivatives initial_layer_travel_speed_config{ train.settings.get("speed_travel_layer_0"), + train.settings.get("acceleration_travel_layer_0"), + train.settings.get("jerk_travel_layer_0") }; GCodePathConfig& travel = travel_config_per_extruder[extruder_nr]; travel.smoothSpeed(initial_layer_travel_speed_config, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); @@ -303,7 +318,6 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& GCodePathConfig& prime_tower = prime_tower_config_per_extruder[extruder_nr]; prime_tower.smoothSpeed(initial_layer_print_speed_config, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); } - } { // meshes @@ -311,11 +325,9 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& { const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; - SpeedDerivatives initial_layer_speed_config{ - mesh.settings.get("speed_print_layer_0") - , mesh.settings.get("acceleration_print_layer_0") - , mesh.settings.get("jerk_print_layer_0") - }; + SpeedDerivatives initial_layer_speed_config{ mesh.settings.get("speed_print_layer_0"), + mesh.settings.get("acceleration_print_layer_0"), + mesh.settings.get("jerk_print_layer_0") }; mesh_configs[mesh_idx].smoothAllSpeeds(initial_layer_speed_config, layer_nr, initial_speedup_layer_count); mesh_configs[mesh_idx].roofing_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count); @@ -323,4 +335,4 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& } } -}//namespace cura +} // namespace cura From 5a0ae57d33d21c275a59f4ce706e288048a6779b Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 16 Aug 2023 18:04:26 +0200 Subject: [PATCH 442/656] Inner countour needs to be unioned and re-intersected anyway to fix spill. And the union _with_ prevent_small_exposed_to_air doesn't hurt if the area us 0 anyway. Done as part of CURA-10829, but fixes a bug found when QA'ing CURA-10670, which was split off and numbered CURA-10945. --- src/infill.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 279e1ceff3..4f4fd757e4 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -111,10 +111,7 @@ void Infill::generate( inner_contour = inner_contour.offset(-small_area_width / 2); inner_contour.removeSmallAreas(too_small_length * too_small_length, true); inner_contour = inner_contour.offset(small_area_width / 2); - if (prevent_small_exposed_to_air.area() > 0) - { - inner_contour = inner_contour.unionPolygons(prevent_small_exposed_to_air).intersection(small_infill); - } + inner_contour = inner_contour.unionPolygons(prevent_small_exposed_to_air).intersection(small_infill); inner_contour = Simplify(max_resolution, max_deviation, 0).polygon(inner_contour); small_infill = small_infill.difference(inner_contour); From edf782a8855b5751b99f99395cf7336ef1bffbf5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 16 Aug 2023 18:21:05 +0200 Subject: [PATCH 443/656] Refactor `MeshPathConfigs` related code for better readability This commit refactors the `MeshPathConfigs` related code for clarity and readability. It eliminates the `PathConfigStorage::` scope resolution from `MeshPathConfigs` usages and refactors the SpeedDerivatives struct initialization to use designated initialization. It also refactors the `handleInitialLayerSpeedup` function to use method `smoothSpeed` on `speed_derivatives` directly. This makes the code cleaner and easier to understand. Contribute to CURA-10446 --- CMakeLists.txt | 1 - include/FffGcodeWriter.h | 30 ++--- include/GCodePathConfig.h | 48 ++----- include/pathPlanning/SpeedDerivatives.h | 14 +- include/plugins/converters.h | 1 + include/settings/PathConfigStorage.h | 38 +++--- src/FffGcodeWriter.cpp | 30 ++--- src/GCodePathConfig.cpp | 43 ------ src/pathPlanning/SpeedDerivatives.cpp | 17 +-- src/settings/PathConfigStorage.cpp | 166 +++++++++++++----------- 10 files changed, 174 insertions(+), 214 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42a6ea97fd..6a100acaaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,7 +160,6 @@ target_include_directories(_CuraEngine $ PRIVATE $ # Include Cura.pb.h - $ # Include generated plugin types ) target_compile_definitions(_CuraEngine diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 014e29f059..82e28c447c 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -1,5 +1,5 @@ // Copyright (c) 2023 UltiMaker -// CuraEngine is released under the terms of the AGPLv3 or higher. +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef GCODE_WRITER_H #define GCODE_WRITER_H @@ -309,7 +309,7 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcodeLayer) const; + void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcodeLayer) const; /*! * Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes. @@ -319,7 +319,7 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ - void addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; + void addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; /*! * Add all features of a given extruder from a single layer from a single mesh-volume to the layer plan \p gcode_layer. @@ -332,7 +332,7 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; + void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. @@ -346,7 +346,7 @@ class FffGcodeWriter : public NoCopy * \param part The part to add * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) const; + void addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) const; /*! * \brief Add infill for a given part in a layer plan. @@ -359,7 +359,7 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; /*! * \brief Add thicker (multiple layers) sparse infill for a given part in a @@ -373,7 +373,7 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; /*! * \brief Add normal sparse infill for a given part in a layer. @@ -385,7 +385,7 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processSingleLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processSingleLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; /*! * Generate the insets for the walls of a given layer part. @@ -397,7 +397,7 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processInsets(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processInsets(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; /*! * Generate the a spiralized wall for a given layer part. @@ -407,7 +407,7 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. */ - void processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const; + void processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const; /*! * Add the gcode of the top/bottom skin of the given part and of the perimeter gaps. @@ -420,7 +420,7 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; /*! * Add the gcode of the top/bottom skin of the given skin part and of the perimeter gaps. @@ -443,7 +443,7 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part) const; + bool processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SkinPart& skin_part) const; /*! * Add the roofing which is the area inside the innermost skin inset which has air 'directly' above @@ -456,7 +456,7 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processRoofing(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; + void processRoofing(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; /*! * Add the normal skinfill which is the area inside the innermost skin inset @@ -470,7 +470,7 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processTopBottom(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; + void processTopBottom(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; /*! * Process a dense skin feature like roofing or top/bottom @@ -491,7 +491,7 @@ class FffGcodeWriter : public NoCopy * \param[out] added_something Whether this function added anything to the layer plan * \param fan_speed fan speed override for this skin area */ - void processSkinPrintFeature(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, EFillMethod pattern, const AngleDegrees skin_angle, const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; + void processSkinPrintFeature(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, EFillMethod pattern, const AngleDegrees skin_angle, const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index be02d75053..9bf8002883 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -17,48 +17,18 @@ namespace cura /*! * The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed. */ -class GCodePathConfig +struct GCodePathConfig { -public: - const PrintFeatureType type; //!< name of the feature type + PrintFeatureType type {}; //!< name of the feature type + coord_t line_width {}; //!< width of the line extruded + coord_t layer_thickness {}; //!< current layer height in micron + Ratio flow {}; //!< extrusion flow modifier. + SpeedDerivatives speed_derivatives {}; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called. + bool is_bridge_path { false }; //!< whether current config is used when bridging + double fan_speed { FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise + double extrusion_mm3_per_mm { calculateExtrusion() }; //!< current mm^3 filament moved per mm line traversed static constexpr double FAN_SPEED_DEFAULT = -1; -private: - SpeedDerivatives speed_derivatives; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called. - const coord_t line_width; //!< width of the line extruded - const coord_t layer_thickness; //!< current layer height in micron - const Ratio flow; //!< extrusion flow modifier. - const double extrusion_mm3_per_mm; //!< current mm^3 filament moved per mm line traversed - const bool is_bridge_path; //!< whether current config is used when bridging - const double fan_speed; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise -public: - GCodePathConfig( - const PrintFeatureType& type, - const coord_t line_width, - const coord_t layer_height, - const Ratio& flow, - const SpeedDerivatives speed_derivatives, - const bool is_bridge_path = false, - const double fan_speed = FAN_SPEED_DEFAULT); - - /*! - * copy constructor - */ - GCodePathConfig(const GCodePathConfig& other); - - /*! - * Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed. - * - * \warning This functions should not be called with @p layer_nr > @p max_speed_layer ! - * - * \warning Calling this function twice will smooth the speed more toward \p first_layer_config - * - * \param first_layer_config The speed settings at layer zero - * \param layer_nr The layer number - * \param max_speed_layer The layer number for which the speed_iconic should be used. - */ - void smoothSpeed(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer); - /*! * Can only be called after the layer height has been set (which is done while writing the gcode!) */ diff --git a/include/pathPlanning/SpeedDerivatives.h b/include/pathPlanning/SpeedDerivatives.h index 47c6cab0a6..f22afc795b 100644 --- a/include/pathPlanning/SpeedDerivatives.h +++ b/include/pathPlanning/SpeedDerivatives.h @@ -16,7 +16,19 @@ struct SpeedDerivatives Acceleration acceleration{}; //!< acceleration of head movement (mm/s^2) Velocity jerk{}; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s) - constexpr void smoothSpeed(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer_nr); + + /*! + * Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed. + * + * \warning This functions should not be called with @p layer_nr > @p max_speed_layer ! + * + * \warning Calling this function twice will smooth the speed more toward \p first_layer_config + * + * \param first_layer_config The speed settings at layer zero + * \param layer_nr The layer number + * \param max_speed_layer The layer number for which the speed_iconic should be used. + */ + void smoothSpeed(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer_nr); }; } // namespace cura diff --git a/include/plugins/converters.h b/include/plugins/converters.h index f25f6ea1ce..6219cbe502 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -24,6 +24,7 @@ #include "settings/Settings.h" #include "settings/types/LayerIndex.h" #include "utils/polygon.h" +#include "pathPlanning/SpeedDerivatives.h" #include #include diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index b57fa0e8c7..ea44eba320 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -18,6 +18,25 @@ class ExtruderTrain; class SliceDataStorage; class SliceMeshStorage; +class MeshPathConfigs +{ +public: + GCodePathConfig inset0_config; + GCodePathConfig insetX_config; + GCodePathConfig bridge_inset0_config; + GCodePathConfig bridge_insetX_config; + GCodePathConfig skin_config; + GCodePathConfig bridge_skin_config; // used for first bridge layer + GCodePathConfig bridge_skin_config2; // used for second bridge layer + GCodePathConfig bridge_skin_config3; // used for third bridge layer + GCodePathConfig roofing_config; + std::vector infill_config; + GCodePathConfig ironing_config; + + MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder); + void smoothAllSpeeds(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer); +}; + /*! * A class to represent all configurations for all features types of printed lines in a meshgroup. */ @@ -38,24 +57,7 @@ class PathConfigStorage static std::vector getLineWidthFactorPerExtruder(const LayerIndex& layer_nr); public: - class MeshPathConfigs - { - public: - GCodePathConfig inset0_config; - GCodePathConfig insetX_config; - GCodePathConfig bridge_inset0_config; - GCodePathConfig bridge_insetX_config; - GCodePathConfig skin_config; - GCodePathConfig bridge_skin_config; // used for first bridge layer - GCodePathConfig bridge_skin_config2; // used for second bridge layer - GCodePathConfig bridge_skin_config3; // used for third bridge layer - GCodePathConfig roofing_config; - std::vector infill_config; - GCodePathConfig ironing_config; - - MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder); - void smoothAllSpeeds(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer); - }; + GCodePathConfig raft_base_config; GCodePathConfig raft_interface_config; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 94e28c504d..a9835f852e 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1022,7 +1022,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn for (size_t mesh_idx : mesh_order) { const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; - const PathConfigStorage::MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; + const MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; if (mesh.settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! @@ -1407,7 +1407,7 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode( const SliceDataStorage& storage, const SliceMeshStorage& mesh, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) @@ -1442,7 +1442,7 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode( addMeshOpenPolyLinesToGCode(mesh, mesh_config, gcode_layer); } -void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const { const SliceLayer* layer = &mesh.layers[gcode_layer.getLayerNr()]; @@ -1453,7 +1453,7 @@ void FffGcodeWriter::addMeshLayerToGCode( const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) @@ -1511,7 +1511,7 @@ void FffGcodeWriter::addMeshPartToGCode( const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) const { @@ -1553,7 +1553,7 @@ bool FffGcodeWriter::processInfill( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) @@ -1570,7 +1570,7 @@ bool FffGcodeWriter::processMultiLayerInfill( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) @@ -1707,7 +1707,7 @@ bool FffGcodeWriter::processSingleLayerInfill( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const { if (extruder_nr != mesh.settings.get("infill_extruder_nr").extruder_nr) @@ -2171,7 +2171,7 @@ bool FffGcodeWriter::partitionInfillBySkinAbove( void FffGcodeWriter::processSpiralizedWall( const SliceDataStorage& storage, LayerPlan& gcode_layer, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const { @@ -2209,7 +2209,7 @@ bool FffGcodeWriter::processInsets( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const { bool added_something = false; @@ -2454,7 +2454,7 @@ bool FffGcodeWriter::processSkin( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const { const size_t top_bottom_extruder_nr = mesh.settings.get("top_bottom_extruder_nr").extruder_nr; @@ -2489,7 +2489,7 @@ bool FffGcodeWriter::processSkinPart( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SkinPart& skin_part) const { bool added_something = false; @@ -2508,7 +2508,7 @@ void FffGcodeWriter::processRoofing( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const { @@ -2549,7 +2549,7 @@ void FffGcodeWriter::processTopBottom( LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const { @@ -2730,7 +2730,7 @@ void FffGcodeWriter::processSkinPrintFeature( const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, - const PathConfigStorage::MeshPathConfigs& mesh_config, + const MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, diff --git a/src/GCodePathConfig.cpp b/src/GCodePathConfig.cpp index 0b2ff5a972..a2479d0a65 100644 --- a/src/GCodePathConfig.cpp +++ b/src/GCodePathConfig.cpp @@ -9,49 +9,6 @@ namespace cura { -GCodePathConfig::GCodePathConfig(const GCodePathConfig& other) - : type(other.type) - , speed_derivatives(other.speed_derivatives) - , line_width(other.line_width) - , layer_thickness(other.layer_thickness) - , flow(other.flow) - , extrusion_mm3_per_mm(other.extrusion_mm3_per_mm) - , is_bridge_path(other.is_bridge_path) - , fan_speed(other.fan_speed) -{ -} - - -GCodePathConfig::GCodePathConfig( - const PrintFeatureType& type, - const coord_t line_width, - const coord_t layer_height, - const Ratio& flow, - const SpeedDerivatives speed_derivatives, - const bool is_bridge_path, - const double fan_speed) - : type(type) - , speed_derivatives(speed_derivatives) - , line_width(line_width) - , layer_thickness(layer_height) - , flow(flow) - , extrusion_mm3_per_mm(calculateExtrusion()) - , is_bridge_path(is_bridge_path) - , fan_speed(fan_speed) -{ -} - -void GCodePathConfig::smoothSpeed(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer_nr) -{ - double max_speed_layer = max_speed_layer_nr; - double first_layer_speed = std::min(speed_derivatives.speed, first_layer_config.speed); - double first_layer_acceleration = std::min(speed_derivatives.acceleration, first_layer_config.acceleration); - double first_layer_jerk = std::min(speed_derivatives.jerk, first_layer_config.jerk); - speed_derivatives.speed = (speed_derivatives.speed * layer_nr) / max_speed_layer + (first_layer_speed * (max_speed_layer - layer_nr) / max_speed_layer); - speed_derivatives.acceleration = (speed_derivatives.acceleration * layer_nr) / max_speed_layer + (first_layer_acceleration * (max_speed_layer - layer_nr) / max_speed_layer); - speed_derivatives.jerk = (speed_derivatives.jerk * layer_nr) / max_speed_layer + (first_layer_jerk * (max_speed_layer - layer_nr) / max_speed_layer); -} - double GCodePathConfig::getExtrusionMM3perMM() const { return extrusion_mm3_per_mm; diff --git a/src/pathPlanning/SpeedDerivatives.cpp b/src/pathPlanning/SpeedDerivatives.cpp index 0b623c8766..9011f951a3 100644 --- a/src/pathPlanning/SpeedDerivatives.cpp +++ b/src/pathPlanning/SpeedDerivatives.cpp @@ -6,15 +6,16 @@ namespace cura { -constexpr void SpeedDerivatives::smoothSpeed(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer_nr) +void SpeedDerivatives::smoothSpeed(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer_nr) { - double max_speed_layer = max_speed_layer_nr; - double first_layer_speed = std::min(speed, first_layer_config.speed); - double first_layer_acceleration = std::min(acceleration, first_layer_config.acceleration); - double first_layer_jerk = std::min(jerk, first_layer_config.jerk); - speed = (speed * layer_nr) / max_speed_layer + (first_layer_speed * (max_speed_layer - layer_nr) / max_speed_layer); - acceleration = (acceleration * layer_nr) / max_speed_layer + (first_layer_acceleration * (max_speed_layer - layer_nr) / max_speed_layer); - jerk = (jerk * layer_nr) / max_speed_layer + (first_layer_jerk * (max_speed_layer - layer_nr) / max_speed_layer); + const auto max_speed_layer = static_cast(max_speed_layer_nr); + const auto first_layer_speed = std::min(speed, first_layer_config.speed); + const auto first_layer_acceleration = std::min(acceleration, first_layer_config.acceleration); + const auto first_layer_jerk = std::min(jerk, first_layer_config.jerk); + speed = (speed * static_cast(layer_nr)) / max_speed_layer + (first_layer_speed * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); + acceleration = (acceleration * static_cast(layer_nr)) / max_speed_layer + (first_layer_acceleration * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); + jerk = (jerk * static_cast(layer_nr)) / max_speed_layer + (first_layer_jerk * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); + } } // namespace cura \ No newline at end of file diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 5a190f308c..fbf3ef818e 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -6,7 +6,6 @@ #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" -#include "pathPlanning/SpeedDerivatives.h" #include "raft.h" #include "settings/EnumSettings.h" //For EPlatformAdhesion. #include "settings/Settings.h" // MAX_INFILL_COMBINE @@ -33,7 +32,7 @@ std::vector PathConfigStorage::getLineWidthFactorPerExtruder(const LayerI return ret; } -PathConfigStorage::MeshPathConfigs::MeshPathConfigs( +MeshPathConfigs::MeshPathConfigs( const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, @@ -43,19 +42,25 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("wall_0_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ mesh.settings.get("speed_wall_0"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0") }) + SpeedDerivatives{ .speed = mesh.settings.get("speed_wall_0"), + .acceleration = mesh.settings.get("acceleration_wall_0"), + .jerk = mesh.settings.get("jerk_wall_0") }) , insetX_config( PrintFeatureType::InnerWall, mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("wall_x_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ mesh.settings.get("speed_wall_x"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x") }) + SpeedDerivatives{ .speed = mesh.settings.get("speed_wall_x"), + .acceleration = mesh.settings.get("acceleration_wall_x"), + .jerk = mesh.settings.get("jerk_wall_x") }) , bridge_inset0_config( PrintFeatureType::OuterWall, mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("bridge_wall_material_flow"), - SpeedDerivatives{ mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_0"), mesh.settings.get("jerk_wall_0") }, + SpeedDerivatives{ .speed = mesh.settings.get("bridge_wall_speed"), + .acceleration = mesh.settings.get("acceleration_wall_0"), + .jerk = mesh.settings.get("jerk_wall_0") }, true // is_bridge_path , mesh.settings.get("bridge_fan_speed") * 100.0) @@ -64,7 +69,9 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("bridge_wall_material_flow"), - SpeedDerivatives{ mesh.settings.get("bridge_wall_speed"), mesh.settings.get("acceleration_wall_x"), mesh.settings.get("jerk_wall_x") }, + SpeedDerivatives{ .speed = mesh.settings.get("bridge_wall_speed"), + .acceleration = mesh.settings.get("acceleration_wall_x"), + .jerk = mesh.settings.get("jerk_wall_x") }, true // is_bridge_path , mesh.settings.get("bridge_fan_speed") * 100.0) @@ -73,17 +80,17 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("skin_material_flow") * ((layer_nr == 0) ? mesh.settings.get("skin_material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ mesh.settings.get("speed_topbottom"), - mesh.settings.get("acceleration_topbottom"), - mesh.settings.get("jerk_topbottom") }) + SpeedDerivatives{ .speed = mesh.settings.get("speed_topbottom"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }) , bridge_skin_config( // use bridge skin flow, speed and fan PrintFeatureType::Skin, mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("bridge_skin_material_flow"), - SpeedDerivatives{ mesh.settings.get("bridge_skin_speed"), - mesh.settings.get("acceleration_topbottom"), - mesh.settings.get("jerk_topbottom") }, + SpeedDerivatives{ .speed = mesh.settings.get("bridge_skin_speed"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }, true // is_bridge_path , mesh.settings.get("bridge_fan_speed") * 100.0) @@ -92,9 +99,9 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("bridge_skin_material_flow_2"), - SpeedDerivatives{ mesh.settings.get("bridge_skin_speed_2"), - mesh.settings.get("acceleration_topbottom"), - mesh.settings.get("jerk_topbottom") }, + SpeedDerivatives{ .speed = mesh.settings.get("bridge_skin_speed_2"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }, true // is_bridge_path , mesh.settings.get("bridge_fan_speed_2") * 100.0) @@ -103,9 +110,9 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("bridge_skin_material_flow_3"), - SpeedDerivatives{ mesh.settings.get("bridge_skin_speed_3"), - mesh.settings.get("acceleration_topbottom"), - mesh.settings.get("jerk_topbottom") }, + SpeedDerivatives{ .speed = mesh.settings.get("bridge_skin_speed_3"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }, true // is_bridge_path , mesh.settings.get("bridge_fan_speed_3") * 100.0) @@ -114,13 +121,17 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("roofing_line_width"), layer_thickness, mesh.settings.get("roofing_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ mesh.settings.get("speed_roofing"), mesh.settings.get("acceleration_roofing"), mesh.settings.get("jerk_roofing") }) + SpeedDerivatives{ .speed = mesh.settings.get("speed_roofing"), + .acceleration = mesh.settings.get("acceleration_roofing"), + .jerk = mesh.settings.get("jerk_roofing") }) , ironing_config( PrintFeatureType::Skin, mesh.settings.get("ironing_line_spacing"), layer_thickness, mesh.settings.get("ironing_flow"), - SpeedDerivatives{ mesh.settings.get("speed_ironing"), mesh.settings.get("acceleration_ironing"), mesh.settings.get("jerk_ironing") }) + SpeedDerivatives{ .speed = mesh.settings.get("speed_ironing"), + .acceleration = mesh.settings.get("acceleration_ironing"), + .jerk = mesh.settings.get("jerk_ironing") }) { infill_config.reserve(MAX_INFILL_COMBINE); @@ -132,7 +143,9 @@ PathConfigStorage::MeshPathConfigs::MeshPathConfigs( mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr], layer_thickness, mesh.settings.get("infill_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1), - SpeedDerivatives{ mesh.settings.get("speed_infill"), mesh.settings.get("acceleration_infill"), mesh.settings.get("jerk_infill") }); + SpeedDerivatives{ .speed = mesh.settings.get("speed_infill"), + .acceleration = mesh.settings.get("acceleration_infill"), + .jerk = mesh.settings.get("jerk_infill") }); } } @@ -152,41 +165,41 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye raft_base_train.settings.get("raft_base_line_width"), raft_base_train.settings.get("raft_base_thickness"), Ratio(1.0), - SpeedDerivatives{ raft_base_train.settings.get("raft_base_speed"), - raft_base_train.settings.get("raft_base_acceleration"), - raft_base_train.settings.get("raft_base_jerk") }) + SpeedDerivatives{ .speed = raft_base_train.settings.get("raft_base_speed"), + .acceleration = raft_base_train.settings.get("raft_base_acceleration"), + .jerk = raft_base_train.settings.get("raft_base_jerk") }) , raft_interface_config( PrintFeatureType::Support, raft_interface_train.settings.get("raft_interface_line_width"), raft_interface_train.settings.get("raft_interface_thickness"), Ratio(1.0), - SpeedDerivatives{ raft_interface_train.settings.get("raft_interface_speed"), - raft_interface_train.settings.get("raft_interface_acceleration"), - raft_interface_train.settings.get("raft_interface_jerk") }) + SpeedDerivatives{ .speed = raft_interface_train.settings.get("raft_interface_speed"), + .acceleration = raft_interface_train.settings.get("raft_interface_acceleration"), + .jerk = raft_interface_train.settings.get("raft_interface_jerk") }) , raft_surface_config( PrintFeatureType::SupportInterface, raft_surface_train.settings.get("raft_surface_line_width"), raft_surface_train.settings.get("raft_surface_thickness"), Ratio(1.0), - SpeedDerivatives{ raft_surface_train.settings.get("raft_surface_speed"), - raft_surface_train.settings.get("raft_surface_acceleration"), - raft_surface_train.settings.get("raft_surface_jerk") }) + SpeedDerivatives{ .speed = raft_surface_train.settings.get("raft_surface_speed"), + .acceleration = raft_surface_train.settings.get("raft_surface_acceleration"), + .jerk = raft_surface_train.settings.get("raft_surface_jerk") }) , support_roof_config( PrintFeatureType::SupportInterface, support_roof_train.settings.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr], layer_thickness, support_roof_train.settings.get("support_roof_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ support_roof_train.settings.get("speed_support_roof"), - support_roof_train.settings.get("acceleration_support_roof"), - support_roof_train.settings.get("jerk_support_roof") }) + SpeedDerivatives{ .speed = support_roof_train.settings.get("speed_support_roof"), + .acceleration = support_roof_train.settings.get("acceleration_support_roof"), + .jerk = support_roof_train.settings.get("jerk_support_roof") }) , support_bottom_config( PrintFeatureType::SupportInterface, support_bottom_train.settings.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr], layer_thickness, support_roof_train.settings.get("support_bottom_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ support_bottom_train.settings.get("speed_support_bottom"), - support_bottom_train.settings.get("acceleration_support_bottom"), - support_bottom_train.settings.get("jerk_support_bottom") }) + SpeedDerivatives{ .speed = support_bottom_train.settings.get("speed_support_bottom"), + .acceleration = support_bottom_train.settings.get("acceleration_support_bottom"), + .jerk = support_bottom_train.settings.get("jerk_support_bottom") }) { const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size(); travel_config_per_extruder.reserve(extruder_count); @@ -201,7 +214,9 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye 0, 0, 0.0, - SpeedDerivatives{ train.settings.get("speed_travel"), train.settings.get("acceleration_travel"), train.settings.get("jerk_travel") }); + SpeedDerivatives{ .speed = train.settings.get("speed_travel"), + .acceleration = train.settings.get("acceleration_travel"), + .jerk = train.settings.get("jerk_travel") }); skirt_brim_config_per_extruder.emplace_back( PrintFeatureType::SkirtBrim, train.settings.get("skirt_brim_line_width") @@ -211,18 +226,18 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye , layer_thickness, train.settings.get("skirt_brim_material_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ train.settings.get("skirt_brim_speed"), - train.settings.get("acceleration_skirt_brim"), - train.settings.get("jerk_skirt_brim") }); + SpeedDerivatives{ .speed = train.settings.get("skirt_brim_speed"), + .acceleration = train.settings.get("acceleration_skirt_brim"), + .jerk = train.settings.get("jerk_skirt_brim") }); prime_tower_config_per_extruder.emplace_back( PrintFeatureType::PrimeTower, train.settings.get("prime_tower_line_width") * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]), layer_thickness, train.settings.get("prime_tower_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ train.settings.get("speed_prime_tower"), - train.settings.get("acceleration_prime_tower"), - train.settings.get("jerk_prime_tower") }); + SpeedDerivatives{ .speed = train.settings.get("speed_prime_tower"), + .acceleration = train.settings.get("acceleration_prime_tower"), + .jerk = train.settings.get("jerk_prime_tower") }); } mesh_configs.reserve(storage.meshes.size()); @@ -242,9 +257,9 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye layer_thickness, support_infill_train.settings.get("support_material_flow") * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1), - SpeedDerivatives{ support_infill_train.settings.get("speed_support_infill"), - support_infill_train.settings.get("acceleration_support_infill"), - support_infill_train.settings.get("jerk_support_infill") }); + SpeedDerivatives{ .speed = support_infill_train.settings.get("speed_support_infill"), + .acceleration = support_infill_train.settings.get("acceleration_support_infill"), + .jerk = support_infill_train.settings.get("jerk_support_infill") }); } const size_t initial_speedup_layer_count = mesh_group_settings.get("speed_slowdown_layers"); @@ -254,28 +269,28 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } } -void PathConfigStorage::MeshPathConfigs::smoothAllSpeeds(SpeedDerivatives first_layer_config, const LayerIndex& layer_nr, const LayerIndex& max_speed_layer) +void MeshPathConfigs::smoothAllSpeeds(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer) { - inset0_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); - insetX_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); - skin_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); - ironing_config.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + inset0_config.speed_derivatives.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + insetX_config.speed_derivatives.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + skin_config.speed_derivatives.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + ironing_config.speed_derivatives.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); for (size_t idx = 0; idx < MAX_INFILL_COMBINE; idx++) { // Infill speed (per combine part per mesh). - infill_config[idx].smoothSpeed(first_layer_config, layer_nr, max_speed_layer); + infill_config[idx].speed_derivatives.smoothSpeed(first_layer_config, layer_nr, max_speed_layer); } } -void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storage, const LayerIndex& layer_nr, const size_t initial_speedup_layer_count) +void PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storage, const LayerIndex& layer_nr, const size_t initial_speedup_layer_count) { std::vector global_first_layer_config_per_extruder; global_first_layer_config_per_extruder.reserve(Application::getInstance().current_slice->scene.extruders.size()); for (const ExtruderTrain& extruder : Application::getInstance().current_slice->scene.extruders) { - global_first_layer_config_per_extruder.emplace_back(SpeedDerivatives{ extruder.settings.get("speed_print_layer_0"), - extruder.settings.get("acceleration_print_layer_0"), - extruder.settings.get("jerk_print_layer_0") }); + global_first_layer_config_per_extruder.emplace_back(SpeedDerivatives{ .speed = extruder.settings.get("speed_print_layer_0"), + .acceleration = extruder.settings.get("acceleration_print_layer_0"), + .jerk = extruder.settings.get("jerk_print_layer_0") }); } { // support @@ -284,18 +299,23 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const size_t extruder_nr_support_infill = mesh_group_settings.get((layer_nr <= 0) ? "support_extruder_nr_layer_0" : "support_infill_extruder_nr").extruder_nr; - SpeedDerivatives& first_layer_config_infill = global_first_layer_config_per_extruder[extruder_nr_support_infill]; for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++) { - support_infill_config[idx].smoothSpeed(first_layer_config_infill, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); + support_infill_config[idx].speed_derivatives.smoothSpeed( + global_first_layer_config_per_extruder[extruder_nr_support_infill], + std::max(LayerIndex(0), layer_nr), + initial_speedup_layer_count); } - const size_t extruder_nr_support_roof = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; - SpeedDerivatives& first_layer_config_roof = global_first_layer_config_per_extruder[extruder_nr_support_roof]; - support_roof_config.smoothSpeed(first_layer_config_roof, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); + support_roof_config.speed_derivatives.smoothSpeed( + global_first_layer_config_per_extruder[extruder_nr_support_roof], + std::max(LayerIndex(0), layer_nr), + initial_speedup_layer_count); const size_t extruder_nr_support_bottom = mesh_group_settings.get("support_bottom_extruder_nr").extruder_nr; - SpeedDerivatives& first_layer_config_bottom = global_first_layer_config_per_extruder[extruder_nr_support_bottom]; - support_bottom_config.smoothSpeed(first_layer_config_bottom, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); + support_bottom_config.speed_derivatives.smoothSpeed( + global_first_layer_config_per_extruder[extruder_nr_support_bottom], + std::max(LayerIndex(0), layer_nr), + initial_speedup_layer_count); } } @@ -303,20 +323,18 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; - SpeedDerivatives initial_layer_travel_speed_config{ train.settings.get("speed_travel_layer_0"), - train.settings.get("acceleration_travel_layer_0"), - train.settings.get("jerk_travel_layer_0") }; + const SpeedDerivatives initial_layer_travel_speed_config{ .speed = train.settings.get("speed_travel_layer_0"), + .acceleration = train.settings.get("acceleration_travel_layer_0"), + .jerk = train.settings.get("jerk_travel_layer_0") }; GCodePathConfig& travel = travel_config_per_extruder[extruder_nr]; - travel.smoothSpeed(initial_layer_travel_speed_config, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); + travel.speed_derivatives.smoothSpeed(initial_layer_travel_speed_config, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); // don't smooth speed for the skirt/brim! // NOTE: not smoothing skirt/brim means the speeds are also not smoothed for the draft/ooze shield - const SpeedDerivatives& initial_layer_print_speed_config = global_first_layer_config_per_extruder[extruder_nr]; - GCodePathConfig& prime_tower = prime_tower_config_per_extruder[extruder_nr]; - prime_tower.smoothSpeed(initial_layer_print_speed_config, std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); + prime_tower.speed_derivatives.smoothSpeed(global_first_layer_config_per_extruder[extruder_nr], std::max(LayerIndex(0), layer_nr), initial_speedup_layer_count); } } @@ -325,12 +343,12 @@ void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& { const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; - SpeedDerivatives initial_layer_speed_config{ mesh.settings.get("speed_print_layer_0"), - mesh.settings.get("acceleration_print_layer_0"), - mesh.settings.get("jerk_print_layer_0") }; + const SpeedDerivatives initial_layer_speed_config{ .speed = mesh.settings.get("speed_print_layer_0"), + .acceleration = mesh.settings.get("acceleration_print_layer_0"), + .jerk = mesh.settings.get("jerk_print_layer_0") }; mesh_configs[mesh_idx].smoothAllSpeeds(initial_layer_speed_config, layer_nr, initial_speedup_layer_count); - mesh_configs[mesh_idx].roofing_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count); + mesh_configs[mesh_idx].roofing_config.speed_derivatives.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count); } } } From d82063fd0c266c73397e1d1d777573be142321a1 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 16 Aug 2023 16:22:04 +0000 Subject: [PATCH 444/656] Applied clang-format. --- include/FffGcodeWriter.h | 219 ++++++++++++++++++-------- include/GCodePathConfig.h | 16 +- include/plugins/converters.h | 2 +- include/settings/PathConfigStorage.h | 2 - src/FffGcodeWriter.cpp | 7 +- src/pathPlanning/SpeedDerivatives.cpp | 4 +- src/settings/PathConfigStorage.cpp | 6 +- 7 files changed, 163 insertions(+), 93 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 82e28c447c..1c67fea697 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -4,17 +4,17 @@ #ifndef GCODE_WRITER_H #define GCODE_WRITER_H -#include -#include - #include "FanSpeedLayerTime.h" -#include "gcodeExport.h" #include "LayerPlanBuffer.h" +#include "gcodeExport.h" #include "settings/PathConfigStorage.h" //For the MeshPathConfigs subclass. #include "utils/ExtrusionLine.h" //Processing variable-width paths. #include "utils/NoCopy.h" -namespace cura +#include +#include + +namespace cura { class AngleDegrees; @@ -28,28 +28,28 @@ class TimeKeeper; /*! * Secondary stage in Fused Filament Fabrication processing: The generated polygons are used in the gcode generation. - * Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines, + * Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines, * while other polygons signify the contours which should be printed. - * + * * The main function of this class is FffGcodeWriter::writeGCode(). */ class FffGcodeWriter : public NoCopy { - friend class FffProcessor; //Because FffProcessor exposes finalize (TODO) + friend class FffProcessor; // Because FffProcessor exposes finalize (TODO) private: coord_t max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print. /* * Buffer for all layer plans (of type LayerPlan) - * + * * The layer plans are buffered so that we can start heating up a nozzle several layers before it needs to be used. * Another reason is to perform Auto Temperature. */ - LayerPlanBuffer layer_plan_buffer; + LayerPlanBuffer layer_plan_buffer; /*! * The class holding the current state of the gcode being written. - * + * * It holds information such as the last written position etc. */ GCodeExport gcode; @@ -104,18 +104,18 @@ class FffGcodeWriter : public NoCopy /*! * Set the target to write gcode to: an output stream. - * + * * Used when CuraEngine is NOT used as command line tool. - * + * * \param stream The stream to write gcode to. */ void setTargetStream(std::ostream* stream); /*! * Get the total extruded volume for a specific extruder in mm^3 - * + * * Retractions and unretractions don't contribute to this. - * + * * \param extruder_nr The extruder number for which to get the total netto extruded volume * \return total filament printed in mm^3 */ @@ -123,7 +123,7 @@ class FffGcodeWriter : public NoCopy /*! * Get the total estimated print time in seconds for each feature - * + * * \return total print time in seconds for each feature */ std::vector getTotalPrintTimePerFeature(); @@ -131,7 +131,7 @@ class FffGcodeWriter : public NoCopy /*! * Write all the gcode for the current meshgroup. * This is the primary function of this class. - * + * * \param[in] storage The data storage from which to get the polygons to print and the areas to fill. * \param timeKeeper The stop watch to see how long it takes for each of the stages in the slicing process. */ @@ -146,28 +146,28 @@ class FffGcodeWriter : public NoCopy /*! * Set the retraction and wipe config globally, per extruder and per mesh. - * + * * \param[out] storage The data storage to which to save the configurations */ void setConfigRetractionAndWipe(SliceDataStorage& storage); /*! * Get the extruder with which to start the print. - * + * * Generally this is the extruder of the adhesion type in use, but in case * the platform adhesion type is none, the support extruder is used. If * support is also disabled, the extruder with lowest number which is used * on the first layer is used as initial extruder. - * + * * \param[in] storage where to get settings from. */ size_t getStartExtruder(const SliceDataStorage& storage); /*! * Set the infill angles and skin angles in the SliceDataStorage. - * + * * These lists of angles are cycled through to get the infill angle of a specific layer. - * + * * \param mesh The mesh for which to determine the infill and skin angles. */ void setInfillAndSkinAngles(SliceMeshStorage& mesh); @@ -186,24 +186,24 @@ class FffGcodeWriter : public NoCopy /*! * Move up and over the already printed meshgroups to print the next meshgroup. - * + * * \param[in] storage where the slice data is stored. */ void processNextMeshGroupCode(const SliceDataStorage& storage); - + /*! * Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer - * + * * \param[in,out] storage where the slice data is stored. */ void processRaft(const SliceDataStorage& storage); /*! * Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer - * + * * In case of negative layer numbers, create layers only containing the data from * the helper parts (support etc) to fill up the gap between the raft and the model. - * + * * \param[in] storage where the slice data is stored. * \param layer_nr The index of the layer to write the gcode of. * \param total_layers The total number of layers. @@ -217,17 +217,17 @@ class FffGcodeWriter : public NoCopy * * Technically, this function checks whether any extruder needs to be primed (with a prime blob) * separately just before they are used. - * + * * \return whether any extruder need to be primed separately just before they are used */ bool getExtruderNeedPrimeBlobDuringFirstLayer(const SliceDataStorage& storage, const size_t extruder_nr) const; /*! * Add the skirt or the brim to the layer plan \p gcodeLayer if it hasn't already been added yet. - * + * * This function should be called for only one layer; * calling it for multiple layers results in the skirt/brim being printed on multiple layers. - * + * * \param storage where the slice data is stored. * \param gcodeLayer The initial planning of the g-code of the layer. * \param extruder_nr The extruder train for which to process the skirt or @@ -238,15 +238,15 @@ class FffGcodeWriter : public NoCopy /*! * Adds the ooze shield to the layer plan \p gcodeLayer. - * + * * \param[in] storage where the slice data is stored. * \param gcodeLayer The initial planning of the gcode of the layer. */ void processOozeShield(const SliceDataStorage& storage, LayerPlan& gcodeLayer) const; - + /*! * Adds the draft protection screen to the layer plan \p gcodeLayer. - * + * * \param[in] storage where the slice data is stored. * \param gcodeLayer The initial planning of the gcode of the layer. */ @@ -256,14 +256,14 @@ class FffGcodeWriter : public NoCopy * Calculate in which order to plan the extruders for each layer * Store the order of extruders for each layer in extruder_order_per_layer for normal layers * and the order of extruders for raft/filler layers in extruder_order_per_layer_negative_layers. - * + * * Only extruders which are (most probably) going to be used are planned - * + * * \note At the planning stage we only have information on areas, not how those are filled. * If an area is too small to be filled with anything it will still get specified as being used with the extruder for that area. - * + * * Computes \ref FffGcodeWriter::extruder_order_per_layer and \ref FffGcodeWriter::extruder_order_per_layer_negative_layers - * + * * \param[in] storage where the slice data is stored. */ void calculateExtruderOrderPerLayer(const SliceDataStorage& storage); @@ -280,10 +280,10 @@ class FffGcodeWriter : public NoCopy /*! * Gets a list of extruders that are used on the given layer, but excluding the given starting extruder. * When it's on the first layer, the prime blob will also be taken into account. - * + * * \note At the planning stage we only have information on areas, not how those are filled. * If an area is too small to be filled with anything it will still get specified as being used with the extruder for that area. - * + * * \param[in] storage where the slice data is stored. * \param current_extruder The current extruder with which we last printed * \return The order of extruders for a layer beginning with \p current_extruder @@ -294,7 +294,7 @@ class FffGcodeWriter : public NoCopy * Calculate in which order to plan the meshes of a specific extruder * Each mesh which has some feature printed with the extruder is included in this order. * One mesh can occur in the mesh order of multiple extruders. - * + * * \param[in] storage where the slice data is stored. * \param extruder_nr The extruder for which to determine the order * \return A vector of mesh indices ordered on print order for that extruder. @@ -303,41 +303,42 @@ class FffGcodeWriter : public NoCopy /*! * Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode. - * + * * \param[in] storage where the slice data is stored. * \param mesh The mesh to add to the layer plan \p gcodeLayer. * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcodeLayer) const; - + /*! * Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes. - * + * * \param[in] storage where the slice data is stored. * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ void addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; - + /*! * Add all features of a given extruder from a single layer from a single mesh-volume to the layer plan \p gcode_layer. - * + * * This adds all features (e.g. walls, skin etc.) of this \p mesh to the gcode which are printed using \p extruder_nr - * + * * \param[in] storage where the slice data is stored. * \param mesh The mesh to add to the layer plan \p gcode_layer. * \param extruder_nr The extruder for which to print all features of the mesh which should be printed with this extruder * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; + void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) + const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. * This only adds the features which are printed with \p extruder_nr. - * + * * \param[in] storage where the slice data is stored. * \param storage Storage to get global settings from. * \param mesh The mesh to add to the layer plan \p gcode_layer. @@ -346,7 +347,13 @@ class FffGcodeWriter : public NoCopy * \param part The part to add * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) const; + void addMeshPartToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + LayerPlan& gcode_layer) const; /*! * \brief Add infill for a given part in a layer plan. @@ -359,12 +366,18 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processInfill( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * \brief Add thicker (multiple layers) sparse infill for a given part in a * layer plan. - * + * * \param gcodeLayer The initial planning of the gcode of the layer. * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. * \param extruder_nr The extruder for which to print all features of the @@ -373,7 +386,13 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processMultiLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * \brief Add normal sparse infill for a given part in a layer. @@ -385,7 +404,13 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processSingleLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processSingleLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * Generate the insets for the walls of a given layer part. @@ -397,7 +422,13 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processInsets(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processInsets( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * Generate the a spiralized wall for a given layer part. @@ -407,7 +438,9 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. */ - void processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const; + void + processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) + const; /*! * Add the gcode of the top/bottom skin of the given part and of the perimeter gaps. @@ -420,21 +453,27 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processSkin( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * Add the gcode of the top/bottom skin of the given skin part and of the perimeter gaps. - * + * * Perimeter gaps are handled for the current extruder for the following features if they are printed with this extruder. * - skin outlines * - roofing (if concentric) * - top/bottom (if concentric) * They are all printed at the end of printing the skin part features which are printed with this extruder. - * + * * Note that the normal perimeter gaps are printed with the outer wall extruder, * while newly generated perimeter gaps * are printed with the extruder with which the feature was printed which generated the gaps. - * + * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. * \param mesh The mesh for which to add to the layer plan \p gcode_layer. @@ -443,7 +482,13 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SkinPart& skin_part) const; + bool processSkinPart( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SkinPart& skin_part) const; /*! * Add the roofing which is the area inside the innermost skin inset which has air 'directly' above @@ -456,7 +501,14 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processRoofing(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; + void processRoofing( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const; /*! * Add the normal skinfill which is the area inside the innermost skin inset @@ -470,11 +522,18 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processTopBottom(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; + void processTopBottom( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const; /*! * Process a dense skin feature like roofing or top/bottom - * + * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. * \param mesh The mesh for which to add to the layer plan \p gcode_layer. @@ -491,7 +550,21 @@ class FffGcodeWriter : public NoCopy * \param[out] added_something Whether this function added anything to the layer plan * \param fan_speed fan speed override for this skin area */ - void processSkinPrintFeature(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, EFillMethod pattern, const AngleDegrees skin_angle, const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; + void processSkinPrintFeature( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const MeshPathConfigs& mesh_config, + const size_t extruder_nr, + const Polygons& area, + const GCodePathConfig& config, + EFillMethod pattern, + const AngleDegrees skin_angle, + const coord_t skin_overlap, + const Ratio skin_density, + const bool monotonic, + bool& added_something, + double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to @@ -510,7 +583,7 @@ class FffGcodeWriter : public NoCopy * +------+ +------+ * 1, 2 = start locations of skin segments * # = seam - * + * * \param filling_part The part which we are going to fill with a linear filling type * \param filling_angle The angle of the filling lines * \param last_position The position the print head is in before going to fill the part @@ -573,9 +646,9 @@ class FffGcodeWriter : public NoCopy /*! * Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last. - * + * * On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower. - * + * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. * \param extruder_nr The extruder to switch to. @@ -589,7 +662,7 @@ class FffGcodeWriter : public NoCopy * \param prev_extruder The current extruder with which we last printed. */ void addPrimeTower(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const size_t prev_extruder) const; - + /*! * Add the end gcode and set all temperatures to zero. */ @@ -628,9 +701,15 @@ class FffGcodeWriter : public NoCopy * \param infill_line_width line width of the infill * \return true if there needs to be a skin edge support wall in this layer, otherwise false */ - static bool 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) ; + static bool 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); }; -}//namespace cura +} // namespace cura #endif // GCODE_WRITER_H diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 9bf8002883..5fbde5e3c6 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -19,14 +19,14 @@ namespace cura */ struct GCodePathConfig { - PrintFeatureType type {}; //!< name of the feature type - coord_t line_width {}; //!< width of the line extruded - coord_t layer_thickness {}; //!< current layer height in micron - Ratio flow {}; //!< extrusion flow modifier. - SpeedDerivatives speed_derivatives {}; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called. - bool is_bridge_path { false }; //!< whether current config is used when bridging - double fan_speed { FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise - double extrusion_mm3_per_mm { calculateExtrusion() }; //!< current mm^3 filament moved per mm line traversed + PrintFeatureType type{}; //!< name of the feature type + coord_t line_width{}; //!< width of the line extruded + coord_t layer_thickness{}; //!< current layer height in micron + Ratio flow{}; //!< extrusion flow modifier. + SpeedDerivatives speed_derivatives{}; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called. + bool is_bridge_path{ false }; //!< whether current config is used when bridging + double fan_speed{ FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise + double extrusion_mm3_per_mm{ calculateExtrusion() }; //!< current mm^3 filament moved per mm line traversed static constexpr double FAN_SPEED_DEFAULT = -1; /*! diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 6219cbe502..788bbab782 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -19,12 +19,12 @@ #include "cura/plugins/slots/simplify/v0/modify.grpc.pb.h" #include "cura/plugins/slots/simplify/v0/modify.pb.h" #include "pathPlanning/GCodePath.h" +#include "pathPlanning/SpeedDerivatives.h" #include "plugins/metadata.h" #include "plugins/types.h" #include "settings/Settings.h" #include "settings/types/LayerIndex.h" #include "utils/polygon.h" -#include "pathPlanning/SpeedDerivatives.h" #include #include diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index ea44eba320..b0279cbab4 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -57,8 +57,6 @@ class PathConfigStorage static std::vector getLineWidthFactorPerExtruder(const LayerIndex& layer_nr); public: - - GCodePathConfig raft_base_config; GCodePathConfig raft_interface_config; GCodePathConfig raft_surface_config; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index a9835f852e..c4fb4d8cb8 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1404,11 +1404,8 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s return ret; } -void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode( - const SliceDataStorage& storage, - const SliceMeshStorage& mesh, - const MeshPathConfigs& mesh_config, - LayerPlan& gcode_layer) const +void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) + const { if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { diff --git a/src/pathPlanning/SpeedDerivatives.cpp b/src/pathPlanning/SpeedDerivatives.cpp index 9011f951a3..c2ca78c8cc 100644 --- a/src/pathPlanning/SpeedDerivatives.cpp +++ b/src/pathPlanning/SpeedDerivatives.cpp @@ -13,9 +13,9 @@ void SpeedDerivatives::smoothSpeed(const SpeedDerivatives& first_layer_config, c const auto first_layer_acceleration = std::min(acceleration, first_layer_config.acceleration); const auto first_layer_jerk = std::min(jerk, first_layer_config.jerk); speed = (speed * static_cast(layer_nr)) / max_speed_layer + (first_layer_speed * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); - acceleration = (acceleration * static_cast(layer_nr)) / max_speed_layer + (first_layer_acceleration * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); + acceleration + = (acceleration * static_cast(layer_nr)) / max_speed_layer + (first_layer_acceleration * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); jerk = (jerk * static_cast(layer_nr)) / max_speed_layer + (first_layer_jerk * (max_speed_layer - static_cast(layer_nr)) / max_speed_layer); - } } // namespace cura \ No newline at end of file diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index fbf3ef818e..9d253f83b4 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -32,11 +32,7 @@ std::vector PathConfigStorage::getLineWidthFactorPerExtruder(const LayerI return ret; } -MeshPathConfigs::MeshPathConfigs( - const SliceMeshStorage& mesh, - const coord_t layer_thickness, - const LayerIndex& layer_nr, - const std::vector& line_width_factor_per_extruder) +MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder) : inset0_config( PrintFeatureType::OuterWall, mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], From 730f89de2e4340a435aac0908ad7c43472464c9a Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 16 Aug 2023 22:30:08 +0000 Subject: [PATCH 445/656] Applied clang-format. --- include/infill.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/infill.h b/include/infill.h index bdeec89efd..704735e95d 100644 --- a/include/infill.h +++ b/include/infill.h @@ -10,7 +10,6 @@ #include "settings/Settings.h" #include "settings/types/Angle.h" #include "utils/AABB.h" -#include "settings/types/Angle.h" #include "utils/ExtrusionLine.h" #include "utils/IntPoint.h" #include "utils/section_type.h" From 3ac71c0280226b9ec7cf18219af0b1dbe40ef42d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sat, 19 Aug 2023 20:37:44 +0200 Subject: [PATCH 446/656] Add conditional compilation for coroutines support This commit updates the file `pluginproxy.h` to include support for both standard and experimental versions of coroutine. The preprocessor directives check if the standard coroutine header is available, if not it checks for the availability of the experimental version. In the latter's case, a flag `USE_EXPERIMENTAL_COROUTINE` is set. This change ensures compatibility across different compiler versions. Contribute to CURA-10475 --- include/plugins/pluginproxy.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index d5340db80e..c457b935d9 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -31,7 +31,12 @@ #include #include +#if __has_include() #include +#elif __has_include() +#include +#define USE_EXPERIMENTAL_COROUTINE +#endif #include #include #include From ef687fe2064d4a7f2ad7874ccf92542367a42a9a Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Sun, 20 Aug 2023 23:40:13 +0200 Subject: [PATCH 447/656] Update header --- include/sliceDataStorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 0d20a90f14..cad566d070 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SLICE_DATA_STORAGE_H From 4a8c9bcc9d893a3e8937a2cc1a6d2bfe89bdc354 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 21 Aug 2023 17:39:55 +0200 Subject: [PATCH 448/656] Refactor GCodePath to use smart pointers for mesh ownership This change migrates GCodePath from using raw pointers to shared pointers for referencing the mesh. This avoids potential raw pointer dangling and null dereference issues. Simultaneously, assertion statement within VoronoiUtils.cpp was commented out to prevent possible failures. Some styling and permissions were also updated across various files for consistency and to meet C++ guidelines. For instance, marking certain functions as [[nodiscard]] and noexcept. Lastly, Copyright headers for GCodePath were updated to reflect the current year and company name. Other files includes updating the header order and deleting unnecessary class declarations. Overall, the commit enhances memory safety and improves the readability and consistency of the codebase. Contributes to CURA-10446 --- include/FffGcodeWriter.h | 2 +- include/GCodePathConfig.h | 28 ++++----- include/LayerPlan.h | 21 +++---- include/TopSurface.h | 3 +- include/pathPlanning/GCodePath.h | 75 +++++++++---------------- include/pathPlanning/SpeedDerivatives.h | 2 + include/settings/PathConfigStorage.h | 20 +------ src/FffGcodeWriter.cpp | 3 +- src/GCodePathConfig.cpp | 25 ++++----- src/LayerPlan.cpp | 4 +- src/pathPlanning/GCodePath.cpp | 47 +++------------- src/plugins/converters.cpp | 2 +- src/utils/VoronoiUtils.cpp | 2 +- 13 files changed, 85 insertions(+), 149 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 1c67fea697..db9664e588 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -7,7 +7,7 @@ #include "FanSpeedLayerTime.h" #include "LayerPlanBuffer.h" #include "gcodeExport.h" -#include "settings/PathConfigStorage.h" //For the MeshPathConfigs subclass. +#include "settings/MeshPathConfigs.h" #include "utils/ExtrusionLine.h" //Processing variable-width paths. #include "utils/NoCopy.h" diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 5fbde5e3c6..1ee2003195 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -6,7 +6,6 @@ #include "PrintFeature.h" #include "pathPlanning/SpeedDerivatives.h" -#include "settings/types/LayerIndex.h" #include "settings/types/Ratio.h" #include "settings/types/Velocity.h" #include "utils/Coord_t.h" @@ -29,42 +28,45 @@ struct GCodePathConfig double extrusion_mm3_per_mm{ calculateExtrusion() }; //!< current mm^3 filament moved per mm line traversed static constexpr double FAN_SPEED_DEFAULT = -1; + [[nodiscard]] constexpr bool operator==(const GCodePathConfig& other) const noexcept = default; + [[nodiscard]] constexpr auto operator<=>(const GCodePathConfig& other) const = default; + /*! * Can only be called after the layer height has been set (which is done while writing the gcode!) */ - double getExtrusionMM3perMM() const; + [[nodiscard]] double getExtrusionMM3perMM() const noexcept; /*! * Get the movement speed in mm/s */ - Velocity getSpeed() const; + [[nodiscard]] Velocity getSpeed() const noexcept; /*! * Get the current acceleration of this config */ - Acceleration getAcceleration() const; + [[nodiscard]] Acceleration getAcceleration() const noexcept; /*! * Get the current jerk of this config */ - Velocity getJerk() const; + [[nodiscard]] Velocity getJerk() const noexcept; - coord_t getLineWidth() const; + [[nodiscard]] coord_t getLineWidth() const noexcept; - bool isTravelPath() const; + [[nodiscard]] bool isTravelPath() const noexcept; - bool isBridgePath() const; + [[nodiscard]] bool isBridgePath() const noexcept; - double getFanSpeed() const; + [[nodiscard]] double getFanSpeed() const noexcept; - Ratio getFlowRatio() const; + [[nodiscard]] Ratio getFlowRatio() const noexcept; - coord_t getLayerThickness() const; + [[nodiscard]] coord_t getLayerThickness() const noexcept; - const PrintFeatureType& getPrintFeatureType() const; + [[nodiscard]] PrintFeatureType getPrintFeatureType() const noexcept; private: - double calculateExtrusion() const; + [[nodiscard]] double calculateExtrusion() const noexcept; }; diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 64da17faa1..a6bf59f303 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -4,14 +4,6 @@ #ifndef LAYER_PLAN_H #define LAYER_PLAN_H -#include -#include -#include -#include -#ifdef BUILD_TESTS -#include //Friend tests, so that they can inspect the privates. -#endif - #include "FanSpeedLayerTime.h" #include "InsetOrderOptimizer.h" #include "PathOrderOptimizer.h" @@ -25,6 +17,15 @@ #include "utils/ExtrusionJunction.h" #include "utils/polygon.h" +#include +#include +#include +#include +#include +#ifdef BUILD_TESTS +#include //Friend tests, so that they can inspect the privates. +#endif + namespace cura { @@ -245,7 +246,7 @@ class LayerPlan : public NoCopy std::vector has_prime_tower_planned_per_extruder; //!< For each extruder, whether the prime tower is planned yet or not. std::optional last_planned_position; //!< The last planned XY position of the print head (if known) - const SliceMeshStorage* current_mesh; //!< The mesh of the last planned move. + std::shared_ptr current_mesh; //!< The mesh of the last planned move. /*! * Whether the skirt or brim polygons have been processed into planned paths @@ -413,7 +414,7 @@ class LayerPlan : public NoCopy * Track the currently printing mesh. * \param mesh_id A unique ID indicating the current mesh. */ - void setMesh(const SliceMeshStorage* mesh_id); + void setMesh(const std::shared_ptr &mesh); /*! * Set bridge_wall_mask. diff --git a/include/TopSurface.h b/include/TopSurface.h index bfebc3f3a9..16fbef18a3 100644 --- a/include/TopSurface.h +++ b/include/TopSurface.h @@ -4,12 +4,13 @@ #ifndef TOPSURFACE_H #define TOPSURFACE_H +#include "GCodePathConfig.h" + #include "utils/polygon.h" //For the polygon areas. namespace cura { -class GCodePathConfig; class FffGcodeWriter; class LayerPlan; class SliceMeshStorage; diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 412f7b49b6..1244dc63c9 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -11,6 +11,9 @@ #include "sliceDataStorage.h" #include "utils/IntPoint.h" +#include +#include + namespace cura { @@ -24,60 +27,34 @@ namespace cura * In the final representation (gcode) each line segment may have different properties, * which are added when the generated GCodePaths are processed. */ -class GCodePath +struct GCodePath { -public: - const GCodePathConfig config; //!< The configuration settings of the path. - const SliceMeshStorage* mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; - SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part - Ratio flow; //!< A type-independent flow configuration - Ratio width_factor; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. - Ratio speed_factor; //!< A speed factor that is multiplied with the travel speed. This factor can be used to change the travel speed. - Ratio speed_back_pressure_factor; // mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; + SpaceFillType space_fill_type{}; //!< The type of space filling of which this path is a part + Ratio flow{}; //!< A type-independent flow configuration + Ratio width_factor{}; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. + bool spiralize{}; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and + //!< end in one layer higher. + Ratio speed_factor{1.0}; //!< A speed factor that is multiplied with the travel speed. This factor can be used to change the travel speed. + Ratio speed_back_pressure_factor{1.0}; // points; //!< The points constituting this path. - bool done; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. - - bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and - //!< end in one layer higher. - - double fan_speed; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise - - TimeMaterialEstimates estimates; //!< Naive time and material estimates - - /*! - * \brief Creates a new g-code path. - * - * \param config The line configuration to use when printing this path. - * \param mesh_id The mesh that this path is part of. - * \param space_fill_type The type of space filling of which this path is a - * part. - * \param flow The flow rate to print this path with. - * \param width_factor A multiplier on the line width. - * \param spiralize Gradually increment the z-coordinate while traversing - * \param speed_factor The factor that the travel speed will be multiplied with - * this path. - */ - GCodePath( - const GCodePathConfig config, - const SliceMeshStorage* mesh_id, - const SpaceFillType space_fill_type, - const Ratio flow, - const Ratio width_factor, - const bool spiralize, - const Ratio speed_factor = 1.0); + bool done{false}; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. + double fan_speed{GCodePathConfig::FAN_SPEED_DEFAULT}; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise + TimeMaterialEstimates estimates{}; //!< Naive time and material estimates /*! * Whether this config is the config of a travel path. * * \return Whether this config is the config of a travel path. */ - bool isTravelPath() const; + [[nodiscard]] bool isTravelPath() const noexcept; /*! * Get the material flow in mm^3 per mm traversed. @@ -86,26 +63,26 @@ class GCodePath * * \return The flow */ - double getExtrusionMM3perMM() const; + [[nodiscard]] double getExtrusionMM3perMM() const noexcept; /*! * Get the actual line width (modulated by the flow) * \return the actual line width as shown in layer view */ - coord_t getLineWidthForLayerView() const; + [[nodiscard]] coord_t getLineWidthForLayerView() const noexcept; /*! * Set fan_speed * * \param fan_speed the fan speed to use for this path */ - void setFanSpeed(double fan_speed); + void setFanSpeed(const double fanspeed) noexcept; /*! * Get the fan speed for this path * \return the value of fan_speed if it is in the range 0-100, otherwise the value from the config */ - double getFanSpeed() const; + [[nodiscard]] double getFanSpeed() const noexcept; }; } // namespace cura diff --git a/include/pathPlanning/SpeedDerivatives.h b/include/pathPlanning/SpeedDerivatives.h index f22afc795b..d4f47788b7 100644 --- a/include/pathPlanning/SpeedDerivatives.h +++ b/include/pathPlanning/SpeedDerivatives.h @@ -16,6 +16,8 @@ struct SpeedDerivatives Acceleration acceleration{}; //!< acceleration of head movement (mm/s^2) Velocity jerk{}; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s) + constexpr bool operator==(const SpeedDerivatives& other) const noexcept = default; + constexpr auto operator<=>(const SpeedDerivatives& other) const noexcept = default; /*! * Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed. diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index b0279cbab4..5e53ae9b72 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -7,6 +7,7 @@ #include "GCodePathConfig.h" #include "pathPlanning/SpeedDerivatives.h" #include "settings/types/LayerIndex.h" +#include "settings/MeshPathConfigs.h" #include "utils/Coord_t.h" #include @@ -18,25 +19,6 @@ class ExtruderTrain; class SliceDataStorage; class SliceMeshStorage; -class MeshPathConfigs -{ -public: - GCodePathConfig inset0_config; - GCodePathConfig insetX_config; - GCodePathConfig bridge_inset0_config; - GCodePathConfig bridge_insetX_config; - GCodePathConfig skin_config; - GCodePathConfig bridge_skin_config; // used for first bridge layer - GCodePathConfig bridge_skin_config2; // used for second bridge layer - GCodePathConfig bridge_skin_config3; // used for third bridge layer - GCodePathConfig roofing_config; - std::vector infill_config; - GCodePathConfig ironing_config; - - MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder); - void smoothAllSpeeds(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer); -}; - /*! * A class to represent all configurations for all features types of printed lines in a meshgroup. */ diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index c4fb4d8cb8..34d2496d7f 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -28,6 +28,7 @@ #include // numeric_limits #include #include +#include #include namespace cura @@ -1470,7 +1471,7 @@ void FffGcodeWriter::addMeshLayerToGCode( return; } - gcode_layer.setMesh(&mesh); + gcode_layer.setMesh(std::make_shared(mesh)); ZSeamConfig z_seam_config; if (mesh.isPrinted()) //"normal" meshes with walls, skin, infill, etc. get the traditional part ordering based on the z-seam settings. diff --git a/src/GCodePathConfig.cpp b/src/GCodePathConfig.cpp index a2479d0a65..2aacd198ad 100644 --- a/src/GCodePathConfig.cpp +++ b/src/GCodePathConfig.cpp @@ -3,68 +3,67 @@ #include "GCodePathConfig.h" -#include "settings/types/LayerIndex.h" #include "utils/IntPoint.h" // INT2MM namespace cura { -double GCodePathConfig::getExtrusionMM3perMM() const +[[nodiscard]] double GCodePathConfig::getExtrusionMM3perMM() const noexcept { return extrusion_mm3_per_mm; } -Velocity GCodePathConfig::getSpeed() const +[[nodiscard]] Velocity GCodePathConfig::getSpeed() const noexcept { return speed_derivatives.speed; } -Acceleration GCodePathConfig::getAcceleration() const +[[nodiscard]] Acceleration GCodePathConfig::getAcceleration() const noexcept { return speed_derivatives.acceleration; } -Velocity GCodePathConfig::getJerk() const +[[nodiscard]] Velocity GCodePathConfig::getJerk() const noexcept { return speed_derivatives.jerk; } -coord_t GCodePathConfig::getLineWidth() const +[[nodiscard]] coord_t GCodePathConfig::getLineWidth() const noexcept { return line_width; } -coord_t GCodePathConfig::getLayerThickness() const +[[nodiscard]] coord_t GCodePathConfig::getLayerThickness() const noexcept { return layer_thickness; } -const PrintFeatureType& GCodePathConfig::getPrintFeatureType() const +[[nodiscard]] PrintFeatureType GCodePathConfig::getPrintFeatureType() const noexcept { return type; } -bool GCodePathConfig::isTravelPath() const +[[nodiscard]] bool GCodePathConfig::isTravelPath() const noexcept { return line_width == 0; } -bool GCodePathConfig::isBridgePath() const +[[nodiscard]] bool GCodePathConfig::isBridgePath() const noexcept { return is_bridge_path; } -double GCodePathConfig::getFanSpeed() const +[[nodiscard]] double GCodePathConfig::getFanSpeed() const noexcept { return fan_speed; } -Ratio GCodePathConfig::getFlowRatio() const +[[nodiscard]] Ratio GCodePathConfig::getFlowRatio() const noexcept { return flow; } -double GCodePathConfig::calculateExtrusion() const +[[nodiscard]] double GCodePathConfig::calculateExtrusion() const noexcept { return INT2MM(line_width) * INT2MM(layer_thickness) * double(flow); } diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 5b9c3770d3..e0f4a47645 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -339,7 +339,7 @@ bool LayerPlan::setExtruder(const size_t extruder_nr) } return true; } -void LayerPlan::setMesh(const SliceMeshStorage* mesh) +void LayerPlan::setMesh(const std::shared_ptr &mesh) { current_mesh = mesh; } @@ -1904,7 +1904,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const bool acceleration_travel_enabled = mesh_group_settings.get("acceleration_travel_enabled"); const bool jerk_enabled = mesh_group_settings.get("jerk_enabled"); const bool jerk_travel_enabled = mesh_group_settings.get("jerk_travel_enabled"); - const SliceMeshStorage* current_mesh = nullptr; + std::shared_ptr current_mesh; for (size_t extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++) { diff --git a/src/pathPlanning/GCodePath.cpp b/src/pathPlanning/GCodePath.cpp index 5710f4e18d..5e0fa03be6 100644 --- a/src/pathPlanning/GCodePath.cpp +++ b/src/pathPlanning/GCodePath.cpp @@ -1,61 +1,32 @@ -// Copyright (c) 2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "pathPlanning/GCodePath.h" -#include "GCodePathConfig.h" - namespace cura { -GCodePath::GCodePath( - const GCodePathConfig config, - const SliceMeshStorage* mesh, - const SpaceFillType space_fill_type, - const Ratio flow, - const Ratio width_factor, - const bool spiralize, - const Ratio speed_factor) - : config(config) - , mesh(mesh) - , space_fill_type(space_fill_type) - , flow(flow) - , width_factor(width_factor) - , speed_factor(speed_factor) - , speed_back_pressure_factor(1.0) - , retract(false) - , unretract_before_last_travel_move(false) - , perform_z_hop(false) - , perform_prime(false) - , skip_agressive_merge_hint(false) - , points(std::vector()) - , done(false) - , spiralize(spiralize) - , fan_speed(GCodePathConfig::FAN_SPEED_DEFAULT) - , estimates(TimeMaterialEstimates()) -{ -} -bool GCodePath::isTravelPath() const +[[nodiscard]] bool GCodePath::isTravelPath() const noexcept { return config.isTravelPath(); } -double GCodePath::getExtrusionMM3perMM() const +[[nodiscard]] double GCodePath::getExtrusionMM3perMM() const noexcept { return flow * width_factor * config.getExtrusionMM3perMM(); } -coord_t GCodePath::getLineWidthForLayerView() const +[[nodiscard]] coord_t GCodePath::getLineWidthForLayerView() const noexcept { - return flow * width_factor * config.getLineWidth() * config.getFlowRatio(); + return static_cast(flow * width_factor * static_cast(config.getLineWidth()) * config.getFlowRatio()); } -void GCodePath::setFanSpeed(double fan_speed) +void GCodePath::setFanSpeed(const double fanspeed) noexcept { - this->fan_speed = fan_speed; + fan_speed = fanspeed; } -double GCodePath::getFanSpeed() const +[[nodiscard]] double GCodePath::getFanSpeed() const noexcept { return (fan_speed >= 0 && fan_speed <= 100) ? fan_speed : config.getFanSpeed(); } diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 8fb4a91805..e7667dc741 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -447,7 +447,7 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper const bool spiralize = gcode_path_msg.spiralize(); const Ratio speed_factor = gcode_path_msg.speed_factor(); - const auto path = GCodePath(config, mesh_id, space_fill_type, flow, width_factor, spiralize, speed_factor); + const auto path = GCodePath(config, nullptr, space_fill_type, flow, width_factor, spiralize, speed_factor); GCodePath gcode_path(path); gcode_path.points = gcode_path_msg.path().path() | ranges::views::transform( diff --git a/src/utils/VoronoiUtils.cpp b/src/utils/VoronoiUtils.cpp index b260a74c3a..4b7f01799b 100644 --- a/src/utils/VoronoiUtils.cpp +++ b/src/utils/VoronoiUtils.cpp @@ -167,7 +167,7 @@ std::vector VoronoiUtils::discretizeParabola(const Point& p, const Segmen // are more than 10 microns away from the projected apex bool add_apex = (sx - px) * dir < -10 && (ex - px) * dir > 10; - assert(! (add_marking_start && add_marking_end) || add_apex); +// assert(! (add_marking_start && add_marking_end) || add_apex); if (add_marking_start && add_marking_end && ! add_apex) { RUN_ONCE(spdlog::warn("Failing to discretize parabola! Must add an apex or one of the endpoints.")); From 67152c7a4033a69ea73555bd320cc87f00adcc15 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 21 Aug 2023 15:40:30 +0000 Subject: [PATCH 449/656] Applied clang-format. --- include/LayerPlan.h | 2 +- include/TopSurface.h | 1 - include/pathPlanning/GCodePath.h | 20 ++++++++++---------- include/settings/PathConfigStorage.h | 2 +- src/FffGcodeWriter.cpp | 2 +- src/LayerPlan.cpp | 2 +- src/utils/VoronoiUtils.cpp | 13 +++++++------ 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index a6bf59f303..42b5eb8d33 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -414,7 +414,7 @@ class LayerPlan : public NoCopy * Track the currently printing mesh. * \param mesh_id A unique ID indicating the current mesh. */ - void setMesh(const std::shared_ptr &mesh); + void setMesh(const std::shared_ptr& mesh); /*! * Set bridge_wall_mask. diff --git a/include/TopSurface.h b/include/TopSurface.h index 16fbef18a3..bac3d87ef3 100644 --- a/include/TopSurface.h +++ b/include/TopSurface.h @@ -5,7 +5,6 @@ #define TOPSURFACE_H #include "GCodePathConfig.h" - #include "utils/polygon.h" //For the polygon areas. namespace cura diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 1244dc63c9..8c3c34dcfe 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -36,17 +36,17 @@ struct GCodePath Ratio width_factor{}; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. bool spiralize{}; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and //!< end in one layer higher. - Ratio speed_factor{1.0}; //!< A speed factor that is multiplied with the travel speed. This factor can be used to change the travel speed. - Ratio speed_back_pressure_factor{1.0}; // points; //!< The points constituting this path. - bool done{false}; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. - double fan_speed{GCodePathConfig::FAN_SPEED_DEFAULT}; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise + bool done{ false }; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. + double fan_speed{ GCodePathConfig::FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise TimeMaterialEstimates estimates{}; //!< Naive time and material estimates /*! diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index 5e53ae9b72..e7bd54773a 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -6,8 +6,8 @@ #include "GCodePathConfig.h" #include "pathPlanning/SpeedDerivatives.h" -#include "settings/types/LayerIndex.h" #include "settings/MeshPathConfigs.h" +#include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" #include diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 34d2496d7f..82438267cc 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -27,8 +27,8 @@ #include #include // numeric_limits #include -#include #include +#include #include namespace cura diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index e0f4a47645..58e2eda6c5 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -339,7 +339,7 @@ bool LayerPlan::setExtruder(const size_t extruder_nr) } return true; } -void LayerPlan::setMesh(const std::shared_ptr &mesh) +void LayerPlan::setMesh(const std::shared_ptr& mesh) { current_mesh = mesh; } diff --git a/src/utils/VoronoiUtils.cpp b/src/utils/VoronoiUtils.cpp index 4b7f01799b..0a134906da 100644 --- a/src/utils/VoronoiUtils.cpp +++ b/src/utils/VoronoiUtils.cpp @@ -1,15 +1,16 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include -#include - -#include - #include "utils/VoronoiUtils.h" + #include "utils/linearAlg2D.h" #include "utils/macros.h" +#include + +#include +#include + namespace cura { @@ -167,7 +168,7 @@ std::vector VoronoiUtils::discretizeParabola(const Point& p, const Segmen // are more than 10 microns away from the projected apex bool add_apex = (sx - px) * dir < -10 && (ex - px) * dir > 10; -// assert(! (add_marking_start && add_marking_end) || add_apex); + // assert(! (add_marking_start && add_marking_end) || add_apex); if (add_marking_start && add_marking_end && ! add_apex) { RUN_ONCE(spdlog::warn("Failing to discretize parabola! Must add an apex or one of the endpoints.")); From 55c5aadff6aaeaf64461ecba8498c009efb3ca53 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 22 Aug 2023 10:41:17 +0200 Subject: [PATCH 450/656] Refactor gcode_paths_modify_response operation Extracted code into helper functions to decrease the complexity of the gcode_paths_modify_response::operator() function. This will aid future modifications and improve readability. Removed unnecessary log from the process. Contributes to CURA-10446 --- include/plugins/converters.h | 3 + src/plugins/converters.cpp | 182 ++++++++++++++++++----------------- 2 files changed, 98 insertions(+), 87 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 788bbab782..2ebc7eddb5 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -118,6 +118,9 @@ struct gcode_paths_modify_request : public details::converter> { + [[nodiscard]] static constexpr PrintFeatureType getPrintFeatureType(const v0::PrintFeature feature) noexcept; + [[nodiscard]] static GCodePathConfig buildConfig(const v0::GCodePath& path); + [[nodiscard]] static constexpr SpaceFillType getSpaceFillType(const v0::SpaceFillType space_fill_type) noexcept; native_value_type operator()(const value_type& message) const; }; diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index e7667dc741..4df1b999de 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -296,7 +296,6 @@ gcode_paths_modify_request::value_type gcode_path->set_flow(path.flow); gcode_path->set_width_factor(path.width_factor); - spdlog::info("path.spiralize: {}", path.spiralize); gcode_path->set_spiralize(path.spiralize); gcode_path->set_speed_factor(path.speed_factor); @@ -369,97 +368,106 @@ gcode_paths_modify_request::value_type return message; } +[[nodiscard]] constexpr PrintFeatureType gcode_paths_modify_response::getPrintFeatureType(const v0::PrintFeature feature) noexcept +{ + switch (feature) + { + case v0::PrintFeature::NONETYPE: + return PrintFeatureType::NoneType; + case v0::PrintFeature::OUTERWALL: + return PrintFeatureType::OuterWall; + case v0::PrintFeature::INNERWALL: + return PrintFeatureType::InnerWall; + case v0::PrintFeature::SKIN: + return PrintFeatureType::Skin; + case v0::PrintFeature::SUPPORT: + return PrintFeatureType::Support; + case v0::PrintFeature::SKIRTBRIM: + return PrintFeatureType::SkirtBrim; + case v0::PrintFeature::INFILL: + return PrintFeatureType::Infill; + case v0::PrintFeature::SUPPORTINFILL: + return PrintFeatureType::SupportInfill; + case v0::PrintFeature::MOVECOMBING: + return PrintFeatureType::MoveCombing; + case v0::PrintFeature::MOVERETRACTION: + return PrintFeatureType::MoveRetraction; + case v0::PrintFeature::SUPPORTINTERFACE: + return PrintFeatureType::SupportInterface; + case v0::PrintFeature::PRIMETOWER: + return PrintFeatureType::PrimeTower; + case v0::PrintFeature::NUMPRINTFEATURETYPES: + return PrintFeatureType::NumPrintFeatureTypes; + default: + return PrintFeatureType::NoneType; + } +} + +[[nodiscard]] constexpr SpaceFillType gcode_paths_modify_response::getSpaceFillType(const v0::SpaceFillType space_fill_type) noexcept +{ + switch (space_fill_type) + { + case v0::SpaceFillType::NONE: + return SpaceFillType::None; + case v0::SpaceFillType::POLYGONS: + return SpaceFillType::Polygons; + case v0::SpaceFillType::POLY_LINES: + return SpaceFillType::PolyLines; + case v0::SpaceFillType::LINES: + return SpaceFillType::Lines; + default: + return SpaceFillType::None; + } +} + +[[nodiscard]] GCodePathConfig gcode_paths_modify_response::buildConfig(const v0::GCodePath& path) +{ + const coord_t line_width = path.config().line_width(); + const coord_t layer_height = path.config().layer_thickness(); + const Ratio flow = path.config().flow_ratio(); + const SpeedDerivatives speed_derivatives{ .speed = path.config().speed_derivatives().velocity(), + .acceleration = path.config().speed_derivatives().acceleration(), + .jerk = path.config().speed_derivatives().jerk() }; + const bool is_bridge_path = path.config().is_bridge_path(); + const double fan_speed = path.config().fan_speed(); + const auto feature_type = getPrintFeatureType(path.config().feature()); + return { .type = feature_type, + .line_width = line_width, + .layer_thickness = layer_height, + .flow = flow, + .speed_derivatives = speed_derivatives, + .is_bridge_path = is_bridge_path, + .fan_speed = fan_speed }; +} + gcode_paths_modify_response::native_value_type gcode_paths_modify_response::operator()(const gcode_paths_modify_response::value_type& message) const { std::vector paths; for (const auto& gcode_path_msg : message.gcode_paths()) { - const auto config = [gcode_path_msg]() - { - const auto type = [gcode_path_msg]() - { - switch (gcode_path_msg.config().feature()) - { - case v0::PrintFeature::NONETYPE: - return PrintFeatureType::NoneType; - case v0::PrintFeature::OUTERWALL: - return PrintFeatureType::OuterWall; - case v0::PrintFeature::INNERWALL: - return PrintFeatureType::InnerWall; - case v0::PrintFeature::SKIN: - return PrintFeatureType::Skin; - case v0::PrintFeature::SUPPORT: - return PrintFeatureType::Support; - case v0::PrintFeature::SKIRTBRIM: - return PrintFeatureType::SkirtBrim; - case v0::PrintFeature::INFILL: - return PrintFeatureType::Infill; - case v0::PrintFeature::SUPPORTINFILL: - return PrintFeatureType::SupportInfill; - case v0::PrintFeature::MOVECOMBING: - return PrintFeatureType::MoveCombing; - case v0::PrintFeature::MOVERETRACTION: - return PrintFeatureType::MoveRetraction; - case v0::PrintFeature::SUPPORTINTERFACE: - return PrintFeatureType::SupportInterface; - case v0::PrintFeature::PRIMETOWER: - return PrintFeatureType::PrimeTower; - case v0::PrintFeature::NUMPRINTFEATURETYPES: - return PrintFeatureType::NumPrintFeatureTypes; - default: - return PrintFeatureType::NoneType; - } - }(); - - const coord_t line_width = gcode_path_msg.config().line_width(); - const coord_t layer_height = gcode_path_msg.config().layer_thickness(); - const Ratio flow = gcode_path_msg.config().flow_ratio(); - const SpeedDerivatives speed_derivatives = { gcode_path_msg.config().speed_derivatives().velocity(), - gcode_path_msg.config().speed_derivatives().acceleration(), - gcode_path_msg.config().speed_derivatives().jerk() }; - const bool is_bridge_path = gcode_path_msg.config().is_bridge_path(); - const double fan_speed = gcode_path_msg.config().fan_speed(); - return GCodePathConfig(type, line_width, layer_height, flow, speed_derivatives, is_bridge_path, fan_speed); - }(); - - const auto gcode_path = [config, gcode_path_msg]() - { - // TODO get actual mesh_id from message - const SliceMeshStorage* mesh_id = nullptr; - const SpaceFillType space_fill_type = [gcode_path_msg]() - { - switch (gcode_path_msg.space_fill_type()) - { - case v0::SpaceFillType::NONE: - return SpaceFillType::None; - case v0::SpaceFillType::POLYGONS: - return SpaceFillType::Polygons; - case v0::SpaceFillType::POLY_LINES: - return SpaceFillType::PolyLines; - case v0::SpaceFillType::LINES: - return SpaceFillType::Lines; - default: - return SpaceFillType::None; - } - }(); - const Ratio flow = gcode_path_msg.flow(); - const Ratio width_factor = gcode_path_msg.width_factor(); - const bool spiralize = gcode_path_msg.spiralize(); - const Ratio speed_factor = gcode_path_msg.speed_factor(); - - const auto path = GCodePath(config, nullptr, space_fill_type, flow, width_factor, spiralize, speed_factor); - GCodePath gcode_path(path); - gcode_path.points = gcode_path_msg.path().path() - | ranges::views::transform( - [](const auto& point_msg) - { - return Point{ point_msg.x(), point_msg.y() }; - }) - | ranges::to_vector; - return gcode_path; - }(); - - paths.emplace_back(gcode_path); + const std::shared_ptr mesh = nullptr; + const GCodePathConfig config = buildConfig(gcode_path_msg); + const Ratio flow = gcode_path_msg.flow(); + const Ratio width_factor = gcode_path_msg.width_factor(); + const bool spiralize = gcode_path_msg.spiralize(); + const Ratio speed_factor = gcode_path_msg.speed_factor(); + const auto space_fill_type = getSpaceFillType(gcode_path_msg.space_fill_type()); + GCodePath path{ .config = config, + .mesh = mesh, + .space_fill_type = space_fill_type, + .flow = flow, + .width_factor = width_factor, + .spiralize = spiralize, + .speed_factor = speed_factor }; + path.points = gcode_path_msg.path().path() + | ranges::views::transform( + [](const auto& point_msg) + { + return Point{ point_msg.x(), point_msg.y() }; + }) + | ranges::to_vector; + + paths.emplace_back(path); } return paths; From 23044f6db345944f9cea0c1c0073fc617659f115 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 22 Aug 2023 10:51:56 +0200 Subject: [PATCH 451/656] Refactored gcode_paths_modify_request function This commit simplifies the gcode_paths_modify_request function operator by extracting the logic for constructing SpaceFillType and PrintFeature into separate, reusable functions. This improves the readability of the code and makes it easier to maintain. The modification also fixes the casting issue of extruder_nr to ensure its value is transmitted correctly. Contributes to CURA-10446 --- include/plugins/converters.h | 2 + src/plugins/converters.cpp | 120 ++++++++++++++++------------------- 2 files changed, 56 insertions(+), 66 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 2ebc7eddb5..204925a2be 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -113,6 +113,8 @@ struct infill_generate_response struct gcode_paths_modify_request : public details::converter> { + [[nodsicard]] static constexpr v0::SpaceFillType getSpaceFillType(const SpaceFillType space_fill_type) noexcept; + [[nodiscard]] static constexpr v0::PrintFeature getPrintFeature(const PrintFeatureType print_feature_type) noexcept; value_type operator()(const native_value_type& gcode, const size_t extruder_nr, const LayerIndex layer_nr) const; }; diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 4df1b999de..1083256e44 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -262,12 +262,63 @@ infill_generate_response::native_value_type infill_generate_response::operator() return { toolpaths_, result_polygons, result_lines }; } +[[nodiscard]] constexpr v0::SpaceFillType gcode_paths_modify_request::getSpaceFillType(const cura::SpaceFillType space_fill_type) noexcept +{ + switch (space_fill_type) + { + case SpaceFillType::None: + return v0::SpaceFillType::NONE; + case SpaceFillType::Polygons: + return v0::SpaceFillType::POLYGONS; + case SpaceFillType::PolyLines: + return v0::SpaceFillType::POLY_LINES; + case SpaceFillType::Lines: + return v0::SpaceFillType::LINES; + default: + return v0::SpaceFillType::NONE; + } +} + +[[nodsicard]] constexpr v0::PrintFeature gcode_paths_modify_request::getPrintFeature(const cura::PrintFeatureType print_feature_type) noexcept +{ + switch (print_feature_type) + { + case PrintFeatureType::NoneType: + return v0::PrintFeature::NONETYPE; + case PrintFeatureType::OuterWall: + return v0::PrintFeature::OUTERWALL; + case PrintFeatureType::InnerWall: + return v0::PrintFeature::INNERWALL; + case PrintFeatureType::Skin: + return v0::PrintFeature::SKIN; + case PrintFeatureType::Support: + return v0::PrintFeature::SUPPORT; + case PrintFeatureType::SkirtBrim: + return v0::PrintFeature::SKIRTBRIM; + case PrintFeatureType::Infill: + return v0::PrintFeature::INFILL; + case PrintFeatureType::SupportInfill: + return v0::PrintFeature::SUPPORTINFILL; + case PrintFeatureType::MoveCombing: + return v0::PrintFeature::MOVECOMBING; + case PrintFeatureType::MoveRetraction: + return v0::PrintFeature::MOVERETRACTION; + case PrintFeatureType::SupportInterface: + return v0::PrintFeature::SUPPORTINTERFACE; + case PrintFeatureType::PrimeTower: + return v0::PrintFeature::PRIMETOWER; + case PrintFeatureType::NumPrintFeatureTypes: + return v0::PrintFeature::NUMPRINTFEATURETYPES; + default: + return v0::PrintFeature::NONETYPE; + } +} gcode_paths_modify_request::value_type gcode_paths_modify_request::operator()(const gcode_paths_modify_request::native_value_type& paths, const size_t extruder_nr, const LayerIndex layer_nr) const { value_type message{}; - message.set_extruder_nr(extruder_nr); + message.set_extruder_nr(static_cast(extruder_nr)); message.set_layer_nr(layer_nr); // Construct the repeated GCodepath message @@ -275,24 +326,7 @@ gcode_paths_modify_request::value_type for (const auto& path : paths) { auto* gcode_path = gcode_paths->Add(); - - switch (path.space_fill_type) - { - case SpaceFillType::None: - gcode_path->set_space_fill_type(v0::SpaceFillType::NONE); - break; - case SpaceFillType::Polygons: - gcode_path->set_space_fill_type(v0::SpaceFillType::POLYGONS); - break; - case SpaceFillType::PolyLines: - gcode_path->set_space_fill_type(v0::SpaceFillType::POLY_LINES); - break; - case SpaceFillType::Lines: - gcode_path->set_space_fill_type(v0::SpaceFillType::LINES); - break; - default: - gcode_path->set_space_fill_type(v0::SpaceFillType::NONE); - } + gcode_path->set_space_fill_type(getSpaceFillType(path.space_fill_type)); gcode_path->set_flow(path.flow); gcode_path->set_width_factor(path.width_factor); @@ -308,53 +342,7 @@ gcode_paths_modify_request::value_type } auto* config_msg = gcode_path->mutable_config(); - - switch (path.config.getPrintFeatureType()) - { - case PrintFeatureType::NoneType: - config_msg->set_feature(v0::PrintFeature::NONETYPE); - break; - case PrintFeatureType::OuterWall: - config_msg->set_feature(v0::PrintFeature::OUTERWALL); - break; - case PrintFeatureType::InnerWall: - config_msg->set_feature(v0::PrintFeature::INNERWALL); - break; - case PrintFeatureType::Skin: - config_msg->set_feature(v0::PrintFeature::SKIN); - break; - case PrintFeatureType::Support: - config_msg->set_feature(v0::PrintFeature::SUPPORT); - break; - case PrintFeatureType::SkirtBrim: - config_msg->set_feature(v0::PrintFeature::SKIRTBRIM); - break; - case PrintFeatureType::Infill: - config_msg->set_feature(v0::PrintFeature::INFILL); - break; - case PrintFeatureType::SupportInfill: - config_msg->set_feature(v0::PrintFeature::SUPPORTINFILL); - break; - case PrintFeatureType::MoveCombing: - config_msg->set_feature(v0::PrintFeature::MOVECOMBING); - break; - case PrintFeatureType::MoveRetraction: - config_msg->set_feature(v0::PrintFeature::MOVERETRACTION); - break; - case PrintFeatureType::SupportInterface: - config_msg->set_feature(v0::PrintFeature::SUPPORTINTERFACE); - break; - case PrintFeatureType::PrimeTower: - config_msg->set_feature(v0::PrintFeature::PRIMETOWER); - break; - case PrintFeatureType::NumPrintFeatureTypes: - config_msg->set_feature(v0::PrintFeature::NUMPRINTFEATURETYPES); - break; - default: - config_msg->set_feature(v0::PrintFeature::NONETYPE); - break; - } - + config_msg->set_feature(getPrintFeature(path.config.type)); config_msg->set_line_width(path.config.getLineWidth()); config_msg->set_layer_thickness(path.config.getLayerThickness()); config_msg->set_flow_ratio(path.config.getFlowRatio()); From 67e49a9ca787bc54b1fcc79453f961c4b62343e4 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 22 Aug 2023 10:52:20 +0200 Subject: [PATCH 452/656] Add dedicated MeshPatchConfig header CURA-10446 --- include/settings/MeshPathConfigs.h | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 include/settings/MeshPathConfigs.h diff --git a/include/settings/MeshPathConfigs.h b/include/settings/MeshPathConfigs.h new file mode 100644 index 0000000000..4f8d4143f0 --- /dev/null +++ b/include/settings/MeshPathConfigs.h @@ -0,0 +1,34 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef SETTINGS_TYPES_MESHPATHCONFIGS_H +#define SETTINGS_TYPES_MESHPATHCONFIGS_H + +#include "GCodePathConfig.h" +#include "settings/types/LayerIndex.h" +#include "settings/types/Ratio.h" +#include "sliceDataStorage.h" + +namespace cura +{ +struct MeshPathConfigs +{ + GCodePathConfig inset0_config{}; + GCodePathConfig insetX_config{}; + GCodePathConfig bridge_inset0_config{}; + GCodePathConfig bridge_insetX_config{}; + GCodePathConfig skin_config{}; + GCodePathConfig bridge_skin_config{}; // used for first bridge layer + GCodePathConfig bridge_skin_config2{}; // used for second bridge layer + GCodePathConfig bridge_skin_config3{}; // used for third bridge layer + GCodePathConfig roofing_config{}; + std::vector infill_config{}; + GCodePathConfig ironing_config{}; + + MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder); + void smoothAllSpeeds(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer); +}; + +} // namespace cura + +#endif // SETTINGS_TYPES_MESHPATHCONFIGS_H From 2d72ea0c311e237165e28dcbcfd9c797f7aae032 Mon Sep 17 00:00:00 2001 From: Cody Andrews Date: Wed, 15 Mar 2023 11:30:46 -0700 Subject: [PATCH 453/656] Fix integer underflow on 32 bit systems --- src/SkeletalTrapezoidation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index 5d6babcdd6..a0254d6270 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -1797,7 +1797,7 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin // TODO: don't use toolpath locations past the middle! // TODO: stretch bead widths and locations of the higher bead count beading to fit in the left over space coord_t next_inset_idx; - for (next_inset_idx = left.toolpath_locations.size() - 1; next_inset_idx >= 0; next_inset_idx--) + for (next_inset_idx = coord_t(left.toolpath_locations.size()) - 1; next_inset_idx >= 0; next_inset_idx--) { if (switching_radius > left.toolpath_locations[next_inset_idx]) { From ddcd384b71f400f80d1b8b711ca84631e140fbd1 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 22 Aug 2023 14:03:21 +0200 Subject: [PATCH 454/656] Small refactor: c-style cast -> 'modern' cast. --- src/SkeletalTrapezoidation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index a0254d6270..1f67bb9a45 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -1797,7 +1797,7 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin // TODO: don't use toolpath locations past the middle! // TODO: stretch bead widths and locations of the higher bead count beading to fit in the left over space coord_t next_inset_idx; - for (next_inset_idx = coord_t(left.toolpath_locations.size()) - 1; next_inset_idx >= 0; next_inset_idx--) + for (next_inset_idx = static_cast(left.toolpath_locations.size()) - 1; next_inset_idx >= 0; next_inset_idx--) { if (switching_radius > left.toolpath_locations[next_inset_idx]) { From 1a39eb23f7a2efeb5baf217bc60e8c4df0b25286 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 22 Aug 2023 15:15:00 +0200 Subject: [PATCH 455/656] Improve and refactor the code for better readability The commit drastically changes the way things are written in the code in different files to improve readability and efficiency. Major changes include an updated method for assigning properties to GCodePath and MeshPathConfigs objects, using struct-designated initializers to improve readability. Variable assignments were also refactored to make understanding the code easier. Overall, the commit aims to make the code more efficient and maintainable. Contributes to CURA-10446 --- CMakeLists.txt | 1 + include/pathPlanning/TimeMaterialEstimates.h | 28 +-- include/settings/MeshPathConfigs.h | 2 +- src/settings/MeshPathConfigs.cpp | 126 +++++++++++++ src/settings/PathConfigStorage.cpp | 113 ------------ tests/ExtruderPlanTest.cpp | 179 ++++++++++++++++--- 6 files changed, 298 insertions(+), 151 deletions(-) create mode 100644 src/settings/MeshPathConfigs.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a100acaaf..a27876f7f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ set(engine_SRCS # Except main.cpp. src/settings/AdaptiveLayerHeights.cpp src/settings/FlowTempGraph.cpp + src/settings/MeshPathConfigs.cpp src/settings/PathConfigStorage.cpp src/settings/Settings.cpp src/settings/ZSeamConfig.cpp diff --git a/include/pathPlanning/TimeMaterialEstimates.h b/include/pathPlanning/TimeMaterialEstimates.h index 90a1cb3a76..4b30011a5e 100644 --- a/include/pathPlanning/TimeMaterialEstimates.h +++ b/include/pathPlanning/TimeMaterialEstimates.h @@ -40,23 +40,23 @@ struct TimeMaterialEstimates constexpr TimeMaterialEstimates operator+(const TimeMaterialEstimates& other) const noexcept { - return TimeMaterialEstimates{ extrude_time + other.extrude_time, - extrude_time_at_slowest_path_speed + other.extrude_time_at_slowest_path_speed, - extrude_time_at_minimum_speed + other.extrude_time_at_minimum_speed, - unretracted_travel_time + other.unretracted_travel_time, - retracted_travel_time + other.retracted_travel_time, - material + other.material }; - } + return { .extrude_time = extrude_time + other.extrude_time, + .unretracted_travel_time = unretracted_travel_time + other.unretracted_travel_time, + .retracted_travel_time = retracted_travel_time + other.retracted_travel_time, + .material = material + other.material, + .extrude_time_at_slowest_path_speed = extrude_time_at_slowest_path_speed + other.extrude_time_at_slowest_path_speed, + .extrude_time_at_minimum_speed = extrude_time_at_minimum_speed + other.extrude_time_at_minimum_speed }; + }; constexpr TimeMaterialEstimates operator-(const TimeMaterialEstimates& other) const noexcept { - return TimeMaterialEstimates{ extrude_time - other.extrude_time, - extrude_time_at_slowest_path_speed - other.extrude_time_at_slowest_path_speed, - extrude_time_at_minimum_speed - other.extrude_time_at_minimum_speed, - unretracted_travel_time - other.unretracted_travel_time, - retracted_travel_time - other.retracted_travel_time, - material - other.material }; - } + return { .extrude_time = extrude_time - other.extrude_time, + .unretracted_travel_time = unretracted_travel_time - other.unretracted_travel_time, + .retracted_travel_time = retracted_travel_time - other.retracted_travel_time, + .material = material - other.material, + .extrude_time_at_slowest_path_speed = extrude_time_at_slowest_path_speed - other.extrude_time_at_slowest_path_speed, + .extrude_time_at_minimum_speed = extrude_time_at_minimum_speed - other.extrude_time_at_minimum_speed }; + }; constexpr auto operator<=>(const TimeMaterialEstimates& other) const noexcept = default; diff --git a/include/settings/MeshPathConfigs.h b/include/settings/MeshPathConfigs.h index 4f8d4143f0..43457fbc8c 100644 --- a/include/settings/MeshPathConfigs.h +++ b/include/settings/MeshPathConfigs.h @@ -25,7 +25,7 @@ struct MeshPathConfigs std::vector infill_config{}; GCodePathConfig ironing_config{}; - MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder); + MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex layer_nr, const std::vector& line_width_factor_per_extruder); void smoothAllSpeeds(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer); }; diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp new file mode 100644 index 0000000000..942084a231 --- /dev/null +++ b/src/settings/MeshPathConfigs.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#include "settings/MeshPathConfigs.h" + +#include "ExtruderTrain.h" +#include "PrintFeature.h" + +#include + +namespace cura +{ + +MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex layer_nr, const std::vector& line_width_factor_per_extruder) + : inset0_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_0_material_flow") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0"), + .acceleration = mesh.settings.get("acceleration_wall_0"), + .jerk = mesh.settings.get("jerk_wall_0") } } + , insetX_config{ .type = PrintFeatureType::InnerWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_x_material_flow") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), + .acceleration = mesh.settings.get("acceleration_wall_x"), + .jerk = mesh.settings.get("jerk_wall_x") } } + , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") + * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("bridge_wall_material_flow"), + .speed_derivatives = { .speed = mesh.settings.get("bridge_wall_speed"), + .acceleration = mesh.settings.get("acceleration_wall_0"), + .jerk = mesh.settings.get("jerk_wall_0") }, + .is_bridge_path = true, + .fan_speed = mesh.settings.get("bridge_fan_speed") * 100.0 } + , bridge_insetX_config{ .type = PrintFeatureType::InnerWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") + * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("bridge_wall_material_flow"), + .speed_derivatives = { .speed = mesh.settings.get("bridge_wall_speed"), + .acceleration = mesh.settings.get("acceleration_wall_x"), + .jerk = mesh.settings.get("jerk_wall_x") }, + .is_bridge_path = true, + .fan_speed = mesh.settings.get("bridge_fan_speed") * 100.0 } + , skin_config{ .type = PrintFeatureType::Skin, + .line_width = static_cast( + mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("skin_material_flow") * (layer_nr == 0 ? mesh.settings.get("skin_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_topbottom"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") } } + , bridge_skin_config{ .type = PrintFeatureType::Skin, + .line_width = static_cast( + mesh.settings.get("skin_line_width") + * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("bridge_skin_material_flow"), + .speed_derivatives = { .speed = mesh.settings.get("bridge_skin_speed"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }, + .is_bridge_path = true, + .fan_speed = mesh.settings.get("bridge_fan_speed") * 100.0 } + , bridge_skin_config2{ .type = PrintFeatureType::Skin, + .line_width = static_cast( + mesh.settings.get("skin_line_width") + * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("bridge_skin_material_flow_2"), + .speed_derivatives = { .speed = mesh.settings.get("bridge_skin_speed_2"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }, + .is_bridge_path = true, + .fan_speed = mesh.settings.get("bridge_fan_speed_2") * 100.0 } + , bridge_skin_config3{ .type = PrintFeatureType::Skin, + .line_width = static_cast( + mesh.settings.get("skin_line_width") + * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("bridge_skin_material_flow_3"), + .speed_derivatives = { .speed = mesh.settings.get("bridge_skin_speed_3"), + .acceleration = mesh.settings.get("acceleration_topbottom"), + .jerk = mesh.settings.get("jerk_topbottom") }, + .is_bridge_path = true, + .fan_speed = mesh.settings.get("bridge_fan_speed_3") * 100.0 } + , roofing_config{ .type = PrintFeatureType::Skin, + .line_width = mesh.settings.get("roofing_line_width"), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("roofing_material_flow") * (layer_nr == 0 ? mesh.settings.get("material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_roofing"), + .acceleration = mesh.settings.get("acceleration_roofing"), + .jerk = mesh.settings.get("jerk_roofing") } } + , ironing_config{ .type = PrintFeatureType::Skin, + .line_width = mesh.settings.get("ironing_line_spacing"), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("ironing_flow"), + .speed_derivatives = { .speed = mesh.settings.get("speed_ironing"), + .acceleration = mesh.settings.get("acceleration_ironing"), + .jerk = mesh.settings.get("jerk_ironing") } } + +{ + infill_config.reserve(MAX_INFILL_COMBINE); + + for (const auto combine_idx : ranges::views::iota(1, MAX_INFILL_COMBINE + 1)) + { + infill_config.emplace_back(GCodePathConfig{ + .type = PrintFeatureType::Infill, + .line_width = static_cast( + mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("infill_material_flow") * (layer_nr == 0 ? mesh.settings.get("material_flow_layer_0") : Ratio{ 1.0 }) * combine_idx, + .speed_derivatives = { .speed = mesh.settings.get("speed_infill"), + .acceleration = mesh.settings.get("acceleration_infill"), + .jerk = mesh.settings.get("jerk_infill") } }); + } +} + +} // namespace cura \ No newline at end of file diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 9d253f83b4..382eacb827 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -32,119 +32,6 @@ std::vector PathConfigStorage::getLineWidthFactorPerExtruder(const LayerI return ret; } -MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t layer_thickness, const LayerIndex& layer_nr, const std::vector& line_width_factor_per_extruder) - : inset0_config( - PrintFeatureType::OuterWall, - mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("wall_0_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = mesh.settings.get("speed_wall_0"), - .acceleration = mesh.settings.get("acceleration_wall_0"), - .jerk = mesh.settings.get("jerk_wall_0") }) - , insetX_config( - PrintFeatureType::InnerWall, - mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("wall_x_material_flow") * ((layer_nr == 0) ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = mesh.settings.get("speed_wall_x"), - .acceleration = mesh.settings.get("acceleration_wall_x"), - .jerk = mesh.settings.get("jerk_wall_x") }) - , bridge_inset0_config( - PrintFeatureType::OuterWall, - mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("bridge_wall_material_flow"), - SpeedDerivatives{ .speed = mesh.settings.get("bridge_wall_speed"), - .acceleration = mesh.settings.get("acceleration_wall_0"), - .jerk = mesh.settings.get("jerk_wall_0") }, - true // is_bridge_path - , - mesh.settings.get("bridge_fan_speed") * 100.0) - , bridge_insetX_config( - PrintFeatureType::InnerWall, - mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("bridge_wall_material_flow"), - SpeedDerivatives{ .speed = mesh.settings.get("bridge_wall_speed"), - .acceleration = mesh.settings.get("acceleration_wall_x"), - .jerk = mesh.settings.get("jerk_wall_x") }, - true // is_bridge_path - , - mesh.settings.get("bridge_fan_speed") * 100.0) - , skin_config( - PrintFeatureType::Skin, - mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("skin_material_flow") * ((layer_nr == 0) ? mesh.settings.get("skin_material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = mesh.settings.get("speed_topbottom"), - .acceleration = mesh.settings.get("acceleration_topbottom"), - .jerk = mesh.settings.get("jerk_topbottom") }) - , bridge_skin_config( // use bridge skin flow, speed and fan - PrintFeatureType::Skin, - mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("bridge_skin_material_flow"), - SpeedDerivatives{ .speed = mesh.settings.get("bridge_skin_speed"), - .acceleration = mesh.settings.get("acceleration_topbottom"), - .jerk = mesh.settings.get("jerk_topbottom") }, - true // is_bridge_path - , - mesh.settings.get("bridge_fan_speed") * 100.0) - , bridge_skin_config2( // use bridge skin 2 flow, speed and fan - PrintFeatureType::Skin, - mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("bridge_skin_material_flow_2"), - SpeedDerivatives{ .speed = mesh.settings.get("bridge_skin_speed_2"), - .acceleration = mesh.settings.get("acceleration_topbottom"), - .jerk = mesh.settings.get("jerk_topbottom") }, - true // is_bridge_path - , - mesh.settings.get("bridge_fan_speed_2") * 100.0) - , bridge_skin_config3( // use bridge skin 3 flow, speed and fan - PrintFeatureType::Skin, - mesh.settings.get("skin_line_width") * line_width_factor_per_extruder[mesh.settings.get("top_bottom_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("bridge_skin_material_flow_3"), - SpeedDerivatives{ .speed = mesh.settings.get("bridge_skin_speed_3"), - .acceleration = mesh.settings.get("acceleration_topbottom"), - .jerk = mesh.settings.get("jerk_topbottom") }, - true // is_bridge_path - , - mesh.settings.get("bridge_fan_speed_3") * 100.0) - , roofing_config( - PrintFeatureType::Skin, - mesh.settings.get("roofing_line_width"), - layer_thickness, - mesh.settings.get("roofing_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = mesh.settings.get("speed_roofing"), - .acceleration = mesh.settings.get("acceleration_roofing"), - .jerk = mesh.settings.get("jerk_roofing") }) - , ironing_config( - PrintFeatureType::Skin, - mesh.settings.get("ironing_line_spacing"), - layer_thickness, - mesh.settings.get("ironing_flow"), - SpeedDerivatives{ .speed = mesh.settings.get("speed_ironing"), - .acceleration = mesh.settings.get("acceleration_ironing"), - .jerk = mesh.settings.get("jerk_ironing") }) - -{ - infill_config.reserve(MAX_INFILL_COMBINE); - - for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++) - { - infill_config.emplace_back( - PrintFeatureType::Infill, - mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr], - layer_thickness, - mesh.settings.get("infill_material_flow") * ((layer_nr == 0) ? mesh.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1), - SpeedDerivatives{ .speed = mesh.settings.get("speed_infill"), - .acceleration = mesh.settings.get("acceleration_infill"), - .jerk = mesh.settings.get("jerk_infill") }); - } -} - PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const LayerIndex& layer_nr, const coord_t layer_thickness) : support_infill_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_infill_extruder_nr").extruder_nr) , support_roof_extruder_nr(Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_roof_extruder_nr").extruder_nr) diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index f5e1223816..026d87f0e4 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -74,19 +74,56 @@ class ExtruderPlanTestPathCollection : extrusion_config(PrintFeatureType::OuterWall, 400, 100, 1.0_r, SpeedDerivatives(50.0, 1000.0, 10.0)) , travel_config(PrintFeatureType::MoveCombing, 0, 100, 0.0_r, SpeedDerivatives(120.0, 5000.0, 30.0)) { - const SliceMeshStorage* mesh = nullptr; + std::shared_ptr mesh = nullptr; constexpr Ratio flow_1 = 1.0_r; constexpr Ratio width_1 = 1.0_r; constexpr bool no_spiralize = false; constexpr Ratio speed_1 = 1.0_r; - square.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::PolyLines, flow_1, width_1, no_spiralize, speed_1) }); + square.assign({ GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::PolyLines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 } }); + square.back().points = { Point(0, 0), Point(1000, 0), Point(1000, 1000), Point(0, 1000), Point(0, 0) }; - lines.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1) }); + lines.assign({ GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = travel_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = travel_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 } }); lines[0].points = { Point(0, 0), Point(1000, 0) }; lines[1].points = { Point(1000, 0), Point(1000, 400) }; lines[2].points = { Point(1000, 400), Point(0, 400) }; @@ -96,11 +133,41 @@ class ExtruderPlanTestPathCollection constexpr Ratio flow_12 = 1.2_r; constexpr Ratio flow_08 = 0.8_r; constexpr Ratio flow_04 = 0.4_r; - decreasing_flow.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_12, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_08, width_1, no_spiralize, speed_1), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_04, width_1, no_spiralize, speed_1) }); + decreasing_flow.assign({ GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_12, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = travel_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_08, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = travel_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_04, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 } }); decreasing_flow[0].points = { Point(0, 0), Point(1000, 0) }; decreasing_flow[1].points = { Point(1000, 0), Point(1000, 400) }; decreasing_flow[2].points = { Point(1000, 400), Point(0, 400) }; @@ -110,11 +177,41 @@ class ExtruderPlanTestPathCollection constexpr Ratio speed_12 = 1.2_r; constexpr Ratio speed_08 = 0.8_r; constexpr Ratio speed_04 = 0.4_r; - decreasing_speed.assign({ GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_12), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_08), - GCodePath(travel_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_04) }); + decreasing_speed.assign({ GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_12 }, + GCodePath{ .config = travel_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_08 }, + GCodePath{ .config = travel_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_04 } }); decreasing_speed[0].points = { Point(0, 0), Point(1000, 0) }; decreasing_speed[1].points = { Point(1000, 0), Point(1000, 400) }; decreasing_speed[2].points = { Point(1000, 400), Point(0, 400) }; @@ -122,12 +219,48 @@ class ExtruderPlanTestPathCollection decreasing_speed[4].points = { Point(0, 800), Point(1000, 800) }; variable_width.assign({ - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, flow_1, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.8_r, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.6_r, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.4_r, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.2_r, width_1, no_spiralize, speed_1), - GCodePath(extrusion_config, mesh, SpaceFillType::Lines, 0.0_r, width_1, no_spiralize, speed_1), + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = flow_1, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = 0.8_r, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = 0.6_r, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = 0.4_r, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = 0.2_r, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, + GCodePath{ .config = extrusion_config, + .mesh = mesh, + .space_fill_type = SpaceFillType::Lines, + .flow = 0.0_r, + .width_factor = width_1, + .spiralize = no_spiralize, + .speed_factor = speed_1 }, }); variable_width[0].points = { Point(0, 0), Point(1000, 0) }; variable_width[1].points = { Point(1000, 0), Point(2000, 0) }; From ae2473d72a6ccb5f16cdd3aab41e6ef4888005ba Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 22 Aug 2023 18:59:43 +0200 Subject: [PATCH 456/656] Map mesh to new gcode-paths CURA-10446 --- include/plugins/converters.h | 9 ++-- include/plugins/pluginproxy.h | 55 ++++++++++++++++++++++-- include/plugins/slotproxy.h | 17 +++++++- include/plugins/slots.h | 6 +-- src/LayerPlan.cpp | 2 +- src/communication/ArcusCommunication.cpp | 6 ++- src/plugins/converters.cpp | 27 +++++++++--- 7 files changed, 100 insertions(+), 22 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 204925a2be..36a89b9df9 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -87,7 +87,7 @@ struct simplify_request : public details::converter { - native_value_type operator()(const value_type& message) const; + native_value_type operator()([[maybe_unused]] native_value_type& original_value, const value_type& message) const; }; struct postprocess_request : public details::converter @@ -97,7 +97,7 @@ struct postprocess_request : public details::converter { - native_value_type operator()(const value_type& message) const; + native_value_type operator()([[maybe_unused]] native_value_type& original_value, const value_type& message) const; }; struct infill_generate_request : public details::converter @@ -113,17 +113,18 @@ struct infill_generate_response struct gcode_paths_modify_request : public details::converter> { - [[nodsicard]] static constexpr v0::SpaceFillType getSpaceFillType(const SpaceFillType space_fill_type) noexcept; + [[nodiscard]] static constexpr v0::SpaceFillType getSpaceFillType(const SpaceFillType space_fill_type) noexcept; [[nodiscard]] static constexpr v0::PrintFeature getPrintFeature(const PrintFeatureType print_feature_type) noexcept; value_type operator()(const native_value_type& gcode, const size_t extruder_nr, const LayerIndex layer_nr) const; }; struct gcode_paths_modify_response : public details::converter> { + [[nodiscard]] static constexpr PrintFeatureType getPrintFeatureType(const v0::PrintFeature feature) noexcept; [[nodiscard]] static GCodePathConfig buildConfig(const v0::GCodePath& path); [[nodiscard]] static constexpr SpaceFillType getSpaceFillType(const v0::SpaceFillType space_fill_type) noexcept; - native_value_type operator()(const value_type& message) const; + native_value_type operator()(native_value_type& original_value, const value_type& message) const; }; } // namespace cura::plugins diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index c457b935d9..102e76833f 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -165,7 +165,7 @@ class PluginProxy } ~PluginProxy() = default; - value_type invoke(auto&&... args) + value_type generate(auto&&... args) { agrpc::GrpcContext grpc_context; value_type ret_value{}; @@ -175,7 +175,40 @@ class PluginProxy grpc_context, [this, &grpc_context, &status, &ret_value, &args...]() { - return this->invokeCall(grpc_context, status, ret_value, std::forward(args)...); + return this->generateCall(grpc_context, status, ret_value, std::forward(args)...); + }, + boost::asio::detached); + grpc_context.run(); + + if (! status.ok()) // TODO: handle different kind of status codes + { + if (plugin_info_.has_value()) + { + spdlog::error( + "Plugin '{}' running at [{}] for slot {} failed with error: {}", + plugin_info_.value().plugin_name, + plugin_info_.value().peer, + slot_info_.slot_id, + status.error_message()); + throw exceptions::RemoteException(slot_info_, plugin_info_.value(), status.error_message()); + } + spdlog::error("Plugin for slot {} failed with error: {}", slot_info_.slot_id, status.error_message()); + throw exceptions::RemoteException(slot_info_, status.error_message()); + } + return ret_value; + } + + value_type modify(auto& original_value, auto&&... args) + { + agrpc::GrpcContext grpc_context; + value_type ret_value{}; + grpc::Status status; + + boost::asio::co_spawn( + grpc_context, + [this, &grpc_context, &status, &ret_value, &original_value, &args...]() + { + return this->modifyCall(grpc_context, status, ret_value, original_value, std::forward(args)...); }, boost::asio::detached); grpc_context.run(); @@ -256,7 +289,7 @@ class PluginProxy * @param args - Request arguments * @return A boost::asio::awaitable indicating completion of the operation */ - boost::asio::awaitable invokeCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) + boost::asio::awaitable generateCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto&&... args) { using RPC = agrpc::ClientRPC<&invoke_stub_t::PrepareAsyncCall>; grpc::ClientContext client_context{}; @@ -272,6 +305,22 @@ class PluginProxy co_return; } + boost::asio::awaitable modifyCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, value_type& ret_value, auto& original_value, auto&&... args) + { + using RPC = agrpc::ClientRPC<&invoke_stub_t::PrepareAsyncCall>; + grpc::ClientContext client_context{}; + prep_client_context(client_context, slot_info_); + + // Construct request + auto request{ req_(original_value, std::forward(args)...) }; + + // Make unary request + rsp_msg_type response; + status = co_await RPC::request(grpc_context, invoke_stub_, client_context, request, response, boost::asio::use_awaitable); + ret_value = std::move(rsp_(original_value, response)); + co_return; + } + template boost::asio::awaitable broadcastCall(agrpc::GrpcContext& grpc_context, grpc::Status& status, auto&&... args) { diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 6562f7660a..84ba7846c3 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -74,15 +74,28 @@ class SlotProxy * @param args The arguments for the plugin request. * @return The result of the plugin request or the default behavior. */ - constexpr auto invoke(auto&&... args) + constexpr auto generate(auto&&... args) { if (plugin_.has_value()) { - return plugin_.value().invoke(std::forward(args)...); + return plugin_.value().generate(std::forward(args)...); } return std::invoke(default_process, std::forward(args)...); } + constexpr auto modify(auto& original_value, auto&&... args) + { + if (plugin_.has_value()) + { + return plugin_.value().modify(original_value, std::forward(args)...); + } + if constexpr (sizeof...(args) == 0) + { + return std::invoke(default_process, original_value); + } + return std::invoke(default_process, original_value, std::forward(args)...); + } + template void broadcast(auto&&... args) { diff --git a/include/plugins/slots.h b/include/plugins/slots.h index c02f50ece2..5b99ab52b0 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -150,15 +150,15 @@ class Registry, Unit> : public Registry } template - constexpr auto modify(auto&&... args) + constexpr auto modify(auto& original_value, auto&&... args) { - return get().invoke(std::forward(args)...); + return get().modify(original_value, std::forward(args)...); } template constexpr auto generate(auto&&... args) { - return get().invoke(std::forward(args)...); + return get().generate(std::forward(args)...); } void connect(const v0::SlotID& slot_id, auto name, auto& version, auto&& channel) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 58e2eda6c5..0cfda2bd4b 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1910,7 +1910,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; - extruder_plan.paths = slots::instance().generate(extruder_plan.paths, extruder_plan.extruder_nr, layer_nr); + extruder_plan.paths = slots::instance().modify(extruder_plan.paths, extruder_plan.extruder_nr, layer_nr); // Since the time/material estimates _may_ have changed during the plugin modify step we recalculate it extruder_plan.computeNaiveTimeEstimates(gcode.getPositionXY()); diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index c01222bd94..cf17d69454 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -342,7 +342,8 @@ void ArcusCommunication::beginGCode() void ArcusCommunication::flushGCode() { - const std::string& message_str = slots::instance().modify(private_data->gcode_output_stream.str()); + std::string gcode_output_stream = private_data->gcode_output_stream.str(); + auto message_str = slots::instance().modify(gcode_output_stream); if (message_str.size() == 0) { return; @@ -375,7 +376,8 @@ void ArcusCommunication::sendCurrentPosition(const Point& position) void ArcusCommunication::sendGCodePrefix(const std::string& prefix) const { std::shared_ptr message = std::make_shared(); - message->set_data(slots::instance().modify(prefix)); + std::string message_str = prefix; + message->set_data(slots::instance().modify(message_str)); private_data->socket->sendMessage(message); } diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 1083256e44..9605f1964c 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -13,6 +13,7 @@ #include "utils/polygon.h" #include +#include #include namespace cura::plugins @@ -123,7 +124,8 @@ simplify_request::value_type return message; } -simplify_response::native_value_type simplify_response::operator()(const simplify_response::value_type& message) const +simplify_response::native_value_type + simplify_response::operator()([[maybe_unused]] simplify_response::native_value_type& original_value, const simplify_response::value_type& message) const { native_value_type poly{}; for (const auto& paths : message.polygons().polygons()) @@ -155,7 +157,8 @@ postprocess_request::value_type postprocess_request::operator()(const postproces return message; } -postprocess_response::native_value_type postprocess_response::operator()(const postprocess_response::value_type& message) const +postprocess_response::native_value_type + postprocess_response::operator()([[maybe_unused]] postprocess_response::native_value_type& original_value, const postprocess_response::value_type& message) const { return message.gcode_word(); } @@ -279,7 +282,7 @@ infill_generate_response::native_value_type infill_generate_response::operator() } } -[[nodsicard]] constexpr v0::PrintFeature gcode_paths_modify_request::getPrintFeature(const cura::PrintFeatureType print_feature_type) noexcept +[[nodiscard]] constexpr v0::PrintFeature gcode_paths_modify_request::getPrintFeature(const cura::PrintFeatureType print_feature_type) noexcept { switch (print_feature_type) { @@ -332,7 +335,7 @@ gcode_paths_modify_request::value_type gcode_path->set_width_factor(path.width_factor); gcode_path->set_spiralize(path.spiralize); gcode_path->set_speed_factor(path.speed_factor); - + gcode_path->set_mesh_name(path.mesh ? path.mesh->mesh_name : ""); // Construct the OpenPath from the points in a GCodePath for (const auto& point : path.points) { @@ -428,12 +431,22 @@ gcode_paths_modify_request::value_type .fan_speed = fan_speed }; } -gcode_paths_modify_response::native_value_type gcode_paths_modify_response::operator()(const gcode_paths_modify_response::value_type& message) const +gcode_paths_modify_response::native_value_type + gcode_paths_modify_response::operator()(gcode_paths_modify_response::native_value_type& original_value, const gcode_paths_modify_response::value_type& message) const { std::vector paths; + using map_t = std::unordered_map>; + auto meshes = original_value + | ranges::views::filter([](const auto& path){ return path.mesh != nullptr; }) + | ranges::views::transform( + [](const auto& path) -> map_t::value_type + { + return { path.mesh->mesh_name, path.mesh }; + }) + | ranges::to; + for (const auto& gcode_path_msg : message.gcode_paths()) { - const std::shared_ptr mesh = nullptr; const GCodePathConfig config = buildConfig(gcode_path_msg); const Ratio flow = gcode_path_msg.flow(); const Ratio width_factor = gcode_path_msg.width_factor(); @@ -441,7 +454,7 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::oper const Ratio speed_factor = gcode_path_msg.speed_factor(); const auto space_fill_type = getSpaceFillType(gcode_path_msg.space_fill_type()); GCodePath path{ .config = config, - .mesh = mesh, + .mesh = gcode_path_msg.mesh_name().empty() ? nullptr : meshes.at(gcode_path_msg.mesh_name()), .space_fill_type = space_fill_type, .flow = flow, .width_factor = width_factor, From 12b86855f73123618d3fa02185faeafe8383d53a Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 22 Aug 2023 17:00:19 +0000 Subject: [PATCH 457/656] Applied clang-format. --- include/plugins/converters.h | 1 - src/plugins/converters.cpp | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 36a89b9df9..9f76c71b5f 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -120,7 +120,6 @@ struct gcode_paths_modify_request : public details::converter> { - [[nodiscard]] static constexpr PrintFeatureType getPrintFeatureType(const v0::PrintFeature feature) noexcept; [[nodiscard]] static GCodePathConfig buildConfig(const v0::GCodePath& path); [[nodiscard]] static constexpr SpaceFillType getSpaceFillType(const v0::SpaceFillType space_fill_type) noexcept; diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 9605f1964c..d13c07d92e 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -437,7 +437,11 @@ gcode_paths_modify_response::native_value_type std::vector paths; using map_t = std::unordered_map>; auto meshes = original_value - | ranges::views::filter([](const auto& path){ return path.mesh != nullptr; }) + | ranges::views::filter( + [](const auto& path) + { + return path.mesh != nullptr; + }) | ranges::views::transform( [](const auto& path) -> map_t::value_type { From a0cfe1d40fe2e8ee67b5cf9afac0ae09ed789098 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 23 Aug 2023 01:37:19 +0200 Subject: [PATCH 458/656] Check configs CURA-10446 --- src/LayerPlan.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 0cfda2bd4b..f4ed218f36 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -108,8 +108,7 @@ GCodePath* LayerPlan::getLatestPathWithConfig( const Ratio speed_factor) { std::vector& paths = extruder_plans.back().paths; - // TODO put back config equality check - if (paths.size() > 0 /*&& paths.back().config == config*/ && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor + if (paths.size() > 0 && paths.back().config == config && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); From 8e205a46a9ed2869b3f22d845b334fb7a615d48b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 23 Aug 2023 16:21:16 +0200 Subject: [PATCH 459/656] Refactor the ExtruderPlan and Infill class to use shared pointers instead of raw pointers In response to eliminating possible memory leaks and improving code quality, the 'ExtruderPlan' and 'infills' classes have been refactored to utilize standard smart pointers (shared_ptr) in lieu with raw pointers. The change affects the 'ExtruderPlan' and 'SierpinskiFillProvider' raw pointers been switched to use 'std::shared_ptr'. Additional changes include the removal of explicit destructors where necessary as shared_ptr manages the lifetime of the objects, ensuring proper clean up when they're no longer in use. Contributes to CURA-10446 --- CMakeLists.txt | 1 + include/ExtruderPlan.h | 212 +++++++++++++++++++++++++++++ include/LayerPlan.h | 192 ++------------------------ include/LayerPlanBuffer.h | 8 +- include/TreeSupportTipGenerator.h | 15 +- include/TreeSupportUtils.h | 5 +- include/infill.h | 10 +- include/infill/LightningLayer.h | 7 +- include/infill/LightningTreeNode.h | 9 +- include/infill/SubDivCube.h | 8 +- include/sliceDataStorage.h | 15 +- src/ExtruderPlan.cpp | 73 ++++++++++ src/FffGcodeWriter.cpp | 8 +- src/FffPolygonGenerator.cpp | 6 +- src/LayerPlan.cpp | 65 --------- src/TreeSupportTipGenerator.cpp | 9 +- src/infill.cpp | 10 +- src/infill/LightningTreeNode.cpp | 6 +- src/infill/SubDivCube.cpp | 15 +- src/sliceDataStorage.cpp | 19 --- src/support.cpp | 4 +- 21 files changed, 353 insertions(+), 344 deletions(-) create mode 100644 include/ExtruderPlan.h create mode 100644 src/ExtruderPlan.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a27876f7f8..86cc733242 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ set(engine_SRCS # Except main.cpp. src/Application.cpp src/bridge.cpp src/ConicalOverhang.cpp + src/ExtruderPlan.cpp src/ExtruderTrain.cpp src/FffGcodeWriter.cpp src/FffPolygonGenerator.cpp diff --git a/include/ExtruderPlan.h b/include/ExtruderPlan.h new file mode 100644 index 0000000000..6f3155148e --- /dev/null +++ b/include/ExtruderPlan.h @@ -0,0 +1,212 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef EXTRUDERPLAN_H +#define EXTRUDERPLAN_H + +#include "FanSpeedLayerTime.h" +#include "RetractionConfig.h" +#include "gcodeExport.h" +#include "pathPlanning/GCodePath.h" +#include "pathPlanning/NozzleTempInsert.h" +#include "pathPlanning/TimeMaterialEstimates.h" +#include "settings/types/LayerIndex.h" +#include "settings/types/Ratio.h" +#include "utils/IntPoint.h" + +#ifdef BUILD_TESTS +#include //Friend tests, so that they can inspect the privates. +#endif + +#include +#include +#include +#include + +namespace cura +{ +class LayerPlanBuffer; +class LayerPlan; +/*! + * An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train. + * + * It allows for temperature command inserts which can be inserted in between paths. + */ +class ExtruderPlan +{ + friend class LayerPlanBuffer; + friend class LayerPlan; +#ifdef BUILD_TESTS + friend class ExtruderPlanPathsParameterizedTest; + FRIEND_TEST(ExtruderPlanPathsParameterizedTest, BackPressureCompensationZeroIsUncompensated); + FRIEND_TEST(ExtruderPlanPathsParameterizedTest, BackPressureCompensationFull); + FRIEND_TEST(ExtruderPlanPathsParameterizedTest, BackPressureCompensationHalf); + FRIEND_TEST(ExtruderPlanTest, BackPressureCompensationEmptyPlan); +#endif +public: + size_t extruder_nr{ 0 }; //!< The extruder used for this paths in the current plan. + + ExtruderPlan() noexcept = default; + + /*! + * Simple contructor. + * + * \warning Doesn't set the required temperature yet. + * + * \param extruder The extruder number for which this object is a plan. + * \param layer_nr The layer index of the layer that this extruder plan is + * part of. + * \param is_raft_layer Whether this extruder plan is part of a raft layer. + */ + ExtruderPlan( + const size_t extruder, + const LayerIndex layer_nr, + const bool is_initial_layer, + const bool is_raft_layer, + const coord_t layer_thickness, + const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, + const RetractionConfig& retraction_config); + + + void insertCommand(NozzleTempInsert&& insert); + + /*! + * Insert the inserts into gcode which should be inserted before \p path_idx + * + * \param path_idx The index into ExtruderPlan::paths which is currently being consider for temperature command insertion + * \param gcode The gcode exporter to which to write the temperature command. + * \param cumulative_path_time The time spend on this path up to this point. + */ + void handleInserts(const size_t path_idx, GCodeExport& gcode, const double cumulative_path_time = std::numeric_limits::infinity()); + + /*! + * Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan + * + * Inserts temperature commands which should be inserted _after_ the last path. + * Also inserts all temperatures which should have been inserted earlier, + * but for which ExtruderPlan::handleInserts hasn't been called correctly. + * + * \param gcode The gcode exporter to which to write the temperature command. + */ + void handleAllRemainingInserts(GCodeExport& gcode); + + /*! + * Applying fan speed changes for minimal layer times. + * + * \param starting_position The position the head was before starting this extruder plan + * \param minTime Maximum minimum layer time for all extruders in this layer + * \param time_other_extr_plans The time spent on the other extruder plans in this layer + */ + void processFanSpeedForMinimalLayerTime(Point starting_position, Duration maximum_cool_min_layer_time, double time_other_extr_plans); + + /*! + * Applying fan speed changes for the first layers. + */ + void processFanSpeedForFirstLayers(); + + /*! + * Get the fan speed computed for this extruder plan + * + * \warning assumes ExtruderPlan::processFanSpeedForMinimalLayerTime has already been called + * + * \return The fan speed computed in processFanSpeedForMinimalLayerTime + */ + double getFanSpeed(); + + /*! + * Apply back-pressure compensation to this path. + * Since the total (filament) pressure in a feeder-system is not only dependent on the pressure that exists between the nozzle and the + * feed-mechanism (which should be near-constant on a bowden style setup), but _also_ between the nozzle and the last-printed layer. + * This last type is called 'back-pressure'. In this function, properties of the path-outflow are adjusted so that the back-pressure is + * compensated for. This is conjectured to be especially important if the printer has a Bowden-tube style setup. + * + * \param The amount of back-pressure compensation as a ratio. 'Applying' a value of 0 is a no-op. + */ + void applyBackPressureCompensation(const Ratio back_pressure_compensation); + +private: + LayerIndex layer_nr{ 0 }; //!< The layer number at which we are currently printing. + bool is_initial_layer{ false }; //!< Whether this extruder plan is printed on the very first layer (which might be raft) + bool is_raft_layer{ false }; //!< Whether this is a layer which is part of the raft + + coord_t layer_thickness{ 200 }; //!< The thickness of this layer in Z-direction + + FanSpeedLayerTimeSettings fan_speed_layer_time_settings{}; //!< The fan speed and layer time settings used to limit this extruder plan + + RetractionConfig retraction_config{}; //!< The retraction settings for the extruder of this plan + + + std::vector paths; //!< The paths planned for this extruder + std::list inserts; //!< The nozzle temperature command inserts, to be inserted in between segments + double heated_pre_travel_time{ 0.0 }; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature + + /*! + * The required temperature at the start of this extruder plan + * or the temp to which to heat gradually over the layer change between this plan and the previous with the same extruder. + * + * In case this extruder plan uses a different extruder than the last extruder plan: + * this is the temperature to which to heat and wait before starting this extruder. + * + * In case this extruder plan uses the same extruder as the previous extruder plan (previous layer): + * this is the temperature used to heat to gradually when moving from the previous extruder layer to the next. + * In that case no temperature (and wait) command will be inserted from this value, but a NozzleTempInsert is used instead. + * In this case this member is only used as a way to convey information between different calls of \ref LayerPlanBuffer::processBuffer + */ + double required_start_temperature{ -1.0 }; + std::optional extrusion_temperature{ std::nullopt }; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate + //!< because of the initial and final print temp (none if extruder plan has no extrusion moves) + std::optional::iterator> extrusion_temperature_command{ + std::nullopt + }; //!< The command to heat from the printing temperature of this extruder plan to the printing + //!< temperature of the next extruder plan (if it has the same extruder). + std::optional prev_extruder_standby_temp{ + std::nullopt + }; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder. + + TimeMaterialEstimates estimates{}; //!< Accumulated time and material estimates for all planned paths within this extruder plan. + double slowest_path_speed{ 0.0 }; + + double extraTime{ 0.0 }; //!< Extra waiting time at the and of this extruder plan, so that the filament can cool + + double fan_speed{ 0.0 }; //!< The fan speed to be used during this extruder plan + + double temperatureFactor{ 0.0 }; //!< Temperature reduction factor for small layers + + /*! + * Set the fan speed to be used while printing this extruder plan + * + * \param fan_speed The speed for the fan + */ + void setFanSpeed(double fan_speed); + + /*! + * Force the minimal layer time to hold by slowing down and lifting the head if required. + * + * \param maximum_cool_min_layer_time Maximum minimum layer time for all extruders in this layer + * \param time_other_extr_plans Time spend on other extruders in this layer + */ + void forceMinimalLayerTime(double maximum_cool_min_layer_time, double time_other_extr_plans); + + /*! + * @return The time needed for (un)retract the path + */ + double getRetractTime(const GCodePath& path); + + /*! + * @return distance between p0 and p1 as well as the time spend on the segment + */ + std::pair getPointToPointTime(const Point& p0, const Point& p1, const GCodePath& path); + + /*! + * Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates. + * and store them in each ExtruderPlan and each GCodePath. + * + * \param starting_position The position the head was in before starting this layer + * \return the total estimates of this layer + */ + TimeMaterialEstimates computeNaiveTimeEstimates(Point starting_position); +}; + +} // namespace cura + +#endif // EXTRUDERPLAN_H diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 42b5eb8d33..9a8be087f1 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -16,203 +16,25 @@ #include "settings/types/LayerIndex.h" #include "utils/ExtrusionJunction.h" #include "utils/polygon.h" +#include "ExtruderPlan.h" + +#ifdef BUILD_TESTS +#include //Friend tests, so that they can inspect the privates. +#endif #include #include #include #include #include -#ifdef BUILD_TESTS -#include //Friend tests, so that they can inspect the privates. -#endif namespace cura { class Comb; -class LayerPlan; // forward declaration so that ExtruderPlan can be a friend -class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a friend class SliceDataStorage; +class LayerPlanBuffer; -/*! - * An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train. - * - * It allows for temperature command inserts which can be inserted in between paths. - */ -class ExtruderPlan -{ - friend class LayerPlan; // TODO: LayerPlan still does a lot which should actually be handled in this class. - friend class LayerPlanBuffer; // TODO: LayerPlanBuffer handles paths directly -#ifdef BUILD_TESTS - friend class ExtruderPlanPathsParameterizedTest; - FRIEND_TEST(ExtruderPlanPathsParameterizedTest, BackPressureCompensationZeroIsUncompensated); - FRIEND_TEST(ExtruderPlanPathsParameterizedTest, BackPressureCompensationFull); - FRIEND_TEST(ExtruderPlanPathsParameterizedTest, BackPressureCompensationHalf); - FRIEND_TEST(ExtruderPlanTest, BackPressureCompensationEmptyPlan); -#endif -protected: - std::vector paths; //!< The paths planned for this extruder - std::list inserts; //!< The nozzle temperature command inserts, to be inserted in between segments - - double heated_pre_travel_time; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature - - /*! - * The required temperature at the start of this extruder plan - * or the temp to which to heat gradually over the layer change between this plan and the previous with the same extruder. - * - * In case this extruder plan uses a different extruder than the last extruder plan: - * this is the temperature to which to heat and wait before starting this extruder. - * - * In case this extruder plan uses the same extruder as the previous extruder plan (previous layer): - * this is the temperature used to heat to gradually when moving from the previous extruder layer to the next. - * In that case no temperature (and wait) command will be inserted from this value, but a NozzleTempInsert is used instead. - * In this case this member is only used as a way to convey information between different calls of \ref LayerPlanBuffer::processBuffer - */ - double required_start_temperature; - std::optional extrusion_temperature; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate because of the - //!< initial and final print temp (none if extruder plan has no extrusion moves) - std::optional::iterator> extrusion_temperature_command; //!< The command to heat from the printing temperature of this extruder plan to the printing - //!< temperature of the next extruder plan (if it has the same extruder). - std::optional prev_extruder_standby_temp; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder. - - TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan. - double slowest_path_speed; - -public: - size_t extruder_nr; //!< The extruder used for this paths in the current plan. - - /*! - * Simple contructor. - * - * \warning Doesn't set the required temperature yet. - * - * \param extruder The extruder number for which this object is a plan. - * \param layer_nr The layer index of the layer that this extruder plan is - * part of. - * \param is_raft_layer Whether this extruder plan is part of a raft layer. - */ - ExtruderPlan( - const size_t extruder, - const LayerIndex layer_nr, - const bool is_initial_layer, - const bool is_raft_layer, - const coord_t layer_thickness, - const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, - const RetractionConfig& retraction_config); - - - void insertCommand(auto&& insert) - { - inserts.emplace_back(std::forward(insert)); - } - - /*! - * Insert the inserts into gcode which should be inserted before \p path_idx - * - * \param path_idx The index into ExtruderPlan::paths which is currently being consider for temperature command insertion - * \param gcode The gcode exporter to which to write the temperature command. - * \param cumulative_path_time The time spend on this path up to this point. - */ - void handleInserts(const size_t path_idx, GCodeExport& gcode, const double& cumulative_path_time = std::numeric_limits::infinity()); - - /*! - * Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan - * - * Inserts temperature commands which should be inserted _after_ the last path. - * Also inserts all temperatures which should have been inserted earlier, - * but for which ExtruderPlan::handleInserts hasn't been called correctly. - * - * \param gcode The gcode exporter to which to write the temperature command. - */ - void handleAllRemainingInserts(GCodeExport& gcode); - - /*! - * Applying fan speed changes for minimal layer times. - * - * \param starting_position The position the head was before starting this extruder plan - * \param minTime Maximum minimum layer time for all extruders in this layer - * \param time_other_extr_plans The time spent on the other extruder plans in this layer - */ - void processFanSpeedForMinimalLayerTime(Point starting_position, Duration maximum_cool_min_layer_time, double time_other_extr_plans); - - /*! - * Applying fan speed changes for the first layers. - */ - void processFanSpeedForFirstLayers(); - - /*! - * Get the fan speed computed for this extruder plan - * - * \warning assumes ExtruderPlan::processFanSpeedForMinimalLayerTime has already been called - * - * \return The fan speed computed in processFanSpeedForMinimalLayerTime - */ - double getFanSpeed(); - - /*! - * Apply back-pressure compensation to this path. - * Since the total (filament) pressure in a feeder-system is not only dependent on the pressure that exists between the nozzle and the - * feed-mechanism (which should be near-constant on a bowden style setup), but _also_ between the nozzle and the last-printed layer. - * This last type is called 'back-pressure'. In this function, properties of the path-outflow are adjusted so that the back-pressure is - * compensated for. This is conjectured to be especially important if the printer has a Bowden-tube style setup. - * - * \param The amount of back-pressure compensation as a ratio. 'Applying' a value of 0 is a no-op. - */ - void applyBackPressureCompensation(const Ratio back_pressure_compensation); - -protected: - LayerIndex layer_nr; //!< The layer number at which we are currently printing. - bool is_initial_layer; //!< Whether this extruder plan is printed on the very first layer (which might be raft) - const bool is_raft_layer; //!< Whether this is a layer which is part of the raft - - coord_t layer_thickness; //!< The thickness of this layer in Z-direction - - const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan - - const RetractionConfig& retraction_config; //!< The retraction settings for the extruder of this plan - - double extraTime; //!< Extra waiting time at the and of this extruder plan, so that the filament can cool - - double fan_speed; //!< The fan speed to be used during this extruder plan - - double temperatureFactor; //!< Temperature reduction factor for small layers - - /*! - * Set the fan speed to be used while printing this extruder plan - * - * \param fan_speed The speed for the fan - */ - void setFanSpeed(double fan_speed); - - /*! - * Force the minimal layer time to hold by slowing down and lifting the head if required. - * - * \param maximum_cool_min_layer_time Maximum minimum layer time for all extruders in this layer - * \param time_other_extr_plans Time spend on other extruders in this layer - */ - void forceMinimalLayerTime(double maximum_cool_min_layer_time, double time_other_extr_plans); - - /*! - * @return The time needed for (un)retract the path - */ - double getRetractTime(const GCodePath& path); - - /*! - * @return distance between p0 and p1 as well as the time spend on the segment - */ - std::pair getPointToPointTime(const Point& p0, const Point& p1, const GCodePath& path); - - /*! - * Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates. - * and store them in each ExtruderPlan and each GCodePath. - * - * \param starting_position The position the head was in before starting this layer - * \return the total estimates of this layer - */ - TimeMaterialEstimates computeNaiveTimeEstimates(Point starting_position); -}; - -class LayerPlanBuffer; // forward declaration to prevent circular dependency /*! * The LayerPlan class stores multiple moves that are planned. @@ -227,7 +49,9 @@ class LayerPlanBuffer; // forward declaration to prevent circular dependency class LayerPlan : public NoCopy { friend class LayerPlanBuffer; +#ifdef BUILD_TESTS friend class AddTravelTest; +#endif public: const PathConfigStorage configs_storage; //!< The line configs for this layer for each feature type diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index 3f97b4e274..deb173c697 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -7,6 +7,8 @@ #include "Preheat.h" #include "settings/Settings.h" #include "settings/types/Duration.h" +#include "LayerPlan.h" +#include "ExtruderPlan.h" #include #include @@ -14,8 +16,8 @@ namespace cura { -class ExtruderPlan; -class LayerPlan; + + class GCodeExport; /*! @@ -33,6 +35,8 @@ class GCodeExport; */ class LayerPlanBuffer { + friend class LayerPlan; + friend class LayerPlanBuffer; GCodeExport& gcode; Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train. diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 16985e90dc..e5118a0eec 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -1,3 +1,6 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + #ifndef TREESUPPORTTIPGENERATOR_H #define TREESUPPORTTIPGENERATOR_H @@ -25,14 +28,6 @@ class TreeSupportTipGenerator TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_); - ~ TreeSupportTipGenerator() - { - if (cross_fill_provider) - { - delete cross_fill_provider; - } - } - /*! * \brief Generate tips, that will later form branches * @@ -116,7 +111,7 @@ class TreeSupportTipGenerator * \param line_width[in] What is the width of a line used in the infill. * \return A valid CrossInfillProvider. Has to be freed manually to avoid a memory leak. */ - SierpinskiFillProvider* generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const; + std::shared_ptr generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const; /*! @@ -276,7 +271,7 @@ class TreeSupportTipGenerator /*! * \brief Required to generate cross infill patterns */ - SierpinskiFillProvider* cross_fill_provider; + std::shared_ptr cross_fill_provider; /*! * \brief Map that saves locations of already inserted tips. Used to prevent tips far to close together from being added. diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index 2f6378dc20..f2a58a16de 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -1,3 +1,6 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + #ifndef TREESUPPORTTUTILS_H #define TREESUPPORTTUTILS_H @@ -95,7 +98,7 @@ class TreeSupportUtils * todo doku * \return A Polygons object that represents the resulting infill lines. */ - [[nodiscard]] static Polygons generateSupportInfillLines(const Polygons& area,const TreeSupportSettings& config, bool roof, LayerIndex layer_idx, coord_t support_infill_distance, SierpinskiFillProvider* cross_fill_provider, bool include_walls, bool generate_support_supporting = false) + [[nodiscard]] static Polygons generateSupportInfillLines(const Polygons& area,const TreeSupportSettings& config, bool roof, LayerIndex layer_idx, coord_t support_infill_distance, std::shared_ptr cross_fill_provider, bool include_walls, bool generate_support_supporting = false) { Polygons gaps; // As we effectivly use lines to place our supportPoints we may use the Infill class for it, while not made for it, it works perfectly. diff --git a/include/infill.h b/include/infill.h index 704735e95d..df626001d6 100644 --- a/include/infill.h +++ b/include/infill.h @@ -203,8 +203,8 @@ class Infill const Settings& settings, int layer_idx, SectionType section_type, - const SierpinskiFillProvider* cross_fill_provider = nullptr, - const LightningLayer* lightning_layer = nullptr, + const std::shared_ptr cross_fill_provider = nullptr, + const std::shared_ptr lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! @@ -238,8 +238,8 @@ class Infill Polygons& result_polygons, Polygons& result_lines, const Settings& settings, - const SierpinskiFillProvider* cross_fill_pattern = nullptr, - const LightningLayer* lightning_layer = nullptr, + const std::shared_ptr cross_fill_pattern = nullptr, + const std::shared_ptr lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! @@ -358,7 +358,7 @@ class Infill * see https://hal.archives-ouvertes.fr/hal-02155929/document * \param result (output) The resulting polygons */ - void generateLightningInfill(const LightningLayer* lightning_layer, Polygons& result_lines); + void generateLightningInfill(const std::shared_ptr lightning_layer, Polygons& result_lines); /*! * Generate sparse concentric infill diff --git a/include/infill/LightningLayer.h b/include/infill/LightningLayer.h index 2ef03a4e0c..beffe612a4 100644 --- a/include/infill/LightningLayer.h +++ b/include/infill/LightningLayer.h @@ -1,11 +1,12 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef LIGHTNING_LAYER_H #define LIGHTNING_LAYER_H #include "../utils/polygonUtils.h" #include "../utils/SquareGrid.h" +#include "infill/LightningTreeNode.h" #include #include @@ -14,8 +15,6 @@ namespace cura { -class LightningTreeNode; - using LightningTreeNodeSPtr = std::shared_ptr; using SparseLightningTreeNodeGrid = SparsePointGridInclusive>; diff --git a/include/infill/LightningTreeNode.h b/include/infill/LightningTreeNode.h index b24f1e37c5..1a36729468 100644 --- a/include/infill/LightningTreeNode.h +++ b/include/infill/LightningTreeNode.h @@ -1,5 +1,5 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef LIGHTNING_TREE_NODE_H #define LIGHTNING_TREE_NODE_H @@ -17,10 +17,6 @@ namespace cura constexpr coord_t locator_cell_size = 4000; -class LightningTreeNode; - -using LightningTreeNodeSPtr = std::shared_ptr; - // NOTE: As written, this struct will only be valid for a single layer, will have to be updated for the next. // NOTE: Reasons for implementing this with some separate closures: // - keep clear deliniation during development @@ -37,6 +33,7 @@ using LightningTreeNodeSPtr = std::shared_ptr; */ class LightningTreeNode : public std::enable_shared_from_this { + using LightningTreeNodeSPtr = std::shared_ptr; public: // Workaround for private/protected constructors and 'make_shared': https://stackoverflow.com/a/27832765 template LightningTreeNodeSPtr static create(Arg&&...arg) diff --git a/include/infill/SubDivCube.h b/include/infill/SubDivCube.h index 4b1b31ef52..d2c7d60f16 100644 --- a/include/infill/SubDivCube.h +++ b/include/infill/SubDivCube.h @@ -1,5 +1,5 @@ -// Copyright (c) 2018 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef INFILL_SUBDIVCUBE_H #define INFILL_SUBDIVCUBE_H @@ -26,8 +26,6 @@ class SubDivCube */ SubDivCube(SliceMeshStorage& mesh, Point3& center, size_t depth); - ~SubDivCube(); //!< destructor (also destroys children) - /*! * Precompute the octree of subdivided cubes * \param mesh contains infill layer data and settings @@ -98,7 +96,7 @@ class SubDivCube size_t depth; //!< the recursion depth of the cube (0 is most recursed) Point3 center; //!< center location of the cube in absolute coordinates - SubDivCube* children[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; //!< pointers to this cube's eight octree children + std::array, 8> children; //!< pointers to this cube's eight octree children static std::vector cube_properties_per_recursion_step; //!< precomputed array of basic properties of cubes based on recursion depth. static Ratio radius_multiplier; //!< multiplier for the bounding radius when determining if a cube should be subdivided static Point3Matrix rotation_matrix; //!< The rotation matrix to get from axis aligned cubes to cubes standing on a corner point aligned with the infill_angle diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index b34c722fbf..b4f674e2e3 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -1,11 +1,12 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef SLICE_DATA_STORAGE_H #define SLICE_DATA_STORAGE_H #include #include +#include #include "PrimeTower.h" #include "RetractionConfig.h" @@ -231,7 +232,7 @@ class SupportStorage std::vector support_bottom_angles; //!< a list of angle values which is cycled through to determine the infill angle of each layer std::vector supportLayers; - SierpinskiFillProvider* cross_fill_provider; //!< the fractal pattern for the cross (3d) filling pattern + std::shared_ptr cross_fill_provider; //!< the fractal pattern for the cross (3d) filling pattern SupportStorage(); ~SupportStorage(); @@ -257,10 +258,10 @@ class SliceMeshStorage std::vector> overhang_points; //!< For each layer a list of points where point-overhang is detected. This is overhang that hasn't got any surface area, such as a corner pointing downwards. AABB3D bounding_box; //!< the mesh's bounding box - SubDivCube* base_subdiv_cube; - SierpinskiFillProvider* cross_fill_provider; //!< the fractal pattern for the cross (3d) filling pattern + std::shared_ptr base_subdiv_cube; + std::shared_ptr cross_fill_provider; //!< the fractal pattern for the cross (3d) filling pattern - LightningGenerator* lightning_generator; //!< Pre-computed structure for Lightning type infill + std::shared_ptr lightning_generator; //!< Pre-computed structure for Lightning type infill RetractionAndWipeConfig retraction_wipe_config; //!< Per-Object retraction and wipe settings. @@ -273,8 +274,6 @@ class SliceMeshStorage */ SliceMeshStorage(Mesh* mesh, const size_t slice_layer_count); - virtual ~SliceMeshStorage(); - /*! * \param extruder_nr The extruder for which to check * \return whether a particular extruder is used by this mesh diff --git a/src/ExtruderPlan.cpp b/src/ExtruderPlan.cpp new file mode 100644 index 0000000000..1b59ff7af4 --- /dev/null +++ b/src/ExtruderPlan.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#include "ExtruderPlan.h" + +namespace cura +{ +ExtruderPlan::ExtruderPlan( + const size_t extruder, + const LayerIndex layer_nr, + const bool is_initial_layer, + const bool is_raft_layer, + const coord_t layer_thickness, + const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, + const RetractionConfig& retraction_config) + : extruder_nr(extruder) + , layer_nr(layer_nr) + , is_initial_layer(is_initial_layer) + , is_raft_layer(is_raft_layer) + , layer_thickness(layer_thickness) + , fan_speed_layer_time_settings(fan_speed_layer_time_settings) + , retraction_config(retraction_config) +{ +} + +void ExtruderPlan::insertCommand(NozzleTempInsert&& insert) +{ + inserts.emplace_back(insert); +} + +void ExtruderPlan::handleInserts(const size_t path_idx, GCodeExport& gcode, const double cumulative_path_time) +{ + while (! inserts.empty() && path_idx >= inserts.front().path_idx && inserts.front().time_after_path_start < cumulative_path_time) + { // handle the Insert to be inserted before this path_idx (and all inserts not handled yet) + inserts.front().write(gcode); + inserts.pop_front(); + } +} + +void ExtruderPlan::handleAllRemainingInserts(GCodeExport& gcode) +{ + while (! inserts.empty()) + { // handle the Insert to be inserted before this path_idx (and all inserts not handled yet) + NozzleTempInsert& insert = inserts.front(); + insert.write(gcode); + inserts.pop_front(); + } +} + +void ExtruderPlan::setFanSpeed(double _fan_speed) +{ + fan_speed = _fan_speed; +} +double ExtruderPlan::getFanSpeed() +{ + return fan_speed; +} + +void ExtruderPlan::applyBackPressureCompensation(const Ratio back_pressure_compensation) +{ + constexpr double epsilon_speed_factor = 0.001; // Don't put on actual 'limit double minimum', because we don't want printers to stall. + for (auto& path : paths) + { + const double nominal_width_for_path = static_cast(path.config.getLineWidth()); + if (path.width_factor <= 0.0 || nominal_width_for_path <= 0.0 || path.config.isTravelPath() || path.config.isBridgePath()) + { + continue; + } + const double line_width_for_path = path.width_factor * nominal_width_for_path; + path.speed_back_pressure_factor = std::max(epsilon_speed_factor, 1.0 + (nominal_width_for_path / line_width_for_path - 1.0) * back_pressure_compensation); + } +} +} // namespace cura \ No newline at end of file diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index aceb1767b9..320d3843f3 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1624,10 +1624,10 @@ bool FffGcodeWriter::processMultiLayerInfill( constexpr size_t zag_skip_count = 0; const bool fill_gaps = density_idx == 0; // Only fill gaps for the lowest density. - const LightningLayer* lightning_layer = nullptr; + std::shared_ptr lightning_layer = nullptr; if (mesh.lightning_generator) { - lightning_layer = &mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr()); + lightning_layer = std::make_shared(mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr())); } Infill infill_comp( infill_pattern, @@ -1820,10 +1820,10 @@ bool FffGcodeWriter::processSingleLayerInfill( Polygons in_outline = part.infill_area_per_combine_per_density[density_idx][0]; - const LightningLayer* lightning_layer = nullptr; + std::shared_ptr lightning_layer; if (mesh.lightning_generator) { - lightning_layer = &mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr()); + lightning_layer = std::make_shared(mesh.lightning_generator->getTreesForLayer(gcode_layer.getLayerNr())); } const bool fill_gaps = density_idx == 0; // Only fill gaps in the lowest infill density pattern. diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 39648aafcf..f60b3f5768 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -684,7 +684,7 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) std::ifstream cross_fs(cross_subdivision_spec_image_file.c_str()); if (! cross_subdivision_spec_image_file.empty() && cross_fs.good()) { - mesh.cross_fill_provider = new SierpinskiFillProvider( + mesh.cross_fill_provider = std::make_shared( mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width"), @@ -697,7 +697,7 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) spdlog::error("Cannot find density image: {}.", cross_subdivision_spec_image_file); } mesh.cross_fill_provider - = new SierpinskiFillProvider(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width")); + = std::make_shared(mesh.bounding_box, mesh.settings.get("infill_line_distance"), mesh.settings.get("infill_line_width")); } } @@ -705,7 +705,7 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh) if (mesh.settings.get("infill_line_distance") > 0 && mesh.settings.get("infill_pattern") == EFillMethod::LIGHTNING) { // TODO: Make all of these into new type pointers (but the cross fill things need to happen too then, otherwise it'd just look weird). - mesh.lightning_generator = new LightningGenerator(mesh); + mesh.lightning_generator = std::make_shared(mesh); } // combine infill diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index f4ed218f36..31873f8608 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -33,71 +33,6 @@ 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; -ExtruderPlan::ExtruderPlan( - const size_t extruder, - const LayerIndex layer_nr, - const bool is_initial_layer, - const bool is_raft_layer, - const coord_t layer_thickness, - const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, - const RetractionConfig& retraction_config) - : heated_pre_travel_time(0) - , required_start_temperature(-1) - , extruder_nr(extruder) - , layer_nr(layer_nr) - , is_initial_layer(is_initial_layer) - , is_raft_layer(is_raft_layer) - , layer_thickness(layer_thickness) - , fan_speed_layer_time_settings(fan_speed_layer_time_settings) - , retraction_config(retraction_config) - , extraTime(0.0) - , temperatureFactor(0.0) - , slowest_path_speed(0.0) -{ -} - -void ExtruderPlan::handleInserts(const size_t path_idx, GCodeExport& gcode, const double& cumulative_path_time) -{ - while (! inserts.empty() && path_idx >= inserts.front().path_idx && inserts.front().time_after_path_start < cumulative_path_time) - { // handle the Insert to be inserted before this path_idx (and all inserts not handled yet) - inserts.front().write(gcode); - inserts.pop_front(); - } -} - -void ExtruderPlan::handleAllRemainingInserts(GCodeExport& gcode) -{ - while (! inserts.empty()) - { // handle the Insert to be inserted before this path_idx (and all inserts not handled yet) - NozzleTempInsert& insert = inserts.front(); - insert.write(gcode); - inserts.pop_front(); - } -} - -void ExtruderPlan::setFanSpeed(double _fan_speed) -{ - fan_speed = _fan_speed; -} -double ExtruderPlan::getFanSpeed() -{ - return fan_speed; -} - -void ExtruderPlan::applyBackPressureCompensation(const Ratio back_pressure_compensation) -{ - constexpr double epsilon_speed_factor = 0.001; // Don't put on actual 'limit double minimum', because we don't want printers to stall. - for (auto& path : paths) - { - const double nominal_width_for_path = static_cast(path.config.getLineWidth()); - if (path.width_factor <= 0.0 || nominal_width_for_path <= 0.0 || path.config.isTravelPath() || path.config.isBridgePath()) - { - continue; - } - const double line_width_for_path = path.width_factor * nominal_width_for_path; - path.speed_back_pressure_factor = std::max(epsilon_speed_factor, 1.0 + (nominal_width_for_path / line_width_for_path - 1.0) * back_pressure_compensation); - } -} GCodePath* LayerPlan::getLatestPathWithConfig( const GCodePathConfig& config, diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index b0ddb5a41b..6544e8b799 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -378,7 +378,7 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& } -SierpinskiFillProvider* TreeSupportTipGenerator::generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const +std::shared_ptr TreeSupportTipGenerator::generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const { if (config.support_pattern == EFillMethod::CROSS || config.support_pattern == EFillMethod::CROSS_3D) { @@ -399,12 +399,9 @@ SierpinskiFillProvider* TreeSupportTipGenerator::generateCrossFillProvider(const std::ifstream cross_fs(cross_subdisivion_spec_image_file.c_str()); if (cross_subdisivion_spec_image_file != "" && cross_fs.good()) { - return new SierpinskiFillProvider(aabb, line_distance, line_width, cross_subdisivion_spec_image_file); - } - else - { - return new SierpinskiFillProvider(aabb, line_distance, line_width); + return std::make_shared(aabb, line_distance, line_width, cross_subdisivion_spec_image_file); } + return std::make_shared(aabb, line_distance, line_width); } return nullptr; } diff --git a/src/infill.cpp b/src/infill.cpp index 622ba99229..f22a9756e7 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -86,8 +86,8 @@ void Infill::generate( const Settings& settings, int layer_idx, SectionType section_type, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, + const std::shared_ptr cross_fill_provider, + const std::shared_ptr lightning_trees, const SliceMeshStorage* mesh) { if (outer_contour.empty()) @@ -250,8 +250,8 @@ void Infill::_generate( Polygons& result_polygons, Polygons& result_lines, const Settings& settings, - const SierpinskiFillProvider* cross_fill_provider, - const LightningLayer* lightning_trees, + const std::shared_ptr cross_fill_provider, + const std::shared_ptr lightning_trees, const SliceMeshStorage* mesh) { if (inner_contour.empty()) @@ -427,7 +427,7 @@ void Infill::generateGyroidInfill(Polygons& result_lines, Polygons& result_polyg PolylineStitcher::stitch(line_segments, result_lines, result_polygons, infill_line_width); } -void Infill::generateLightningInfill(const LightningLayer* trees, Polygons& result_lines) +void Infill::generateLightningInfill(const std::shared_ptr trees, Polygons& result_lines) { // Don't need to support areas smaller than line width, as they are always within radius: if (std::abs(inner_contour.area()) < infill_line_width || ! trees) diff --git a/src/infill/LightningTreeNode.cpp b/src/infill/LightningTreeNode.cpp index 33af29678c..9766c6fcb2 100644 --- a/src/infill/LightningTreeNode.cpp +++ b/src/infill/LightningTreeNode.cpp @@ -1,5 +1,5 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #include "infill/LightningTreeNode.h" @@ -7,6 +7,8 @@ using namespace cura; +using LightningTreeNodeSPtr = std::shared_ptr; + coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const { constexpr coord_t min_valence_for_boost = 0; diff --git a/src/infill/SubDivCube.cpp b/src/infill/SubDivCube.cpp index 87478fa204..d81b92d0e0 100644 --- a/src/infill/SubDivCube.cpp +++ b/src/infill/SubDivCube.cpp @@ -23,17 +23,6 @@ coord_t SubDivCube::radius_addition = 0; Point3Matrix SubDivCube::rotation_matrix; PointMatrix SubDivCube::infill_rotation_matrix; -SubDivCube::~SubDivCube() -{ - for (int child_idx = 0; child_idx < 8; child_idx++) - { - if (children[child_idx]) - { - delete children[child_idx]; - } - } -} - void SubDivCube::precomputeOctree(SliceMeshStorage& mesh, const Point& infill_origin) { radius_addition = mesh.settings.get("sub_div_rad_add"); @@ -91,7 +80,7 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh, const Point& infill_or rotation_matrix = infill_angle_mat.compose(tilt); - mesh.base_subdiv_cube = new SubDivCube(mesh, center, curr_recursion_depth - 1); + mesh.base_subdiv_cube = std::make_shared(mesh, center, curr_recursion_depth - 1); } void SubDivCube::generateSubdivisionLines(const coord_t z, Polygons& result) @@ -189,7 +178,7 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, size_t depth) child_center = center + rotation_matrix.apply(rel_child_center * int32_t(cube_properties.side_length / 4)); if (isValidSubdivision(mesh, child_center, radius)) { - children[child_nr] = new SubDivCube(mesh, child_center, depth - 1); + children[child_nr] = std::make_shared(mesh, child_center, depth - 1); child_nr++; } } diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 6dfe67a5e2..4e89ea5625 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -26,10 +26,6 @@ SupportStorage::SupportStorage() : generated(false), layer_nr_max_filled_layer(- SupportStorage::~SupportStorage() { supportLayers.clear(); - if (cross_fill_provider) - { - delete cross_fill_provider; - } } Polygons& SliceLayerPart::getOwnInfillArea() @@ -102,21 +98,6 @@ SliceMeshStorage::SliceMeshStorage(Mesh* mesh, const size_t slice_layer_count) layers.resize(slice_layer_count); } -SliceMeshStorage::~SliceMeshStorage() -{ - if (base_subdiv_cube) - { - delete base_subdiv_cube; - } - if (cross_fill_provider) - { - delete cross_fill_provider; - } - if (lightning_generator) - { - delete lightning_generator; - } -} bool SliceMeshStorage::getExtruderIsUsed(const size_t extruder_nr) const { diff --git a/src/support.cpp b/src/support.cpp index 45f9901d77..f1c1bc6bd6 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -749,7 +749,7 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) std::ifstream cross_fs(cross_subdisivion_spec_image_file.c_str()); if (cross_subdisivion_spec_image_file != "" && cross_fs.good()) { - storage.support.cross_fill_provider = new SierpinskiFillProvider( + storage.support.cross_fill_provider = std::make_shared( aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width"), @@ -762,7 +762,7 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) spdlog::error("Cannot find density image: {}.", cross_subdisivion_spec_image_file); } storage.support.cross_fill_provider - = new SierpinskiFillProvider(aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width")); + = std::make_shared(aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width")); } } } From 89c13338092b5e25a27029a855bdda693d5ec295 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 23 Aug 2023 14:21:57 +0000 Subject: [PATCH 460/656] Applied clang-format. --- include/LayerPlan.h | 2 +- include/LayerPlanBuffer.h | 5 +- include/TreeSupportTipGenerator.h | 56 ++++++++----- include/TreeSupportUtils.h | 124 ++++++++++++++--------------- include/infill/LightningLayer.h | 24 +++--- include/infill/LightningTreeNode.h | 40 ++++++---- include/sliceDataStorage.h | 64 ++++++++------- src/TreeSupportTipGenerator.cpp | 2 +- src/infill/LightningTreeNode.cpp | 55 +++++-------- src/sliceDataStorage.cpp | 45 +++++++---- src/support.cpp | 6 +- 11 files changed, 217 insertions(+), 206 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 9a8be087f1..3eda752178 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -4,6 +4,7 @@ #ifndef LAYER_PLAN_H #define LAYER_PLAN_H +#include "ExtruderPlan.h" #include "FanSpeedLayerTime.h" #include "InsetOrderOptimizer.h" #include "PathOrderOptimizer.h" @@ -16,7 +17,6 @@ #include "settings/types/LayerIndex.h" #include "utils/ExtrusionJunction.h" #include "utils/polygon.h" -#include "ExtruderPlan.h" #ifdef BUILD_TESTS #include //Friend tests, so that they can inspect the privates. diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index deb173c697..721a003ba5 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -4,11 +4,11 @@ #ifndef LAYER_PLAN_BUFFER_H #define LAYER_PLAN_BUFFER_H +#include "ExtruderPlan.h" +#include "LayerPlan.h" #include "Preheat.h" #include "settings/Settings.h" #include "settings/types/Duration.h" -#include "LayerPlan.h" -#include "ExtruderPlan.h" #include #include @@ -17,7 +17,6 @@ namespace cura { - class GCodeExport; /*! diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index e5118a0eec..d19e1a99b0 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -5,6 +5,7 @@ #define TREESUPPORTTIPGENERATOR_H #include "TreeModelVolumes.h" +#include "TreeSupport.h" #include "TreeSupportBaseCircle.h" #include "TreeSupportElement.h" #include "TreeSupportEnums.h" @@ -15,7 +16,6 @@ #include "sliceDataStorage.h" #include "utils/Coord_t.h" #include "utils/polygon.h" -#include "TreeSupport.h" namespace cura { @@ -23,9 +23,7 @@ namespace cura class TreeSupportTipGenerator { - public: - TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_); /*! @@ -38,10 +36,14 @@ class TreeSupportTipGenerator * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. */ - void generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh ,std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas); + void generateTips( + SliceDataStorage& storage, + const SliceMeshStorage& mesh, + std::vector>& move_bounds, + std::vector& additional_support_areas, + std::vector& placed_support_lines_support_areas); private: - enum class LineStatus { INVALID, @@ -80,17 +82,16 @@ class TreeSupportTipGenerator std::function)> getEvaluatePointForNextLayerFunction(size_t current_layer); /*! - * \brief Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using supplied lambda. + * \brief Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using + * supplied lambda. * * \param lines[in] The lines that have to be evaluated. * \param evaluatePoint[in] The function used to evaluate the points. * \return A pair with which points are still valid in the first slot and which are not in the second slot. */ - std::pair, std::vector> splitLines - ( - std::vector lines, - std::function)> evaluatePoint - ); // assumes all Points on the current line are valid + std::pair, std::vector> splitLines( + std::vector lines, + std::function)> evaluatePoint); // assumes all Points on the current line are valid /*! * \brief Ensures that every line segment is about distance in length. The resulting lines may differ from the original but all points are on the original @@ -120,7 +121,7 @@ class TreeSupportTipGenerator * \param result[out] The dropped overhang ares * \param roof[in] Whether the result is for roof generation. */ - void dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof ); + void dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof); /*! * \brief Calculates which areas should be supported with roof, and saves these in roof support_roof_drawn @@ -138,7 +139,15 @@ class TreeSupportTipGenerator * \param roof[in] Whether the tip supports a roof. * \param skip_ovalisation[in] Whether the tip may be ovalized when drawn later. */ - void addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); + void addPointAsInfluenceArea( + std::vector>& move_bounds, + std::pair p, + size_t dtt, + LayerIndex insert_layer, + size_t dont_move_until, + bool roof, + bool skip_ovalisation, + std::vector additional_ovalization_targets = std::vector()); /*! @@ -150,7 +159,14 @@ class TreeSupportTipGenerator * \param supports_roof[in] Whether the tip supports a roof. * \param dont_move_until[in] Until which dtt the branch should not move if possible. */ - void addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, size_t dont_move_until, bool connect_points); + void addLinesAsInfluenceAreas( + std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + bool supports_roof, + size_t dont_move_until, + bool connect_points); /*! * \brief Remove tips that should not have been added in the first place. @@ -158,7 +174,7 @@ class TreeSupportTipGenerator * \param storage[in] Background storage, required for adding roofs. * \param additional_support_areas[in] Areas that should have been roofs, but are now support, as they would not generate any lines as roof. */ - void removeUselessAddedPoints(std::vector>& move_bounds,SliceDataStorage& storage, std::vector& additional_support_areas); + void removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage, std::vector& additional_support_areas); /*! @@ -259,7 +275,8 @@ class TreeSupportTipGenerator const bool only_gracious = SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL; /*! - * \brief Whether minimum_roof_area is a hard limit. If false the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof further up/down. + * \brief Whether minimum_roof_area is a hard limit. If false the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof + * further up/down. */ const bool force_minimum_roof_area = SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT; @@ -289,15 +306,10 @@ class TreeSupportTipGenerator std::vector roof_tips_drawn; - - std::mutex critical_move_bounds; std::mutex critical_roof_tips; - - - }; -} +} // namespace cura #endif /* TREESUPPORT_H */ \ No newline at end of file diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index f2a58a16de..fd6ab60526 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -10,21 +10,20 @@ #include "TreeSupportEnums.h" #include "TreeSupportSettings.h" #include "boost/functional/hash.hpp" // For combining hashes +#include "infill.h" #include "polyclipping/clipper.hpp" #include "settings/EnumSettings.h" #include "sliceDataStorage.h" #include "utils/Coord_t.h" #include "utils/polygon.h" + #include -#include "infill.h" namespace cura { class TreeSupportUtils { - - public: /*! * \brief Adds the implicit line from the last vertex of a Polygon to the first one. @@ -84,7 +83,6 @@ class TreeSupportUtils } - /*! * \brief Returns Polylines representing the (infill) lines that will result in slicing the given area * @@ -98,7 +96,15 @@ class TreeSupportUtils * todo doku * \return A Polygons object that represents the resulting infill lines. */ - [[nodiscard]] static Polygons generateSupportInfillLines(const Polygons& area,const TreeSupportSettings& config, bool roof, LayerIndex layer_idx, coord_t support_infill_distance, std::shared_ptr cross_fill_provider, bool include_walls, bool generate_support_supporting = false) + [[nodiscard]] static Polygons generateSupportInfillLines( + const Polygons& area, + const TreeSupportSettings& config, + bool roof, + LayerIndex layer_idx, + coord_t support_infill_distance, + std::shared_ptr cross_fill_provider, + bool include_walls, + bool generate_support_supporting = false) { Polygons gaps; // As we effectivly use lines to place our supportPoints we may use the Infill class for it, while not made for it, it works perfectly. @@ -110,7 +116,7 @@ class TreeSupportUtils constexpr coord_t support_roof_overlap = 0; constexpr size_t infill_multiplier = 1; const int support_shift = roof ? 0 : support_infill_distance / 2; - const size_t wall_line_count = include_walls ? (!roof ? config.support_wall_count : config.support_roof_wall_count):0; + const size_t wall_line_count = include_walls ? (! roof ? config.support_wall_count : config.support_roof_wall_count) : 0; constexpr coord_t narrow_area_width = 0; const Point infill_origin; constexpr bool skip_stitching = false; @@ -127,32 +133,30 @@ class TreeSupportUtils const size_t divisor = angles.size(); const size_t index = ((layer_idx % divisor) + divisor) % divisor; const AngleDegrees fill_angle = angles[index]; - Infill roof_computation - ( - pattern, - zig_zaggify_infill, - connect_polygons, - area, - roof ? config.support_roof_line_width : config.support_line_width, - support_infill_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - z, - support_shift, - config.maximum_resolution, - config.maximum_deviation, - wall_line_count, - narrow_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size - ); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + area, + roof ? config.support_roof_line_width : config.support_line_width, + support_infill_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + z, + support_shift, + config.maximum_resolution, + config.maximum_deviation, + wall_line_count, + narrow_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); Polygons areas; Polygons lines; @@ -171,8 +175,8 @@ class TreeSupportUtils [[nodiscard]] static Polygons safeUnion(const Polygons& first, const Polygons& second = Polygons()) { // The unionPolygons function can slowly remove Polygons under certain circumstances, because of rounding issues (Polygons that have a thin area). - // This does not cause a problem when actually using it on large areas, but as influence areas (representing centerpoints) can be very thin, this does occur so this ugly workaround is needed - // Here is an example of a Polygons object that will loose vertices when unioning, and will be gone after a few times unionPolygons was called: + // This does not cause a problem when actually using it on large areas, but as influence areas (representing centerpoints) can be very thin, this does occur so this ugly + // workaround is needed Here is an example of a Polygons object that will loose vertices when unioning, and will be gone after a few times unionPolygons was called: /* 120410,83599 120384,83643 @@ -201,14 +205,12 @@ class TreeSupportUtils * \param distance[in] The distance by which me should be offset. Expects values >=0. * \param collision[in] The area representing obstacles. * \param last_step_offset_without_check[in] The most it is allowed to offset in one step. - * \param min_amount_offset[in] How many steps have to be done at least. As this uses round offset this increases the amount of vertices, which may be required if Polygons get very small. - * Required as arcTolerance is not exposed in offset, which should result with a similar result, benefit may be eliminated by simplifying. - * \param min_offset_per_step Don't get below this amount of offset per step taken. Fine-tune tradeoff between speed and accuracy. - * \param simplifier[in] Pointer to Simplify object if the offset operation also simplify the Polygon. Improves performance. - * \return The resulting Polygons object. + * \param min_amount_offset[in] How many steps have to be done at least. As this uses round offset this increases the amount of vertices, which may be required if Polygons get + * very small. Required as arcTolerance is not exposed in offset, which should result with a similar result, benefit may be eliminated by simplifying. \param + * min_offset_per_step Don't get below this amount of offset per step taken. Fine-tune tradeoff between speed and accuracy. \param simplifier[in] Pointer to Simplify object if + * the offset operation also simplify the Polygon. Improves performance. \return The resulting Polygons object. */ - [[nodiscard]] static Polygons safeOffsetInc - ( + [[nodiscard]] static Polygons safeOffsetInc( const Polygons& me, coord_t distance, const Polygons& collision, @@ -216,8 +218,7 @@ class TreeSupportUtils coord_t last_step_offset_without_check, size_t min_amount_offset, coord_t min_offset_per_step, - Simplify* simplifier - ) + Simplify* simplifier) { bool do_final_difference = last_step_offset_without_check == 0; Polygons ret = safeUnion(me); // Ensure sane input. @@ -270,7 +271,7 @@ class TreeSupportUtils } } ret = ret.offset(distance - steps * step_size, ClipperLib::jtRound); // Offset the remainder. - if(simplifier) + if (simplifier) { ret = simplifier->polygon(ret); } @@ -287,31 +288,30 @@ class TreeSupportUtils * * \param polylines[in] The polyline object from which the lines are moved. * \param area[in] The area the points are moved out of. - * \param max_allowed_distance[in] The maximum distance a point may be moved. If not possible the point will be moved as far as possible in the direction of the outside of the provided area. - * \return A Polyline object containing the moved points. + * \param max_allowed_distance[in] The maximum distance a point may be moved. If not possible the point will be moved as far as possible in the direction of the outside of the + * provided area. \return A Polyline object containing the moved points. */ - [[nodiscard]]static Polygons movePointsOutside(const Polygons& polylines, const Polygons& area, coord_t max_allowed_distance) + [[nodiscard]] static Polygons movePointsOutside(const Polygons& polylines, const Polygons& area, coord_t max_allowed_distance) { Polygons result; - for (auto line:polylines) + for (auto line : polylines) { Polygon next_line; - for (Point p:line) + for (Point p : line) { - if (area.inside(p)) { Point next_outside = p; - PolygonUtils::moveOutside(area,next_outside); - if (vSize2(p-next_outside)0) + if (next_line.size() > 0) { result.add(next_line); } @@ -329,25 +329,23 @@ class TreeSupportUtils return result; } - [[nodiscard]]static VariableWidthLines polyLineToVWL(const Polygons& polylines, coord_t line_width) + [[nodiscard]] static VariableWidthLines polyLineToVWL(const Polygons& polylines, coord_t line_width) { VariableWidthLines result; - for (auto path: polylines) + for (auto path : polylines) { - ExtrusionLine vwl_line(1,true); + ExtrusionLine vwl_line(1, true); - for(Point p: path) + for (Point p : path) { - vwl_line.emplace_back(p,line_width,1); + vwl_line.emplace_back(p, line_width, 1); } result.emplace_back(vwl_line); } return result; } - - }; -} //namespace cura +} // namespace cura #endif // TREESUPPORTTUTILS_H diff --git a/include/infill/LightningLayer.h b/include/infill/LightningLayer.h index beffe612a4..64540ea896 100644 --- a/include/infill/LightningLayer.h +++ b/include/infill/LightningLayer.h @@ -4,14 +4,14 @@ #ifndef LIGHTNING_LAYER_H #define LIGHTNING_LAYER_H -#include "../utils/polygonUtils.h" #include "../utils/SquareGrid.h" +#include "../utils/polygonUtils.h" #include "infill/LightningTreeNode.h" -#include -#include #include +#include #include +#include namespace cura { @@ -35,28 +35,24 @@ class LightningLayer public: std::vector tree_roots; - void generateNewTrees - ( + void generateNewTrees( const Polygons& current_overhang, const Polygons& current_outlines, const LocToLineGrid& outline_locator, const coord_t supporting_radius, - const coord_t wall_supporting_radius - ); + const coord_t wall_supporting_radius); /*! Determine & connect to connection point in tree/outline. * \param min_dist_from_boundary_for_tree If the unsupported point is closer to the boundary than this then don't consider connecting it to a tree */ - GroundingLocation getBestGroundingLocation - ( + GroundingLocation getBestGroundingLocation( const Point& unsupported_location, const Polygons& current_outlines, const LocToLineGrid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius, const SparseLightningTreeNodeGrid& tree_node_locator, - const LightningTreeNodeSPtr& exclude_tree = nullptr - ); + const LightningTreeNodeSPtr& exclude_tree = nullptr); /*! * \param[out] new_child The new child node introduced @@ -65,14 +61,12 @@ class LightningLayer */ bool attach(const Point& unsupported_location, const GroundingLocation& ground, LightningTreeNodeSPtr& new_child, LightningTreeNodeSPtr& new_root); - void reconnectRoots - ( + void reconnectRoots( std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, const LocToLineGrid& outline_locator, const coord_t supporting_radius, - const coord_t wall_supporting_radius - ); + const coord_t wall_supporting_radius); Polygons convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; diff --git a/include/infill/LightningTreeNode.h b/include/infill/LightningTreeNode.h index 1a36729468..21c63bde3b 100644 --- a/include/infill/LightningTreeNode.h +++ b/include/infill/LightningTreeNode.h @@ -4,14 +4,14 @@ #ifndef LIGHTNING_TREE_NODE_H #define LIGHTNING_TREE_NODE_H +#include "../utils/polygon.h" +#include "../utils/polygonUtils.h" + #include #include #include #include -#include "../utils/polygonUtils.h" -#include "../utils/polygon.h" - namespace cura { @@ -34,13 +34,18 @@ constexpr coord_t locator_cell_size = 4000; class LightningTreeNode : public std::enable_shared_from_this { using LightningTreeNodeSPtr = std::shared_ptr; + public: // Workaround for private/protected constructors and 'make_shared': https://stackoverflow.com/a/27832765 - template LightningTreeNodeSPtr static create(Arg&&...arg) + template + LightningTreeNodeSPtr static create(Arg&&... arg) { struct EnableMakeShared : public LightningTreeNode { - EnableMakeShared(Arg&&...arg) : LightningTreeNode(std::forward(arg)...) {} + EnableMakeShared(Arg&&... arg) + : LightningTreeNode(std::forward(arg)...) + { + } }; return std::make_shared(std::forward(arg)...); } @@ -91,15 +96,13 @@ class LightningTreeNode : public std::enable_shared_from_this * \param max_remove_colinear_dist The maximum distance of a line-segment * from which straightening may remove a colinear point. */ - void propagateToNextLayer - ( + void propagateToNextLayer( std::vector& next_trees, const Polygons& next_outlines, const LocToLineGrid& outline_locator, const coord_t prune_distance, const coord_t smooth_magnitude, - const coord_t max_remove_colinear_dist - ) const; + const coord_t max_remove_colinear_dist) const; /*! * Executes a given function for every line segment in this node's sub-tree. @@ -144,7 +147,10 @@ class LightningTreeNode : public std::enable_shared_from_this * \return ``true`` if this node is the root (no parents) or ``false`` if it * is a child node of some other node. */ - bool isRoot() const { return is_root; } + bool isRoot() const + { + return is_root; + } /*! * Reverse the parent-child relationship all the way to the root, from this node onward. @@ -226,11 +232,11 @@ class LightningTreeNode : public std::enable_shared_from_this public: /*! * Convert the tree into polylines - * + * * At each junction one line is chosen at random to continue - * + * * The lines start at a leaf and end in a junction - * + * * \param output all branches in this tree connected into polylines */ void convertToPolylines(Polygons& output, const coord_t line_width) const; @@ -244,11 +250,11 @@ class LightningTreeNode : public std::enable_shared_from_this protected: /*! * Convert the tree into polylines - * + * * At each junction one line is chosen at random to continue - * + * * The lines start at a leaf and end in a junction - * + * * \param long_line a reference to a polyline in \p output which to continue building on in the recursion * \param output all branches in this tree connected into polylines */ @@ -261,7 +267,7 @@ class LightningTreeNode : public std::enable_shared_from_this std::weak_ptr parent; std::vector children; - std::optional last_grounding_location; // last_grounding_location; // -#include -#include - #include "PrimeTower.h" #include "RetractionConfig.h" #include "SupportInfillPart.h" #include "TopSurface.h" +#include "WipeScriptConfig.h" #include "settings/Settings.h" //For MAX_EXTRUDERS. #include "settings/types/Angle.h" //Infill angles. #include "settings/types/LayerIndex.h" @@ -20,7 +17,10 @@ #include "utils/IntPoint.h" #include "utils/NoCopy.h" #include "utils/polygon.h" -#include "WipeScriptConfig.h" + +#include +#include +#include // libArachne #include "utils/ExtrusionLine.h" @@ -33,14 +33,15 @@ class SierpinskiFillProvider; class LightningGenerator; /*! - * A SkinPart is a connected area designated as top and/or bottom skin. + * A SkinPart is a connected area designated as top and/or bottom skin. * Surrounding each non-bridged skin area with an outline may result in better top skins. * It's filled during FffProcessor.processSliceData(.) and used in FffProcessor.writeGCode(.) to generate the final gcode. */ class SkinPart { public: - PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module. Includes both roofing and non-roofing. + PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module. Includes both + //!< roofing and non-roofing. Polygons skin_fill; //!< The part of the skin which is not roofing. Polygons roofing_fill; //!< The inner infill which has air directly above Polygons top_most_surface_fill; //!< The inner infill of the uppermost top layer which has air directly above. @@ -65,7 +66,7 @@ class SliceLayerPart //!< Too small parts will be omitted compared to the outline. Polygons spiral_wall; //!< The centerline of the wall used by spiralize mode. Only computed if spiralize mode is enabled. Polygons inner_area; //!< The area of the outline, minus the walls. This will be filled with either skin or infill. - std::vector skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets. + std::vector skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets. std::vector wall_toolpaths; //!< toolpaths for walls, will replace(?) the insets. Binned by inset_idx. std::vector infill_wall_toolpaths; //!< toolpaths for the walls of the infill areas. Binned by inset_idx. @@ -126,7 +127,7 @@ class SliceLayerPart * This maximum number of layers we can combine is a user setting. This number, say "n", means the maximum number of layers we can combine into one. * On the combined layers, the extrusion amount will be higher than the normal extrusion amount because it needs to extrude for multiple layers instead of one. * - * infill_area[x][n] is infill_area of (n+1) layers thick. + * infill_area[x][n] is infill_area of (n+1) layers thick. * * infill_area[0] corresponds to the most dense infill area. * infill_area[x] will lie fully inside infill_area[x+1]. @@ -162,9 +163,9 @@ class SliceLayerPart class SliceLayer { public: - coord_t printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft. - coord_t thickness; //!< The thickness of this layer. Can be different when using variable layer heights. - std::vector parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model. + coord_t printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft. + coord_t thickness; //!< The thickness of this layer. Can be different when using variable layer heights. + std::vector parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model. Polygons openPolyLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation) /*! @@ -177,7 +178,7 @@ class SliceLayer /*! * Get the all outlines of all layer parts in this layer. - * + * * \param external_polys_only Whether to only include the outermost outline of each layer part * \return A collection of all the outline polygons */ @@ -186,7 +187,7 @@ class SliceLayer /*! * Get the all outlines of all layer parts in this layer. * Add those polygons to @p result. - * + * * \param external_polys_only Whether to only include the outermost outline of each layer part * \param result The result: a collection of all the outline polygons */ @@ -198,12 +199,10 @@ class SliceLayer /******************/ - - class SupportLayer { public: - std::vector support_infill_parts; //!< a list of support infill parts + std::vector support_infill_parts; //!< a list of support infill parts Polygons support_bottom; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof. Polygons support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. Polygons support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support @@ -254,8 +253,10 @@ class SliceMeshStorage std::vector roofing_angles; //!< a list of angle values which is cycled through to determine the roofing angle of each layer std::vector skin_angles; //!< a list of angle values which is cycled through to determine the skin angle of each layer std::vector overhang_areas; //!< For each layer the areas that are classified as overhang on this mesh. - std::vector full_overhang_areas; //!< For each layer the full overhang without the tangent of the overhang angle removed, such that the overhang area adjoins the areas of the next layers. - std::vector> overhang_points; //!< For each layer a list of points where point-overhang is detected. This is overhang that hasn't got any surface area, such as a corner pointing downwards. + std::vector full_overhang_areas; //!< For each layer the full overhang without the tangent of the overhang angle removed, such that the overhang area adjoins the + //!< areas of the next layers. + std::vector> overhang_points; //!< For each layer a list of points where point-overhang is detected. This is overhang that hasn't got any surface area, + //!< such as a corner pointing downwards. AABB3D bounding_box; //!< the mesh's bounding box std::shared_ptr base_subdiv_cube; @@ -321,22 +322,23 @@ class SliceDataStorage : public NoCopy std::vector retraction_wipe_config_per_extruder; //!< Config for retractions, extruder switch retractions, and wipes, per extruder. SupportStorage support; - + std::vector skirt_brim[MAX_EXTRUDERS]; //!< Skirt/brim polygons per extruder, ordered from inner to outer polygons. Polygons support_brim; //!< brim lines for support, going from the edge of the support inward. \note Not ordered by inset. - Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated. - Polygons primeRaftOutline; // ... the raft underneath the prime-tower will have to be printed first, if there is one. (When the raft has top layers with a different extruder for example.) + Polygons raftOutline; // Storage for the outline of the raft. Will be filled with lines when the GCode is generated. + Polygons primeRaftOutline; // ... the raft underneath the prime-tower will have to be printed first, if there is one. (When the raft has top layers with a different extruder + // for example.) int max_print_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder std::vector max_print_height_per_extruder; //!< For each extruder the highest layer number at which it is used. std::vector max_print_height_order; //!< Ordered indices into max_print_height_per_extruder: back() will return the extruder number with the highest print height. std::vector spiralize_seam_vertex_indices; //!< the index of the seam vertex for each layer - std::vector spiralize_wall_outlines; //!< the wall outline polygons for each layer + std::vector spiralize_wall_outlines; //!< the wall outline polygons for each layer PrimeTower primeTower; - std::vector oozeShield; //oozeShield per layer + std::vector oozeShield; // oozeShield per layer Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber. /*! @@ -351,7 +353,7 @@ class SliceDataStorage : public NoCopy /*! * Get all outlines within a given layer. - * + * * \param layer_nr The index of the layer for which to get the outlines * (negative layer numbers indicate the raft). * \param include_support Whether to include support in the outline. @@ -360,11 +362,13 @@ class SliceDataStorage : public NoCopy * \param external_polys_only Whether to disregard all hole polygons. * \param extruder_nr (optional) only give back outlines for this extruder (where the walls are printed with this extruder) */ - Polygons getLayerOutlines(const LayerIndex layer_nr, const bool include_support, const bool include_prime_tower, const bool external_polys_only = false, const int extruder_nr = -1) const; + Polygons + getLayerOutlines(const LayerIndex layer_nr, const bool include_support, const bool include_prime_tower, const bool external_polys_only = false, const int extruder_nr = -1) + const; /*! * Get the extruders used. - * + * * \return A vector of booleans indicating whether the extruder with the * corresponding index is used in the mesh group. */ @@ -372,7 +376,7 @@ class SliceDataStorage : public NoCopy /*! * Get the extruders used on a particular layer. - * + * * \param layer_nr the layer for which to check * \return a vector of bools indicating whether the extruder with corresponding index is used in this layer. */ @@ -401,6 +405,6 @@ class SliceDataStorage : public NoCopy std::vector initializeRetractionAndWipeConfigs(); }; -}//namespace cura +} // namespace cura -#endif//SLICE_DATA_STORAGE_H +#endif // SLICE_DATA_STORAGE_H diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 6544e8b799..35419aef1e 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -401,7 +401,7 @@ std::shared_ptr TreeSupportTipGenerator::generateCrossFi { return std::make_shared(aabb, line_distance, line_width, cross_subdisivion_spec_image_file); } - return std::make_shared(aabb, line_distance, line_width); + return std::make_shared(aabb, line_distance, line_width); } return nullptr; } diff --git a/src/infill/LightningTreeNode.cpp b/src/infill/LightningTreeNode.cpp index 9766c6fcb2..cdde2d374d 100644 --- a/src/infill/LightningTreeNode.cpp +++ b/src/infill/LightningTreeNode.cpp @@ -15,7 +15,7 @@ coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location constexpr coord_t max_valence_for_boost = 4; constexpr coord_t valence_boost_multiplier = 4; - const size_t valence = (!is_root) + children.size(); + const size_t valence = (! is_root) + children.size(); const coord_t valence_boost = (min_valence_for_boost < valence && valence < max_valence_for_boost) ? valence_boost_multiplier * supporting_radius : 0; const coord_t dist_here = vSize(getLocation() - unsupported_location); return dist_here - valence_boost; @@ -29,7 +29,8 @@ bool LightningTreeNode::hasOffspring(const LightningTreeNodeSPtr& to_be_checked) } for (auto& child_ptr : children) { - if (child_ptr->hasOffspring(to_be_checked)) return true; + if (child_ptr->hasOffspring(to_be_checked)) + return true; } return false; } @@ -54,22 +55,20 @@ LightningTreeNodeSPtr LightningTreeNode::addChild(const Point& child_loc) LightningTreeNodeSPtr LightningTreeNode::addChild(LightningTreeNodeSPtr& new_child) { assert(new_child != shared_from_this()); - //assert(p != new_child->p); // NOTE: No problem for now. Issue to solve later. Maybe even afetr final. Low prio. + // assert(p != new_child->p); // NOTE: No problem for now. Issue to solve later. Maybe even afetr final. Low prio. children.push_back(new_child); new_child->parent = shared_from_this(); new_child->is_root = false; return new_child; } -void LightningTreeNode::propagateToNextLayer -( +void LightningTreeNode::propagateToNextLayer( std::vector& next_trees, const Polygons& next_outlines, const LocToLineGrid& outline_locator, const coord_t prune_distance, const coord_t smooth_magnitude, - const coord_t max_remove_colinear_dist -) const + const coord_t max_remove_colinear_dist) const { auto tree_below = deepCopy(); @@ -105,10 +104,11 @@ void LightningTreeNode::visitNodes(const std::function& last_grounding_location /*= std::nullopt*/) -: is_root(true) -, p(p) -, last_grounding_location(last_grounding_location) -{} + : is_root(true) + , p(p) + , last_grounding_location(last_grounding_location) +{ +} LightningTreeNodeSPtr LightningTreeNode::deepCopy() const { @@ -169,12 +169,7 @@ LightningTreeNodeSPtr LightningTreeNode::closestNode(const Point& loc) return result; } -bool LightningTreeNode::realign -( - const Polygons& outlines, - const LocToLineGrid& outline_locator, - std::vector& rerooted_parts -) +bool LightningTreeNode::realign(const Polygons& outlines, const LocToLineGrid& outline_locator, std::vector& rerooted_parts) { if (outlines.empty()) { @@ -186,8 +181,7 @@ bool LightningTreeNode::realign // Only keep children that have an unbroken connection to here, realign will put the rest in rerooted parts due to recursion: Point coll; bool reground_me = false; - const auto remove_unconnected_func - { + const auto remove_unconnected_func{ [&](const LightningTreeNodeSPtr& child) { bool connect_branch = child->realign(outlines, outline_locator, rerooted_parts); @@ -233,13 +227,8 @@ void LightningTreeNode::straighten(const coord_t magnitude, const coord_t max_re straighten(magnitude, p, 0, max_remove_colinear_dist * max_remove_colinear_dist); } -LightningTreeNode::RectilinearJunction LightningTreeNode::straighten -( - const coord_t magnitude, - const Point& junction_above, - const coord_t accumulated_dist, - const coord_t max_remove_colinear_dist2 -) +LightningTreeNode::RectilinearJunction + LightningTreeNode::straighten(const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, const coord_t max_remove_colinear_dist2) { constexpr coord_t junction_magnitude_factor_numerator = 3; constexpr coord_t junction_magnitude_factor_denominator = 4; @@ -269,14 +258,10 @@ 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 + 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) < close_enough) { child_p->parent = parent; for (auto& sibling : parent_node->children) @@ -329,7 +314,7 @@ coord_t LightningTreeNode::prune(const coord_t& pruning_distance) } coord_t max_distance_pruned = 0; - for (auto child_it = children.begin(); child_it != children.end(); ) + for (auto child_it = children.begin(); child_it != children.end();) { auto& child = *child_it; coord_t dist_pruned_child = child->prune(pruning_distance); @@ -403,7 +388,7 @@ void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& outpu void LightningTreeNode::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const { const coord_t reduction = line_width / 2; // TODO make configurable? - for (auto poly_it = result_lines.begin(); poly_it != result_lines.end(); ) + for (auto poly_it = result_lines.begin(); poly_it != result_lines.end();) { PolygonRef polyline = *poly_it; if (polyline.size() <= 1) diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 4e89ea5625..788ac2e45a 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include +#include "sliceDataStorage.h" #include "Application.h" //To get settings. #include "ExtruderTrain.h" @@ -12,14 +12,18 @@ #include "infill/SierpinskiFillProvider.h" #include "infill/SubDivCube.h" // For the destructor #include "raft.h" -#include "sliceDataStorage.h" #include "utils/math.h" //For PI. +#include + namespace cura { -SupportStorage::SupportStorage() : generated(false), layer_nr_max_filled_layer(-1), cross_fill_provider(nullptr) +SupportStorage::SupportStorage() + : generated(false) + , layer_nr_max_filled_layer(-1) + , cross_fill_provider(nullptr) { } @@ -124,7 +128,8 @@ bool SliceMeshStorage::getExtruderIsUsed(const size_t extruder_nr) const { return true; } - if ((settings.get("wall_line_count") > 1 || settings.get("alternate_extra_perimeter")) && settings.get("wall_x_extruder_nr").extruder_nr == extruder_nr) + if ((settings.get("wall_line_count") > 1 || settings.get("alternate_extra_perimeter")) + && settings.get("wall_x_extruder_nr").extruder_nr == extruder_nr) { return true; } @@ -155,7 +160,8 @@ bool SliceMeshStorage::getExtruderIsUsed(const size_t extruder_nr, const LayerIn return false; } const SliceLayer& layer = layers[layer_nr]; - if (settings.get("wall_0_extruder_nr").extruder_nr == extruder_nr && (settings.get("wall_line_count") > 0 || settings.get("skin_outline_count") > 0)) + if (settings.get("wall_0_extruder_nr").extruder_nr == extruder_nr + && (settings.get("wall_line_count") > 0 || settings.get("skin_outline_count") > 0)) { for (const SliceLayerPart& part : layer.parts) { @@ -165,11 +171,13 @@ bool SliceMeshStorage::getExtruderIsUsed(const size_t extruder_nr, const LayerIn } } } - if (settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && settings.get("wall_0_extruder_nr").extruder_nr == extruder_nr && layer.openPolyLines.size() > 0) + if (settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && settings.get("wall_0_extruder_nr").extruder_nr == extruder_nr + && layer.openPolyLines.size() > 0) { return true; } - if ((settings.get("wall_line_count") > 1 || settings.get("alternate_extra_perimeter")) && settings.get("wall_x_extruder_nr").extruder_nr == extruder_nr) + if ((settings.get("wall_line_count") > 1 || settings.get("alternate_extra_perimeter")) + && settings.get("wall_x_extruder_nr").extruder_nr == extruder_nr) { for (const SliceLayerPart& part : layer.parts) { @@ -241,10 +249,10 @@ std::vector SliceDataStorage::initializeRetractionAndWi return ret; } -SliceDataStorage::SliceDataStorage() : - print_layer_count(0), - retraction_wipe_config_per_extruder(initializeRetractionAndWipeConfigs()), - max_print_height_second_to_last_extruder(-1) +SliceDataStorage::SliceDataStorage() + : print_layer_count(0) + , retraction_wipe_config_per_extruder(initializeRetractionAndWipeConfigs()) + , max_print_height_second_to_last_extruder(-1) { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; Point3 machine_max(mesh_group_settings.get("machine_width"), mesh_group_settings.get("machine_depth"), mesh_group_settings.get("machine_height")); @@ -258,7 +266,9 @@ SliceDataStorage::SliceDataStorage() : machine_size.include(machine_max); } -Polygons SliceDataStorage::getLayerOutlines(const LayerIndex layer_nr, const bool include_support, const bool include_prime_tower, const bool external_polys_only, const int extruder_nr) const +Polygons + SliceDataStorage::getLayerOutlines(const LayerIndex layer_nr, const bool include_support, const bool include_prime_tower, const bool external_polys_only, const int extruder_nr) + const { const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; if (layer_nr < 0 && layer_nr < -static_cast(Raft::getFillerLayerCount())) @@ -469,7 +479,8 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) // support if (layer_nr < int(support.supportLayers.size())) { - const SupportLayer& support_layer = support.supportLayers[std::max(LayerIndex(0), layer_nr)]; // Below layer 0, it's the same as layer 0 (even though it's not stored here). + const SupportLayer& support_layer + = support.supportLayers[std::max(LayerIndex(0), layer_nr)]; // Below layer 0, it's the same as layer 0 (even though it's not stored here). if (layer_nr == 0) { if (! support_layer.support_infill_parts.empty()) @@ -552,7 +563,7 @@ Polygons SliceDataStorage::getMachineBorder(int checking_extruder_nr) const for (PolygonRef poly : disallowed_areas) for (Point& p : poly) p = Point(machine_size.max.x / 2 + p.X, machine_size.max.y / 2 - p.Y); // apparently the frontend stores the disallowed areas in a different coordinate system - + std::vector extruder_is_used = getExtrudersUsed(); constexpr coord_t prime_clearance = MM2INT(6.5); @@ -563,7 +574,7 @@ Polygons SliceDataStorage::getMachineBorder(int checking_extruder_nr) const continue; } Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[extruder_nr].settings; - if (!(extruder_settings.get("prime_blob_enable") && mesh_group_settings.get("extruder_prime_pos_abs"))) + if (! (extruder_settings.get("prime_blob_enable") && mesh_group_settings.get("extruder_prime_pos_abs"))) { continue; } @@ -583,7 +594,7 @@ Polygons SliceDataStorage::getMachineBorder(int checking_extruder_nr) const bool first = true; for (size_t extruder_nr = 0; extruder_nr < extruder_is_used.size(); extruder_nr++) { - if ((checking_extruder_nr != -1 && int(extruder_nr) != checking_extruder_nr) || !extruder_is_used[extruder_nr]) + if ((checking_extruder_nr != -1 && int(extruder_nr) != checking_extruder_nr) || ! extruder_is_used[extruder_nr]) { continue; } @@ -602,7 +613,7 @@ Polygons SliceDataStorage::getMachineBorder(int checking_extruder_nr) const } } disallowed_all_extruders.processEvenOdd(ClipperLib::pftNonZero); // prevent overlapping disallowed areas from XORing - + Polygons border_all_extruders = border; // each extruders border areas must be limited to the global border, which is the union of all extruders borders if (mesh_group_settings.has("nozzle_offsetting_for_disallowed_areas") && mesh_group_settings.get("nozzle_offsetting_for_disallowed_areas")) { diff --git a/src/support.cpp b/src/support.cpp index f1c1bc6bd6..1adb0b1ae7 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -761,8 +761,10 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) { spdlog::error("Cannot find density image: {}.", cross_subdisivion_spec_image_file); } - storage.support.cross_fill_provider - = std::make_shared(aabb, infill_extruder.settings.get("support_line_distance"), infill_extruder.settings.get("support_line_width")); + storage.support.cross_fill_provider = std::make_shared( + aabb, + infill_extruder.settings.get("support_line_distance"), + infill_extruder.settings.get("support_line_width")); } } } From 159dd626f746e029a30e123026414111aed887c4 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 24 Aug 2023 12:35:25 +0200 Subject: [PATCH 461/656] Add scripta logs CURA-10446 --- src/LayerPlan.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 31873f8608..0851d6cb40 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -18,8 +18,10 @@ #include "utils/Simplify.h" #include "utils/linearAlg2D.h" #include "utils/polygonUtils.h" +#include "utils/section_type.h" #include +#include #include #include @@ -1844,10 +1846,45 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; + scripta::log( + "extruder_plan_0", + extruder_plan.paths, + SectionType::NA, + layer_nr, + scripta::CellVDI{ "flow", &GCodePath::flow }, + scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, + scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, + scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, + scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, + scripta::CellVDI{ "retract", &GCodePath::retract }, + scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, + scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, + scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, + scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, + scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); + extruder_plan.paths = slots::instance().modify(extruder_plan.paths, extruder_plan.extruder_nr, layer_nr); // Since the time/material estimates _may_ have changed during the plugin modify step we recalculate it extruder_plan.computeNaiveTimeEstimates(gcode.getPositionXY()); + scripta::log( + "extruder_plan_1", + extruder_plan.paths, + SectionType::NA, + layer_nr, + scripta::CellVDI{ "flow", &GCodePath::flow }, + scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, + scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, + scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, + scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, + scripta::CellVDI{ "retract", &GCodePath::retract }, + scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, + scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, + scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, + scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, + scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); const RetractionAndWipeConfig* retraction_config = current_mesh ? ¤t_mesh->retraction_wipe_config : &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; @@ -2173,6 +2210,23 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } extruder_plan.handleAllRemainingInserts(gcode); + scripta::log( + "extruder_plan_2", + extruder_plan.paths, + SectionType::NA, + layer_nr, + scripta::CellVDI{ "flow", &GCodePath::flow }, + scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, + scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, + scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, + scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, + scripta::CellVDI{ "retract", &GCodePath::retract }, + scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, + scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, + scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, + scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, + scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); } // extruder plans /\ . communication->sendLayerComplete(layer_nr, z, layer_thickness); From 49055b7fb251d542466fc56667de854ecdb46f0b Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 25 Aug 2023 14:27:49 +0200 Subject: [PATCH 462/656] Fix compiling on mac CURA-10446 --- src/LayerPlan.cpp | 9 +- src/settings/PathConfigStorage.cpp | 152 ++++++++++++++--------------- 2 files changed, 84 insertions(+), 77 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 0851d6cb40..e66328e18c 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -50,7 +50,14 @@ GCodePath* LayerPlan::getLatestPathWithConfig( { return &paths.back(); } - paths.emplace_back(config, current_mesh, space_fill_type, flow, width_factor, spiralize, speed_factor); + paths.emplace_back(GCodePath{ .config = config, + .mesh = current_mesh, + .space_fill_type = space_fill_type, + .flow = flow, + .width_factor = width_factor, + .spiralize = spiralize, + .speed_factor = speed_factor }); + GCodePath* ret = &paths.back(); ret->skip_agressive_merge_hint = mode_skip_agressive_merge; return ret; diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 382eacb827..e32e9afb15 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -43,46 +43,45 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye , support_roof_train(Application::getInstance().current_slice->scene.extruders[support_roof_extruder_nr]) , support_bottom_train(Application::getInstance().current_slice->scene.extruders[support_bottom_extruder_nr]) , line_width_factor_per_extruder(PathConfigStorage::getLineWidthFactorPerExtruder(layer_nr)) - , raft_base_config( - PrintFeatureType::SupportInterface, - raft_base_train.settings.get("raft_base_line_width"), - raft_base_train.settings.get("raft_base_thickness"), - Ratio(1.0), - SpeedDerivatives{ .speed = raft_base_train.settings.get("raft_base_speed"), - .acceleration = raft_base_train.settings.get("raft_base_acceleration"), - .jerk = raft_base_train.settings.get("raft_base_jerk") }) - , raft_interface_config( - PrintFeatureType::Support, - raft_interface_train.settings.get("raft_interface_line_width"), - raft_interface_train.settings.get("raft_interface_thickness"), - Ratio(1.0), - SpeedDerivatives{ .speed = raft_interface_train.settings.get("raft_interface_speed"), - .acceleration = raft_interface_train.settings.get("raft_interface_acceleration"), - .jerk = raft_interface_train.settings.get("raft_interface_jerk") }) - , raft_surface_config( - PrintFeatureType::SupportInterface, - raft_surface_train.settings.get("raft_surface_line_width"), - raft_surface_train.settings.get("raft_surface_thickness"), - Ratio(1.0), - SpeedDerivatives{ .speed = raft_surface_train.settings.get("raft_surface_speed"), - .acceleration = raft_surface_train.settings.get("raft_surface_acceleration"), - .jerk = raft_surface_train.settings.get("raft_surface_jerk") }) - , support_roof_config( - PrintFeatureType::SupportInterface, - support_roof_train.settings.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr], - layer_thickness, - support_roof_train.settings.get("support_roof_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = support_roof_train.settings.get("speed_support_roof"), - .acceleration = support_roof_train.settings.get("acceleration_support_roof"), - .jerk = support_roof_train.settings.get("jerk_support_roof") }) - , support_bottom_config( - PrintFeatureType::SupportInterface, - support_bottom_train.settings.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr], - layer_thickness, - support_roof_train.settings.get("support_bottom_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = support_bottom_train.settings.get("speed_support_bottom"), - .acceleration = support_bottom_train.settings.get("acceleration_support_bottom"), - .jerk = support_bottom_train.settings.get("jerk_support_bottom") }) + , raft_base_config(GCodePathConfig{ .type = PrintFeatureType::SupportInterface, + .line_width = raft_base_train.settings.get("raft_base_line_width"), + .layer_thickness = raft_base_train.settings.get("raft_base_thickness"), + .flow = Ratio(1.0), + .speed_derivatives = SpeedDerivatives{ .speed = raft_base_train.settings.get("raft_base_speed"), + .acceleration = raft_base_train.settings.get("raft_base_acceleration"), + .jerk = raft_base_train.settings.get("raft_base_jerk") } }) + , raft_interface_config(GCodePathConfig{ .type = PrintFeatureType::Support, + .line_width = raft_interface_train.settings.get("raft_interface_line_width"), + .layer_thickness = raft_interface_train.settings.get("raft_interface_thickness"), + .flow = Ratio(1.0), + .speed_derivatives = SpeedDerivatives{ .speed = raft_interface_train.settings.get("raft_interface_speed"), + .acceleration = raft_interface_train.settings.get("raft_interface_acceleration"), + .jerk = raft_interface_train.settings.get("raft_interface_jerk") } }) + , raft_surface_config(GCodePathConfig{ .type = PrintFeatureType::SupportInterface, + .line_width = raft_surface_train.settings.get("raft_surface_line_width"), + .layer_thickness = raft_surface_train.settings.get("raft_surface_thickness"), + .flow = Ratio(1.0), + .speed_derivatives = SpeedDerivatives{ .speed = raft_surface_train.settings.get("raft_surface_speed"), + .acceleration = raft_surface_train.settings.get("raft_surface_acceleration"), + .jerk = raft_surface_train.settings.get("raft_surface_jerk") } }) + , support_roof_config(GCodePathConfig{ + .type = PrintFeatureType::SupportInterface, + .line_width = static_cast(support_roof_train.settings.get("support_roof_line_width") * line_width_factor_per_extruder[support_roof_extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = support_roof_train.settings.get("support_roof_material_flow") * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), + .speed_derivatives = { .speed = support_roof_train.settings.get("speed_support_roof"), + .acceleration = support_roof_train.settings.get("acceleration_support_roof"), + .jerk = support_roof_train.settings.get("jerk_support_roof") } }) + , support_bottom_config(GCodePathConfig{ + .type = PrintFeatureType::SupportInterface, + .line_width = static_cast(support_bottom_train.settings.get("support_bottom_line_width") * line_width_factor_per_extruder[support_bottom_extruder_nr]), + .layer_thickness = layer_thickness, + .flow = support_roof_train.settings.get("support_bottom_material_flow") + * ((layer_nr == 0) ? support_roof_train.settings.get("material_flow_layer_0") : Ratio(1.0)), + .speed_derivatives = SpeedDerivatives{ .speed = support_bottom_train.settings.get("speed_support_bottom"), + .acceleration = support_bottom_train.settings.get("acceleration_support_bottom"), + .jerk = support_bottom_train.settings.get("jerk_support_bottom") } }) { const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size(); travel_config_per_extruder.reserve(extruder_count); @@ -92,35 +91,36 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; - travel_config_per_extruder.emplace_back( - PrintFeatureType::MoveCombing, - 0, - 0, - 0.0, - SpeedDerivatives{ .speed = train.settings.get("speed_travel"), - .acceleration = train.settings.get("acceleration_travel"), - .jerk = train.settings.get("jerk_travel") }); + travel_config_per_extruder.emplace_back(GCodePathConfig{ .type = PrintFeatureType::MoveCombing, + .line_width = 0, + .layer_thickness = 0, + .flow = 0.0, + .speed_derivatives = SpeedDerivatives{ .speed = train.settings.get("speed_travel"), + .acceleration = train.settings.get("acceleration_travel"), + .jerk = train.settings.get("jerk_travel") } }); skirt_brim_config_per_extruder.emplace_back( - PrintFeatureType::SkirtBrim, - train.settings.get("skirt_brim_line_width") - * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) - ? 1.0_r - : line_width_factor_per_extruder[extruder_nr]) // cause it's also used for the draft/ooze shield - , - layer_thickness, - train.settings.get("skirt_brim_material_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = train.settings.get("skirt_brim_speed"), - .acceleration = train.settings.get("acceleration_skirt_brim"), - .jerk = train.settings.get("jerk_skirt_brim") }); - prime_tower_config_per_extruder.emplace_back( - PrintFeatureType::PrimeTower, - train.settings.get("prime_tower_line_width") - * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr]), - layer_thickness, - train.settings.get("prime_tower_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), - SpeedDerivatives{ .speed = train.settings.get("speed_prime_tower"), - .acceleration = train.settings.get("acceleration_prime_tower"), - .jerk = train.settings.get("jerk_prime_tower") }); + GCodePathConfig{ .type = PrintFeatureType::SkirtBrim, + .line_width = static_cast( + train.settings.get("skirt_brim_line_width") + * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) + ? 1.0_r + : line_width_factor_per_extruder[extruder_nr])) // cause it's also used for the draft/ooze shield + , + .layer_thickness = layer_thickness, + .flow = train.settings.get("skirt_brim_material_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), + .speed_derivatives = SpeedDerivatives{ .speed = train.settings.get("skirt_brim_speed"), + .acceleration = train.settings.get("acceleration_skirt_brim"), + .jerk = train.settings.get("jerk_skirt_brim") } }); + prime_tower_config_per_extruder.emplace_back(GCodePathConfig{ + .type = PrintFeatureType::PrimeTower, + .line_width = static_cast( + train.settings.get("prime_tower_line_width") + * ((mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT) ? 1.0_r : line_width_factor_per_extruder[extruder_nr])), + .layer_thickness = layer_thickness, + .flow = train.settings.get("prime_tower_flow") * ((layer_nr == 0) ? train.settings.get("material_flow_layer_0") : Ratio(1.0)), + .speed_derivatives = SpeedDerivatives{ .speed = train.settings.get("speed_prime_tower"), + .acceleration = train.settings.get("acceleration_prime_tower"), + .jerk = train.settings.get("jerk_prime_tower") } }); } mesh_configs.reserve(storage.meshes.size()); @@ -135,14 +135,14 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++) { support_infill_config.emplace_back( - PrintFeatureType::Support, - support_infill_train.settings.get("support_line_width") * support_infill_line_width_factor, - layer_thickness, - support_infill_train.settings.get("support_material_flow") * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) - * (combine_idx + 1), - SpeedDerivatives{ .speed = support_infill_train.settings.get("speed_support_infill"), - .acceleration = support_infill_train.settings.get("acceleration_support_infill"), - .jerk = support_infill_train.settings.get("jerk_support_infill") }); + GCodePathConfig{ .type = PrintFeatureType::Support, + .line_width = static_cast(support_infill_train.settings.get("support_line_width") * support_infill_line_width_factor), + .layer_thickness = layer_thickness, + .flow = -support_infill_train.settings.get("support_material_flow") + * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1), + .speed_derivatives = SpeedDerivatives{ .speed = support_infill_train.settings.get("speed_support_infill"), + .acceleration = support_infill_train.settings.get("acceleration_support_infill"), + .jerk = support_infill_train.settings.get("jerk_support_infill") } }); } const size_t initial_speedup_layer_count = mesh_group_settings.get("speed_slowdown_layers"); From 540ea165415471789a8d8d8597f1f5204701884c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 25 Aug 2023 15:11:40 +0200 Subject: [PATCH 463/656] For linear based (crossing) infill patterns, prevent crossing. When connecting infill lines, lines corssing close to the boundary will tend to create small 'loops' with sharp corners. This 'undoes' crossings of subsequential lines close to the border when connect infill is on. Still to do; - The 'close to the border only' part. - (More) documentation. part of CURA-10410 --- include/infill.h | 37 ++++++++++++ include/utils/linearAlg2D.h | 7 +++ src/infill.cpp | 110 +++++++++++++++++++++++++++++++----- src/utils/LinearAlg2D.cpp | 8 +++ 4 files changed, 149 insertions(+), 13 deletions(-) diff --git a/include/infill.h b/include/infill.h index b3c915ff4c..3f9b76f25a 100644 --- a/include/infill.h +++ b/include/infill.h @@ -193,9 +193,11 @@ class Infill */ InfillLineSegment(const Point start, const size_t start_segment, const size_t start_polygon, const Point end, const size_t end_segment, const size_t end_polygon) : start(start) + , altered_start(start) , start_segment(start_segment) , start_polygon(start_polygon) , end(end) + , altered_end(end) , end_segment(end_segment) , end_polygon(end_polygon) , previous(nullptr) @@ -206,6 +208,11 @@ class Infill */ Point start; + /*! + * TODO: documentation + */ + Point altered_start; + /*! * Which polygon line segment the start of this infill line belongs to. * @@ -223,11 +230,21 @@ class Infill */ size_t start_polygon; + /*! + * TODO: documentation + */ + std::optional start_bend; + /*! * Where the line segment ends. */ Point end; + /*! + * TODO: documentation + */ + Point altered_end; + /*! * Which polygon line segment the end of this infill line belongs to. * @@ -245,6 +262,11 @@ class Infill */ size_t end_polygon; + /*! + * TODO: documentation + */ + std::optional end_bend; + /*! * The previous line segment that this line segment is connected to, if * any. @@ -263,6 +285,16 @@ class Infill * \param other The line segment to compare this line segment with. */ bool operator==(const InfillLineSegment& other) const; + + /*! + * TODO: documentation + */ + void swapDirection(); + + /*! + * TODO: documentation + */ + void appendTo(PolygonRef& result_polyline, const bool include_start = true); }; /*! @@ -468,6 +500,11 @@ class Infill */ coord_t getShiftOffsetFromInfillOriginAndRotation(const double& infill_rotation); + /*! + * TODO: documentation! + */ + void resolveIntersection(const coord_t at_distance, const Point& intersect, Point& connect_start, Point& connect_end, InfillLineSegment* a, InfillLineSegment* b); + /*! * Connects infill lines together so that they form polylines. * diff --git a/include/utils/linearAlg2D.h b/include/utils/linearAlg2D.h index 21af13e5d6..efb6323f03 100644 --- a/include/utils/linearAlg2D.h +++ b/include/utils/linearAlg2D.h @@ -423,6 +423,13 @@ class LinearAlg2D * Test whether the \p query_point is inside of a polygon w.r.t a single corner. */ static bool isInsideCorner(const Point a, const Point b, const Point c, const Point query_point); + + /*! + * TODO: documentation + * + * NOTE: Result is _not_ a normalized vector! + */ + static Point getBisectorVector(const Point& intersect, const Point& a, const Point& b); }; diff --git a/src/infill.cpp b/src/infill.cpp index 4f4fd757e4..1eb85de0ea 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -13,6 +13,7 @@ #include "infill/SubDivCube.h" #include "infill/UniformDensityProvider.h" #include "sliceDataStorage.h" +#include "utils/linearAlg2D.h" #include "utils/PolygonConnector.h" #include "utils/PolylineStitcher.h" #include "utils/Simplify.h" @@ -782,6 +783,44 @@ void Infill::generateLinearBasedInfill( } } +void Infill::resolveIntersection(const coord_t at_distance, const Point& intersect, Point& connect_start, Point& connect_end, InfillLineSegment* a, InfillLineSegment* b) +{ + // Select wich ends of the line need to 'bend'. + const bool forward_line_a = a->end == connect_start; + const bool forward_line_b = b->start == connect_end; + auto& bend_a = forward_line_a ? a->end_bend : a->start_bend; + auto& bend_b = forward_line_b ? b->start_bend : b->end_bend; + auto& end_a = forward_line_a ? a->altered_end : a->altered_start; + auto& end_b = forward_line_b ? b->altered_start : b->altered_end; + + // Initialize 'bends'. + assert(! bend_a.has_value()); + assert(! bend_b.has_value()); + bend_a.emplace(0, 0); + bend_b.emplace(0, 0); + + // Find a bisector of the intersection; specifically, the one that crosses the connection & offset it by 1/2 distance to each side. + const auto bisect = LinearAlg2D::getBisectorVector(intersect, connect_start, connect_end); + const auto offset = ((at_distance / 2) * Point(-bisect.Y, bisect.X)) / vSize(bisect); + const auto q = intersect + offset; + const auto r = q + bisect; + const auto s = intersect - offset; + const auto t = s + bisect; + + // Use both of the resulting lines to place the 'bends' by intersecting with the original line-segments. + Point helper_a, helper_b; + assert(LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value())); + assert(LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value())); + + // Also set the new end-points. + assert(LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a)); + assert(LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b)); + + // The connecting line will be made from the end-points. + connect_start = end_a; + connect_end = end_b; +} + void Infill::connectLines(Polygons& result_lines) { UnionFind connected_lines; // Keeps track of which lines are connected to which. @@ -852,12 +891,14 @@ void Infill::connectLines(Polygons& result_lines) // Join two infill lines together with a connecting line. // Here the InfillLineSegments function as a linked list, so that they can easily be joined. - const Point previous_point + Point& previous_point = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) ? previous_segment->start : previous_segment->end; - const Point next_point = (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index) ? crossing->start : crossing->end; + Point& next_point = (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index) ? crossing->start : crossing->end; + InfillLineSegment* new_segment; - // If the segment is zero length, we avoid creating it but still want to connect the crossing with the previous segment - if (previous_point == next_point) + // If the segment is near zero length, we avoid creating it but still want to connect the crossing with the previous segment. + constexpr coord_t epsilon_squared = 25; + if (vSize2(previous_point - next_point) < epsilon_squared) { if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) { @@ -871,8 +912,19 @@ void Infill::connectLines(Polygons& result_lines) } else { + // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop' just inside the boundary. + Point intersect; + if (LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) && + LinearAlg2D::pointIsProjectedBeyondLine(intersect, previous_segment->start, previous_segment->end) == 0 && + LinearAlg2D::pointIsProjectedBeyondLine(intersect, crossing->start, crossing->end) == 0) + { + resolveIntersection(infill_line_width, intersect, previous_point, next_point, previous_segment, crossing); + } + new_segment = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); // A connecting line between them. + new_segment->altered_start = previous_point; + new_segment->altered_end = next_point; new_segment->previous = previous_segment; if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) { @@ -976,20 +1028,27 @@ void Infill::connectLines(Polygons& result_lines) } // Now go along the linked list of infill lines and output the infill lines to the actual result. - InfillLineSegment* old_line = current_infill_line; - const Point first_vertex = (! current_infill_line->previous) ? current_infill_line->start : current_infill_line->end; - previous_vertex = (! current_infill_line->previous) ? current_infill_line->end : current_infill_line->start; - current_infill_line = (first_vertex == current_infill_line->start) ? current_infill_line->next : current_infill_line->previous; PolygonRef result_line = result_lines.newPoly(); - result_line.add(first_vertex); - result_line.add(previous_vertex); + InfillLineSegment* old_line = current_infill_line; + if (current_infill_line->previous) + { + current_infill_line->swapDirection(); + } + current_infill_line->appendTo(result_line); + previous_vertex = current_infill_line->end; + current_infill_line = current_infill_line->next; delete old_line; while (current_infill_line) { old_line = current_infill_line; // We'll delete this after we've traversed to the next line. - const Point next_vertex = (previous_vertex == current_infill_line->start) ? current_infill_line->end : current_infill_line->start; // Opposite side of the line. - current_infill_line = (previous_vertex == current_infill_line->start) ? current_infill_line->next : current_infill_line->previous; - result_line.add(next_vertex); + if (previous_vertex != current_infill_line->start) + { + current_infill_line->swapDirection(); + } + const Point next_vertex = current_infill_line->end; // Opposite side of the line. + constexpr bool polyline_break = false; + current_infill_line->appendTo(result_line, polyline_break); + current_infill_line = current_infill_line->next; previous_vertex = next_vertex; delete old_line; } @@ -1003,4 +1062,29 @@ bool Infill::InfillLineSegment::operator==(const InfillLineSegment& other) const return start == other.start && end == other.end; } +void Infill::InfillLineSegment::swapDirection() +{ + std::swap(start, end); + std::swap(altered_start, altered_end); + std::swap(start_bend, end_bend); + std::swap(next, previous); +} + +void Infill::InfillLineSegment::appendTo(PolygonRef& result_polyline, const bool include_start) +{ + if (include_start) + { + result_polyline.add(altered_start); + } + if (start_bend.has_value()) + { + result_polyline.add(start_bend.value()); + } + if (end_bend.has_value()) + { + result_polyline.add(end_bend.value()); + } + result_polyline.add(altered_end); +} + } // namespace cura diff --git a/src/utils/LinearAlg2D.cpp b/src/utils/LinearAlg2D.cpp index 4a5c2d1cd3..39227de261 100644 --- a/src/utils/LinearAlg2D.cpp +++ b/src/utils/LinearAlg2D.cpp @@ -257,4 +257,12 @@ coord_t LinearAlg2D::getDistFromLine(const Point& p, const Point& a, const Point return px_size; } +Point LinearAlg2D::getBisectorVector(const Point& intersect, const Point& a, const Point& b) +{ + constexpr coord_t large_enough = 0xFFFF; + const auto a0 = a - intersect; + const auto b0 = b - intersect; + return (((a0 * large_enough) / std::max(1LL,vSize(a0))) + ((b0 * large_enough) / std::max(1LL,vSize(b0)))) / 2; +} + } // namespace cura From b2f716785c37c84570ca45c88a526c76bfc5f001 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 25 Aug 2023 15:34:06 +0200 Subject: [PATCH 464/656] Only prevent line-crossing when the distance is small enough. If a large (enough) connecting line would need to be make, the prevention of the crossing wouldn't be nescesary, since it's for the prevention of (relatively) small loops near the boundaries of the infill. part of CURA-10410 --- src/infill.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 1eb85de0ea..bea98b553d 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -838,6 +838,7 @@ void Infill::connectLines(Polygons& result_lines) } } + const auto half_line_distance_squared = (line_distance * line_distance) / 4; for (size_t polygon_index = 0; polygon_index < inner_contour.size(); polygon_index++) { ConstPolygonRef inner_contour_polygon = inner_contour[polygon_index]; @@ -898,7 +899,8 @@ void Infill::connectLines(Polygons& result_lines) InfillLineSegment* new_segment; // If the segment is near zero length, we avoid creating it but still want to connect the crossing with the previous segment. constexpr coord_t epsilon_squared = 25; - if (vSize2(previous_point - next_point) < epsilon_squared) + const auto connect_distance_squared = vSize2(previous_point - next_point); + if (connect_distance_squared < epsilon_squared) { if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) { @@ -914,7 +916,8 @@ void Infill::connectLines(Polygons& result_lines) { // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop' just inside the boundary. Point intersect; - if (LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) && + if ( connect_distance_squared < half_line_distance_squared && + LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) && LinearAlg2D::pointIsProjectedBeyondLine(intersect, previous_segment->start, previous_segment->end) == 0 && LinearAlg2D::pointIsProjectedBeyondLine(intersect, crossing->start, crossing->end) == 0) { From 33fb1720c2dc8d266a41db6499d5f0fa91921eca Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 25 Aug 2023 15:59:29 +0200 Subject: [PATCH 465/656] Add documentation. part of CURA-10410 --- include/infill.h | 40 ++++++++++++++++++++++++++++++------- include/utils/linearAlg2D.h | 8 ++++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/include/infill.h b/include/infill.h index 3f9b76f25a..269c695887 100644 --- a/include/infill.h +++ b/include/infill.h @@ -209,7 +209,9 @@ class Infill Point start; /*! - * TODO: documentation + * If the line-segment starts at a different point due to prevention of crossing near the boundary, it gets saved here. + * + * The original start-point is still used to determine ordering then, so it can't just be overwritten. */ Point altered_start; @@ -231,7 +233,7 @@ class Infill size_t start_polygon; /*! - * TODO: documentation + * If the line-segment needs to prevent crossing with another line near its start, a point is inserted near the start. */ std::optional start_bend; @@ -241,7 +243,9 @@ class Infill Point end; /*! - * TODO: documentation + * If the line-segment ends at a different point due to prevention of crossing near the boundary, it gets saved here. + * + * The original end-point is still used to determine ordering then, so it can't just be overwritten. */ Point altered_end; @@ -263,7 +267,7 @@ class Infill size_t end_polygon; /*! - * TODO: documentation + * If the line-segment needs to prevent crossing with another line near its end, a point is inserted near the end. */ std::optional end_bend; @@ -287,12 +291,16 @@ class Infill bool operator==(const InfillLineSegment& other) const; /*! - * TODO: documentation + * Invert the direction of the line-segment. + * + * Useful when the next move is from end to start instead of 'forwards'. */ void swapDirection(); /*! - * TODO: documentation + * Append this line-segment to the results, start, bends and end. + * + * \param include_start Wether to include the start point or not, useful when tracing a poly-line. */ void appendTo(PolygonRef& result_polyline, const bool include_start = true); }; @@ -501,7 +509,25 @@ class Infill coord_t getShiftOffsetFromInfillOriginAndRotation(const double& infill_rotation); /*! - * TODO: documentation! + * Used to prevent intersections of linear-based infill. + * + * When connecting infill, and the infill crosses itself near the boundary, small 'loops' can occur, which have large internal angles. + * Prevent this by altering the two crossing line-segments just before the crossing takes place: + * + * \ / \ / + * \ / \ / + * X | | + * / \ | | + * --- - + * ======= ======= + * before after + * + * \param at_distance At which distance the offset of the bisector takes place (will be the length of the resulting connection along the edge). + * \param intersect The point at which these line-segments intersect. + * \param connect_start Input; the original point at the border which the first line-segment touches. Output; the updated point. + * \param connect_end Input; the original point at the border which the second line-segment touches. Output; the updated point. + * \param a The first line-segment. + * \param b The second line-segment. */ void resolveIntersection(const coord_t at_distance, const Point& intersect, Point& connect_start, Point& connect_end, InfillLineSegment* a, InfillLineSegment* b); diff --git a/include/utils/linearAlg2D.h b/include/utils/linearAlg2D.h index efb6323f03..567fcf4fdf 100644 --- a/include/utils/linearAlg2D.h +++ b/include/utils/linearAlg2D.h @@ -425,9 +425,13 @@ class LinearAlg2D static bool isInsideCorner(const Point a, const Point b, const Point c, const Point query_point); /*! - * TODO: documentation + * Finds the vector for the bisection of a-b as seen from the intersection point. * - * NOTE: Result is _not_ a normalized vector! + * NOTE: The result has _not_ been normalized! This is done to prevent numerical instability later on. + * + * \param intersect The origin of the constellation. + * \param a The first point. + * \param b The second point. */ static Point getBisectorVector(const Point& intersect, const Point& a, const Point& b); }; From e5fecf595d7b511168ca0dbc136a79f5a0bad716 Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 25 Aug 2023 14:00:26 +0000 Subject: [PATCH 466/656] Applied clang-format. --- include/utils/linearAlg2D.h | 180 +++++++++++++++++------------------- src/infill.cpp | 13 +-- src/utils/LinearAlg2D.cpp | 58 ++++++------ 3 files changed, 122 insertions(+), 129 deletions(-) diff --git a/include/utils/linearAlg2D.h b/include/utils/linearAlg2D.h index 567fcf4fdf..a099496918 100644 --- a/include/utils/linearAlg2D.h +++ b/include/utils/linearAlg2D.h @@ -1,5 +1,5 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2020 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef UTILS_LINEAR_ALG_2D_H #define UTILS_LINEAR_ALG_2D_H @@ -14,7 +14,7 @@ class LinearAlg2D static short pointLiesOnTheRightOfLine(const Point& p, const Point& p0, const Point& p1) { // no tests unless the segment p0-p1 is at least partly at, or to right of, p.X - if ( std::max(p0.X, p1.X) >= p.X ) + if (std::max(p0.X, p1.X) >= p.X) { const coord_t pd_y = p1.Y - p0.Y; if (pd_y < 0) // p0->p1 is 'falling' @@ -54,8 +54,7 @@ class LinearAlg2D // - p1 exactly matches p (might otherwise be missed) // - p0->p1 exactly horizontal, and includes p. // (we already tested std::max(p0.X,p1.X) >= p.X ) - if (p.X == p1.X || - (pd_y == 0 && std::min(p0.X, p1.X) <= p.X) ) + if (p.X == p1.X || (pd_y == 0 && std::min(p0.X, p1.X) <= p.X)) { return 0; } @@ -63,35 +62,34 @@ class LinearAlg2D } } return -1; - } static bool lineLineIntersection(const Point& a, const Point& b, const Point& c, const Point& d, Point& output) { - //Adapted from Apex: https://github.com/Ghostkeeper/Apex/blob/eb75f0d96e36c7193d1670112826842d176d5214/include/apex/line_segment.hpp#L91 - //Adjusted to work with lines instead of line segments. + // Adapted from Apex: https://github.com/Ghostkeeper/Apex/blob/eb75f0d96e36c7193d1670112826842d176d5214/include/apex/line_segment.hpp#L91 + // Adjusted to work with lines instead of line segments. const Point l1_delta = b - a; const Point l2_delta = d - c; - const coord_t divisor = cross(l1_delta, l2_delta); //Pre-compute divisor needed for the intersection check. - if(divisor == 0) + const coord_t divisor = cross(l1_delta, l2_delta); // Pre-compute divisor needed for the intersection check. + if (divisor == 0) { - //The lines are parallel if the cross product of their directions is zero. + // The lines are parallel if the cross product of their directions is zero. return false; } - //Create a parametric representation of each line. - //We'll equate the parametric equations to each other to find the intersection then. - //Parametric equation is L = P + Vt (where P and V are a starting point and directional vector). - //We'll map the starting point of one line onto the parameter system of the other line. - //Then using the divisor we can see whether and where they cross. + // Create a parametric representation of each line. + // We'll equate the parametric equations to each other to find the intersection then. + // Parametric equation is L = P + Vt (where P and V are a starting point and directional vector). + // We'll map the starting point of one line onto the parameter system of the other line. + // Then using the divisor we can see whether and where they cross. const Point starts_delta = a - c; const coord_t l1_parametric = cross(l2_delta, starts_delta); Point result = a + Point(round_divide_signed(l1_parametric * l1_delta.X, divisor), round_divide_signed(l1_parametric * l1_delta.Y, divisor)); - if(std::abs(result.X) > std::numeric_limits::max() || std::abs(result.Y) > std::numeric_limits::max()) + if (std::abs(result.X) > std::numeric_limits::max() || std::abs(result.Y) > std::numeric_limits::max()) { - //Intersection is so far away that it could lead to integer overflows. - //Even though the lines aren't 100% parallel, it's better to pretend they are. They are practically parallel. + // Intersection is so far away that it could lead to integer overflows. + // Even though the lines aren't 100% parallel, it's better to pretend they are. They are practically parallel. return false; } output = result; @@ -103,7 +101,7 @@ class LinearAlg2D * - properly on the line : zero returned * - closer to \p a : -1 returned * - closer to \p b : 1 returned - * + * * \param from The point to check in relation to the line segment * \param a The start point of the line segment * \param b The end point of the line segment @@ -126,60 +124,63 @@ class LinearAlg2D } /*! - * Find the point closest to \p from on the line segment from \p p0 to \p p1 - */ + * Find the point closest to \p from on the line segment from \p p0 to \p p1 + */ static Point getClosestOnLineSegment(const Point& from, const Point& p0, const Point& p1) { const Point direction = p1 - p0; const Point to_from = from - p0; - const coord_t projected_x = dot(to_from, direction) ; + const coord_t projected_x = dot(to_from, direction); const coord_t x_p0 = 0; const coord_t x_p1 = vSize2(direction); if (x_p1 == 0) { - //Line segment has length 0. + // Line segment has length 0. return p0; } if (projected_x <= x_p0) { - //Projection is beyond p0. + // Projection is beyond p0. return p0; } if (projected_x >= x_p1) { - //Projection is beyond p1. + // Projection is beyond p1. return p1; } else { - //Projection is between p0 and p1. - //Return direction-normalised projection (projected_x / vSize(direction)) on direction vector. - //vSize(direction) * vSize(direction) == vSize2(direction) == x_p1. + // Projection is between p0 and p1. + // Return direction-normalised projection (projected_x / vSize(direction)) on direction vector. + // vSize(direction) * vSize(direction) == vSize2(direction) == x_p1. return p0 + projected_x * direction / x_p1; } } /*! - * Find the point closest to \p from on the line through \p p0 to \p p1 - */ + * Find the point closest to \p from on the line through \p p0 to \p p1 + */ static Point getClosestOnLine(const Point& from, const Point& p0, const Point& p1) { - if (p1 == p0) { return p0; } + if (p1 == p0) + { + return p0; + } const Point direction = p1 - p0; const Point to_from = from - p0; const coord_t projected_x = dot(to_from, direction); - Point ret = p0 + projected_x / vSize(direction) * direction / vSize(direction); + Point ret = p0 + projected_x / vSize(direction) * direction / vSize(direction); return ret; } /*! * Find the two points on two line segments closest to each other. - * + * * Find the smallest line segment connecting the two line segments a and b. - * + * * \param a1 first point on line a * \param a2 second point on line a * \param b1 first point on line b @@ -189,39 +190,39 @@ class LinearAlg2D static std::pair getClosestConnection(Point a1, Point a2, Point b1, Point b2); /*! - * Get the squared distance from point \p b to a line *segment* from \p a to \p c. - * - * In case \p b is on \p a or \p c, \p b_is_beyond_ac should become 0. - * - * \param a the first point of the line segment - * \param b the point to measure the distance from - * \param c the second point on the line segment - * \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1) - */ + * Get the squared distance from point \p b to a line *segment* from \p a to \p c. + * + * In case \p b is on \p a or \p c, \p b_is_beyond_ac should become 0. + * + * \param a the first point of the line segment + * \param b the point to measure the distance from + * \param c the second point on the line segment + * \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1) + */ static coord_t getDist2FromLineSegment(const Point& a, const Point& b, const Point& c, int16_t* b_is_beyond_ac = nullptr) { - /* - * a, - * /| - * / | - * b,/__|, x - * \ | - * \ | - * \| - * 'c - * - * x = b projected on ac - * ax = ab dot ac / vSize(ac) - * xb = ab - ax - * error = vSize(xb) - */ + /* + * a, + * /| + * / | + * b,/__|, x + * \ | + * \ | + * \| + * 'c + * + * x = b projected on ac + * ax = ab dot ac / vSize(ac) + * xb = ab - ax + * error = vSize(xb) + */ const Point ac = c - a; const coord_t ac_size = vSize(ac); const Point ab = b - a; - if (ac_size == 0) + if (ac_size == 0) { - const coord_t ab_dist2 = vSize2(ab); + const coord_t ab_dist2 = vSize2(ab); if (ab_dist2 == 0 && b_is_beyond_ac) { *b_is_beyond_ac = 0; // a is on b is on c @@ -232,8 +233,8 @@ class LinearAlg2D const coord_t projected_x = dot(ab, ac); const coord_t ax_size = projected_x / ac_size; - if (ax_size < 0) - {// b is 'before' segment ac + if (ax_size < 0) + { // b is 'before' segment ac if (b_is_beyond_ac) { *b_is_beyond_ac = -1; @@ -241,7 +242,7 @@ class LinearAlg2D return vSize2(ab); } if (ax_size > ac_size) - {// b is 'after' segment ac + { // b is 'after' segment ac if (b_is_beyond_ac) { *b_is_beyond_ac = 1; @@ -256,13 +257,13 @@ class LinearAlg2D const Point ax = ac * ax_size / ac_size; const Point bx = ab - ax; return vSize2(bx); -// return vSize2(ab) - ax_size*ax_size; // less accurate + // return vSize2(ab) - ax_size*ax_size; // less accurate } /*! * Checks whether the minimal distance between two line segments is at most \p max_dist * The first line semgent is given by end points \p a and \p b, the second by \p c and \p d. - * + * * \param a One end point of the first line segment * \param b Another end point of the first line segment * \param c One end point of the second line segment @@ -273,16 +274,14 @@ class LinearAlg2D { const coord_t max_dist2 = max_dist * max_dist; - return getDist2FromLineSegment(a, c, b) <= max_dist2 - || getDist2FromLineSegment(a, d, b) <= max_dist2 - || getDist2FromLineSegment(c, a, d) <= max_dist2 - || getDist2FromLineSegment(c, b, d) <= max_dist2; + return getDist2FromLineSegment(a, c, b) <= max_dist2 || getDist2FromLineSegment(a, d, b) <= max_dist2 || getDist2FromLineSegment(c, a, d) <= max_dist2 + || getDist2FromLineSegment(c, b, d) <= max_dist2; } /*! * Get the minimal distance between two line segments * The first line semgent is given by end points \p a and \p b, the second by \p c and \p d. - * + * * \param a One end point of the first line segment * \param b Another end point of the first line segment * \param c One end point of the second line segment @@ -290,21 +289,17 @@ class LinearAlg2D */ static coord_t getDist2BetweenLineSegments(const Point& a, const Point& b, const Point& c, const Point& d) { - return - std::min(getDist2FromLineSegment(a, c, b), - std::min(getDist2FromLineSegment(a, d, b), - std::min(getDist2FromLineSegment(c, a, d), - getDist2FromLineSegment(c, b, d)))); + return std::min(getDist2FromLineSegment(a, c, b), std::min(getDist2FromLineSegment(a, d, b), std::min(getDist2FromLineSegment(c, a, d), getDist2FromLineSegment(c, b, d)))); } /*! * Check whether two line segments collide. - * + * * \warning Edge cases (end points of line segments fall on other line segment) register as a collision. - * + * * \note All points are assumed to be transformed by the transformation matrix of the vector from \p a_from to \p a_to. * I.e. a is a vertical line; the Y of \p a_from_transformed is the same as the Y of \p a_to_transformed. - * + * * \param a_from_transformed The transformed from location of line a * \param a_from_transformed The transformed to location of line a * \param b_from_transformed The transformed from location of line b @@ -315,16 +310,16 @@ class LinearAlg2D /*! * Compute the angle between two consecutive line segments. - * + * * The angle is computed from the left side of b when looking from a. - * + * * c * \ . * \ b * angle| * | * a - * + * * \param a start of first line segment * \param b end of first segment and start of second line segment * \param c end of second line segment @@ -334,10 +329,10 @@ class LinearAlg2D /*! * Returns the determinant of the 2D matrix defined by the the vectors ab and ap as rows. - * + * * The returned value is zero for \p p lying (approximately) on the line going through \p a and \p b * The value is positive for values lying to the left and negative for values lying to the right when looking from \p a to \p b. - * + * * \param p the point to check * \param a the from point of the line * \param b the to point of the line @@ -350,9 +345,9 @@ class LinearAlg2D /*! * Get a point on the line segment (\p a - \p b)with a given distance to point \p p - * + * * In case there are two possible point that meet the criteria, choose the one closest to a. - * + * * \param p The reference point * \param a Start of the line segment * \param b End of the line segment @@ -387,12 +382,12 @@ class LinearAlg2D /*! * Check whether a corner is acute or obtuse. - * + * * This function is irrespective of the order between \p a and \p c; * the lowest angle among bot hsides of the corner is always chosen. - * + * * isAcuteCorner(a, b, c) === isAcuteCorner(c, b, a) - * + * * \param a start of first line segment * \param b end of first segment and start of second line segment * \param c end of second line segment @@ -419,7 +414,7 @@ class LinearAlg2D * Test whether a point is inside a corner. * Whether point \p query_point is left of the corner abc. * Whether the \p query_point is in the circle half left of ab and left of bc, rather than to the right. - * + * * Test whether the \p query_point is inside of a polygon w.r.t a single corner. */ static bool isInsideCorner(const Point a, const Point b, const Point c, const Point query_point); @@ -437,6 +432,5 @@ class LinearAlg2D }; - -}//namespace cura -#endif//UTILS_LINEAR_ALG_2D_H +} // namespace cura +#endif // UTILS_LINEAR_ALG_2D_H diff --git a/src/infill.cpp b/src/infill.cpp index bea98b553d..14f57b5d88 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -13,11 +13,11 @@ #include "infill/SubDivCube.h" #include "infill/UniformDensityProvider.h" #include "sliceDataStorage.h" -#include "utils/linearAlg2D.h" #include "utils/PolygonConnector.h" #include "utils/PolylineStitcher.h" #include "utils/Simplify.h" #include "utils/UnionFind.h" +#include "utils/linearAlg2D.h" #include "utils/polygonUtils.h" #include @@ -914,12 +914,13 @@ void Infill::connectLines(Polygons& result_lines) } else { - // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop' just inside the boundary. + // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop' just inside the + // boundary. Point intersect; - if ( connect_distance_squared < half_line_distance_squared && - LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) && - LinearAlg2D::pointIsProjectedBeyondLine(intersect, previous_segment->start, previous_segment->end) == 0 && - LinearAlg2D::pointIsProjectedBeyondLine(intersect, crossing->start, crossing->end) == 0) + if (connect_distance_squared < half_line_distance_squared + && LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) + && LinearAlg2D::pointIsProjectedBeyondLine(intersect, previous_segment->start, previous_segment->end) == 0 + && LinearAlg2D::pointIsProjectedBeyondLine(intersect, crossing->start, crossing->end) == 0) { resolveIntersection(infill_line_width, intersect, previous_point, next_point, previous_segment, crossing); } diff --git a/src/utils/LinearAlg2D.cpp b/src/utils/LinearAlg2D.cpp index 39227de261..d9191db723 100644 --- a/src/utils/LinearAlg2D.cpp +++ b/src/utils/LinearAlg2D.cpp @@ -1,15 +1,15 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "utils/linearAlg2D.h" -#include // atan2 -#include -#include // swap - #include "utils/IntPoint.h" // dot -namespace cura +#include // swap +#include +#include // atan2 + +namespace cura { float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c) @@ -20,10 +20,7 @@ float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c) const coord_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant if (det == 0) { - if ( - (ba.X != 0 && (ba.X > 0) == (bc.X > 0)) - || (ba.X == 0 && (ba.Y > 0) == (bc.Y > 0)) - ) + if ((ba.X != 0 && (ba.X > 0) == (bc.X > 0)) || (ba.X == 0 && (ba.Y > 0) == (bc.Y > 0))) { return 0; // pointy bit } @@ -37,7 +34,7 @@ float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c) { return angle; } - else + else { return M_PI * 2 + angle; } @@ -55,7 +52,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 < 50) ? dot(normal(ab, 1000), ap) / 1000 : 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) @@ -159,9 +156,10 @@ bool LinearAlg2D::lineSegmentsCollide(const Point& a_from_transformed, const Poi { assert(std::abs(a_from_transformed.Y - a_to_transformed.Y) < 2 && "line a is supposed to be transformed to be aligned with the X axis!"); assert(a_from_transformed.X - 2 <= a_to_transformed.X && "line a is supposed to be aligned with X axis in positive direction!"); - if ((b_from_transformed.Y >= a_from_transformed.Y && b_to_transformed.Y <= a_from_transformed.Y) || (b_to_transformed.Y >= a_from_transformed.Y && b_from_transformed.Y <= a_from_transformed.Y)) + if ((b_from_transformed.Y >= a_from_transformed.Y && b_to_transformed.Y <= a_from_transformed.Y) + || (b_to_transformed.Y >= a_from_transformed.Y && b_from_transformed.Y <= a_from_transformed.Y)) { - if(b_to_transformed.Y == b_from_transformed.Y) + if (b_to_transformed.Y == b_from_transformed.Y) { if (b_to_transformed.X < b_from_transformed.X) { @@ -179,7 +177,8 @@ bool LinearAlg2D::lineSegmentsCollide(const Point& a_from_transformed, const Poi } else { - const coord_t x = b_from_transformed.X + (b_to_transformed.X - b_from_transformed.X) * (a_from_transformed.Y - b_from_transformed.Y) / (b_to_transformed.Y - b_from_transformed.Y); + const coord_t x + = b_from_transformed.X + (b_to_transformed.X - b_from_transformed.X) * (a_from_transformed.Y - b_from_transformed.Y) / (b_to_transformed.Y - b_from_transformed.Y); if (x >= a_from_transformed.X && x <= a_to_transformed.X) { return true; @@ -214,26 +213,25 @@ 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. + 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 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. - const coord_t project_c_perpendicular = dot(bc, perpendicular); //Project vertex C on the perpendicular line. - if ((project_a_perpendicular > 0) != (project_c_perpendicular > 0)) //Query is between A and C on the projection. + 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. + const coord_t project_c_perpendicular = dot(bc, perpendicular); // Project vertex C on the perpendicular line. + if ((project_a_perpendicular > 0) != (project_c_perpendicular > 0)) // Query is between A and C on the projection. { - return project_a_perpendicular > 0; //Due to the winding order of corner ABC, this means that the query is inside. + return project_a_perpendicular > 0; // Due to the winding order of corner ABC, this means that the query is inside. } - else //Beyond either A or C, but it could still be inside of the polygon. + else // Beyond either A or C, but it could still be inside of the polygon. { - const coord_t project_a_parallel = dot(ba, bq); //Project not on the perpendicular, but on the original. + const coord_t project_a_parallel = dot(ba, bq); // Project not on the perpendicular, but on the original. const coord_t project_c_parallel = dot(bc, bq); - //Either: - // * A is to the right of B (project_a_perpendicular > 0) and C is below A (project_c_parallel < project_a_parallel), or - // * A is to the left of B (project_a_perpendicular < 0) and C is above A (project_c_parallel > project_a_parallel). + // Either: + // * A is to the right of B (project_a_perpendicular > 0) and C is below A (project_c_parallel < project_a_parallel), or + // * A is to the left of B (project_a_perpendicular < 0) and C is above A (project_c_parallel > project_a_parallel). return (project_c_parallel < project_a_parallel) == (project_a_perpendicular > 0); } } @@ -248,7 +246,7 @@ coord_t LinearAlg2D::getDistFromLine(const Point& p, const Point& a, const Point const Point vab = b - a; const Point vap = p - a; const double ab_size = vSize(vab); - if(ab_size == 0) //Line of 0 length. Assume it's a line perpendicular to the direction to p. + if (ab_size == 0) // Line of 0 length. Assume it's a line perpendicular to the direction to p. { return vSize(vap); } @@ -262,7 +260,7 @@ Point LinearAlg2D::getBisectorVector(const Point& intersect, const Point& a, con constexpr coord_t large_enough = 0xFFFF; const auto a0 = a - intersect; const auto b0 = b - intersect; - return (((a0 * large_enough) / std::max(1LL,vSize(a0))) + ((b0 * large_enough) / std::max(1LL,vSize(b0)))) / 2; + return (((a0 * large_enough) / std::max(1LL, vSize(a0))) + ((b0 * large_enough) / std::max(1LL, vSize(b0)))) / 2; } } // namespace cura From dd23c8ace5675f326e68fc24936ffdd8b4615f0c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 25 Aug 2023 18:26:40 +0200 Subject: [PATCH 467/656] Connect infill: Points equal if < 5 micron, and a little refactoring. Done as part of CURA-10410 --- src/infill.cpp | 116 ++++++++++++++++--------------------------------- 1 file changed, 37 insertions(+), 79 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 14f57b5d88..075f5f01e5 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -838,6 +838,7 @@ void Infill::connectLines(Polygons& result_lines) } } + constexpr coord_t epsilon_squared = 25; const auto half_line_distance_squared = (line_distance * line_distance) / 4; for (size_t polygon_index = 0; polygon_index < inner_contour.size(); polygon_index++) { @@ -864,10 +865,10 @@ void Infill::connectLines(Polygons& result_lines) [&vertex_before, polygon_index, vertex_index](InfillLineSegment* left_hand_side, InfillLineSegment* right_hand_side) { // Find the two endpoints that are relevant. - const Point left_hand_point - = (left_hand_side->start_segment == vertex_index && left_hand_side->start_polygon == polygon_index) ? left_hand_side->start : left_hand_side->end; - const Point right_hand_point - = (right_hand_side->start_segment == vertex_index && right_hand_side->start_polygon == polygon_index) ? right_hand_side->start : right_hand_side->end; + const bool choose_left = (left_hand_side->start_segment == vertex_index && left_hand_side->start_polygon == polygon_index); + const bool choose_right = (right_hand_side->start_segment == vertex_index && right_hand_side->start_polygon == polygon_index); + const Point left_hand_point = choose_left ? left_hand_side->start : left_hand_side->end; + const Point right_hand_point = choose_right ? right_hand_side->start : right_hand_side->end; return vSize(left_hand_point - vertex_before) < vSize(right_hand_point - vertex_before); }); @@ -884,38 +885,30 @@ void Infill::connectLines(Polygons& result_lines) assert(crossing_handle != (size_t)-1); const size_t previous_crossing_handle = connected_lines.find(previous_crossing); assert(previous_crossing_handle != (size_t)-1); - if (crossing_handle - == previous_crossing_handle) // These two infill lines are already connected. Don't create a loop now. Continue connecting with the next crossing. + if (crossing_handle == previous_crossing_handle) { + // These two infill lines are already connected. Don't create a loop now. Continue connecting with the next crossing. continue; } // Join two infill lines together with a connecting line. // Here the InfillLineSegments function as a linked list, so that they can easily be joined. - Point& previous_point - = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) ? previous_segment->start : previous_segment->end; - Point& next_point = (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index) ? crossing->start : crossing->end; + const bool previous_forward = (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index); + const bool next_forward = (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index); + Point& previous_point = previous_forward ? previous_segment->start : previous_segment->end; + Point& next_point = next_forward ? crossing->start : crossing->end; InfillLineSegment* new_segment; // If the segment is near zero length, we avoid creating it but still want to connect the crossing with the previous segment. - constexpr coord_t epsilon_squared = 25; const auto connect_distance_squared = vSize2(previous_point - next_point); if (connect_distance_squared < epsilon_squared) { - if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) - { - previous_segment->previous = crossing; - } - else - { - previous_segment->next = crossing; - } + (previous_forward ? previous_segment->previous : previous_segment->next) = crossing; new_segment = previous_segment; } else { - // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop' just inside the - // boundary. + // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop'. Point intersect; if (connect_distance_squared < half_line_distance_squared && LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) @@ -925,30 +918,16 @@ void Infill::connectLines(Polygons& result_lines) resolveIntersection(infill_line_width, intersect, previous_point, next_point, previous_segment, crossing); } - new_segment - = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); // A connecting line between them. + // A connecting line between them. + new_segment = new InfillLineSegment(previous_point, vertex_index, polygon_index, next_point, vertex_index, polygon_index); new_segment->altered_start = previous_point; new_segment->altered_end = next_point; new_segment->previous = previous_segment; - if (previous_segment->start_segment == vertex_index && previous_segment->start_polygon == polygon_index) - { - previous_segment->previous = new_segment; - } - else - { - previous_segment->next = new_segment; - } + (previous_forward ? previous_segment->previous : previous_segment->next) = new_segment; new_segment->next = crossing; } - if (crossing->start_segment == vertex_index && crossing->start_polygon == polygon_index) - { - crossing->previous = new_segment; - } - else - { - crossing->next = new_segment; - } + (next_forward ? crossing->previous : crossing->next) = new_segment; connected_lines.unite(crossing_handle, previous_crossing_handle); previous_crossing = nullptr; previous_segment = nullptr; @@ -959,49 +938,27 @@ void Infill::connectLines(Polygons& result_lines) if (previous_crossing) { InfillLineSegment* new_segment; - if (vertex_index == previous_segment->start_segment && polygon_index == previous_segment->start_polygon) + + const bool choose_side = (vertex_index == previous_segment->start_segment && polygon_index == previous_segment->start_polygon); + const auto& previous_side = choose_side ? previous_segment->start : previous_segment->end; + if (vSize2(previous_side - vertex_after) < epsilon_squared) { - if (previous_segment->start == vertex_after) - { - // Edge case when an infill line ends directly on top of vertex_after: We skip the extra connecting line segment, as that would be 0-length. - previous_segment = nullptr; - previous_crossing = nullptr; - } - else - { - new_segment = new InfillLineSegment( - previous_segment->start, - vertex_index, - polygon_index, - vertex_after, - (vertex_index + 1) % inner_contour[polygon_index].size(), - polygon_index); - previous_segment->previous = new_segment; - new_segment->previous = previous_segment; - previous_segment = new_segment; - } + // Edge case when an infill line ends directly on top of vertex_after: We skip the extra connecting line segment, as that would be 0-length. + previous_segment = nullptr; + previous_crossing = nullptr; } else { - if (previous_segment->end == vertex_after) - { - // Edge case when an infill line ends directly on top of vertex_after: We skip the extra connecting line segment, as that would be 0-length. - previous_segment = nullptr; - previous_crossing = nullptr; - } - else - { - new_segment = new InfillLineSegment( - previous_segment->end, - vertex_index, - polygon_index, - vertex_after, - (vertex_index + 1) % inner_contour[polygon_index].size(), - polygon_index); - previous_segment->next = new_segment; - new_segment->previous = previous_segment; - previous_segment = new_segment; - } + new_segment = new InfillLineSegment( + previous_side, + vertex_index, + polygon_index, + vertex_after, + (vertex_index + 1) % inner_contour[polygon_index].size(), + polygon_index); + (choose_side ? previous_segment->previous : previous_segment->next) = new_segment; + new_segment->previous = previous_segment; + previous_segment = new_segment; } } @@ -1026,8 +983,9 @@ void Infill::connectLines(Polygons& result_lines) InfillLineSegment* current_infill_line = infill_line; while (current_infill_line->next && current_infill_line->previous) // Until we reached an endpoint. { - const Point next_vertex = (previous_vertex == current_infill_line->start) ? current_infill_line->end : current_infill_line->start; - current_infill_line = (previous_vertex == current_infill_line->start) ? current_infill_line->next : current_infill_line->previous; + const bool choose_side = (previous_vertex == current_infill_line->start); + const Point next_vertex = choose_side ? current_infill_line->end : current_infill_line->start; + current_infill_line = choose_side ? current_infill_line->next : current_infill_line->previous; previous_vertex = next_vertex; } From 60982da097f0f4c4eb83c83d05815a75edd83963 Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 25 Aug 2023 16:53:49 +0000 Subject: [PATCH 468/656] Applied clang-format. --- src/infill.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 075f5f01e5..52a826f293 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -949,13 +949,8 @@ void Infill::connectLines(Polygons& result_lines) } else { - new_segment = new InfillLineSegment( - previous_side, - vertex_index, - polygon_index, - vertex_after, - (vertex_index + 1) % inner_contour[polygon_index].size(), - polygon_index); + new_segment + = new InfillLineSegment(previous_side, vertex_index, polygon_index, vertex_after, (vertex_index + 1) % inner_contour[polygon_index].size(), polygon_index); (choose_side ? previous_segment->previous : previous_segment->next) = new_segment; new_segment->previous = previous_segment; previous_segment = new_segment; From d8896212ae6cc64f7e45bf8862b8dbae35a62854 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 27 Aug 2023 13:56:07 +0200 Subject: [PATCH 469/656] don't alert See https://github.com/benchmark-action/github-action-benchmark/issues/182 --- .github/workflows/benchmark.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 316a9e513f..ef2ca4ee88 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -161,7 +161,7 @@ jobs: tool: 'googlecpp' github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true - alert-threshold: '175%' -# summary-always: true -# comment-on-alert: true +# alert-threshold: '175%' +# summary-always: true +# comment-on-alert: true max-items-in-chart: 250 From 5b492be7da44ef86f1eb85120a85e38f5e0d7d6f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Sun, 27 Aug 2023 13:56:48 +0200 Subject: [PATCH 470/656] don't alert See: https://github.com/benchmark-action/github-action-benchmark/issues/182 --- .github/workflows/gcodeanalyzer.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 17ec5c436e..e31eb1e2fb 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -303,7 +303,7 @@ jobs: tool: customBiggerIsBetter github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true - alert-threshold: '110%' - summary-always: true - comment-on-alert: true - max-items-in-chart: 250 \ No newline at end of file +# alert-threshold: '110%' +# summary-always: true +# comment-on-alert: true + max-items-in-chart: 250 From b6b03ab78056694bc5e24004b771e9b41027658f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 29 Aug 2023 08:15:26 +0200 Subject: [PATCH 471/656] Points toward CURA-10951 Arcus Contributes to CURA-10951 and CURA-10446 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 48a1345cca..fb5c5b4fce 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/cura_10475") # TODO: point to `testing` once the CURA-10475 from libArcus is main + self.requires("arcus/(latest)@ultimaker/cura_10951") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") From 8d7f60caac521ea8361193597b62a0b08747cdc6 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 29 Aug 2023 09:33:23 +0200 Subject: [PATCH 472/656] Use CURA-10475 for Arcus Contributes to CURA-10951 and CURA-10446 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index fb5c5b4fce..42fcd716de 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/cura_10951") + self.requires("arcus/(latest)@ultimaker/cura_10475") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") From 2695411fa75c8002c50ef78dfbfa91d891f17fe8 Mon Sep 17 00:00:00 2001 From: Remco Burema <41987080+rburema@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:44:47 +0200 Subject: [PATCH 473/656] Apply suggestions from code review. CURA-10410 Co-authored-by: Casper Lamboo --- include/utils/linearAlg2D.h | 2 +- src/infill.cpp | 5 ++--- src/utils/LinearAlg2D.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/utils/linearAlg2D.h b/include/utils/linearAlg2D.h index a099496918..97fffe6647 100644 --- a/include/utils/linearAlg2D.h +++ b/include/utils/linearAlg2D.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef UTILS_LINEAR_ALG_2D_H diff --git a/src/infill.cpp b/src/infill.cpp index 52a826f293..be69cc6d23 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -796,8 +796,8 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse // Initialize 'bends'. assert(! bend_a.has_value()); assert(! bend_b.has_value()); - bend_a.emplace(0, 0); - bend_b.emplace(0, 0); + bend_a.emplace(bend_a.X, bend_a.Y); + bend_b.emplace(bend_b.X, bend_b.Y); // Find a bisector of the intersection; specifically, the one that crosses the connection & offset it by 1/2 distance to each side. const auto bisect = LinearAlg2D::getBisectorVector(intersect, connect_start, connect_end); @@ -808,7 +808,6 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse const auto t = s + bisect; // Use both of the resulting lines to place the 'bends' by intersecting with the original line-segments. - Point helper_a, helper_b; assert(LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value())); assert(LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value())); diff --git a/src/utils/LinearAlg2D.cpp b/src/utils/LinearAlg2D.cpp index d9191db723..459ba15236 100644 --- a/src/utils/LinearAlg2D.cpp +++ b/src/utils/LinearAlg2D.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. #include "utils/linearAlg2D.h" From 0e137e2583dd2c453a4851d40bff7e5b5372929f Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 29 Aug 2023 10:46:48 +0200 Subject: [PATCH 474/656] const correctness CURA-10446 --- include/plugins/converters.h | 4 ++-- src/plugins/converters.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 9f76c71b5f..01870b2056 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -87,7 +87,7 @@ struct simplify_request : public details::converter { - native_value_type operator()([[maybe_unused]] native_value_type& original_value, const value_type& message) const; + native_value_type operator()([[maybe_unused]] const native_value_type& original_value, const value_type& message) const; }; struct postprocess_request : public details::converter @@ -97,7 +97,7 @@ struct postprocess_request : public details::converter { - native_value_type operator()([[maybe_unused]] native_value_type& original_value, const value_type& message) const; + native_value_type operator()([[maybe_unused]] const native_value_type& original_value, const value_type& message) const; }; struct infill_generate_request : public details::converter diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index d13c07d92e..e0e9c27a3b 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -125,7 +125,7 @@ simplify_request::value_type } simplify_response::native_value_type - simplify_response::operator()([[maybe_unused]] simplify_response::native_value_type& original_value, const simplify_response::value_type& message) const + simplify_response::operator()([[maybe_unused]] const simplify_response::native_value_type& original_value, const simplify_response::value_type& message) const { native_value_type poly{}; for (const auto& paths : message.polygons().polygons()) @@ -158,7 +158,7 @@ postprocess_request::value_type postprocess_request::operator()(const postproces } postprocess_response::native_value_type - postprocess_response::operator()([[maybe_unused]] postprocess_response::native_value_type& original_value, const postprocess_response::value_type& message) const + postprocess_response::operator()([[maybe_unused]] const postprocess_response::native_value_type& original_value, const postprocess_response::value_type& message) const { return message.gcode_word(); } From a79eb6f9e151975751055ce4af86dadc868216bf Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 29 Aug 2023 11:30:15 +0200 Subject: [PATCH 475/656] Fix debug/release differences. Well that was dumb... my bad, of course it's going to delete the asserts in release-mode, d'oh. part of CURA-10410 --- src/infill.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index be69cc6d23..5b981247c5 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -793,11 +793,9 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse auto& end_a = forward_line_a ? a->altered_end : a->altered_start; auto& end_b = forward_line_b ? b->altered_start : b->altered_end; - // Initialize 'bends'. - assert(! bend_a.has_value()); - assert(! bend_b.has_value()); - bend_a.emplace(bend_a.X, bend_a.Y); - bend_b.emplace(bend_b.X, bend_b.Y); + // Set values ('pre existing' values are needed when feeging these as reference parameters to functions that need a value). + bend_a.emplace(0, 0); + bend_b.emplace(0, 0); // Find a bisector of the intersection; specifically, the one that crosses the connection & offset it by 1/2 distance to each side. const auto bisect = LinearAlg2D::getBisectorVector(intersect, connect_start, connect_end); @@ -808,12 +806,12 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse const auto t = s + bisect; // Use both of the resulting lines to place the 'bends' by intersecting with the original line-segments. - assert(LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value())); - assert(LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value())); + LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value()); + LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value()); // Also set the new end-points. - assert(LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a)); - assert(LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b)); + LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a); + LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b); // The connecting line will be made from the end-points. connect_start = end_a; From eea854cc6ce8bdd07a03695455dd57c9a082b1eb Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 29 Aug 2023 12:12:50 +0200 Subject: [PATCH 476/656] Set vector-lenght _outside_ of the function. This way you don't have to recalculate if you need it later. Suggested by code-review in CURA-10410 Co-authored-by: Casper Lamboo Date: Tue, 29 Aug 2023 12:55:17 +0200 Subject: [PATCH 477/656] Partial revert to solve hang. The exact points are used as start and end-points later on to resolve the total begin/end of the entire loop. So, revert this small improvement that has little to nothing to do with the issue at hand itself. done as part of CURA-10410 --- src/infill.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 541f01abed..59505e74e0 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -836,7 +836,6 @@ void Infill::connectLines(Polygons& result_lines) } } - constexpr coord_t epsilon_squared = 25; const auto half_line_distance_squared = (line_distance * line_distance) / 4; for (size_t polygon_index = 0; polygon_index < inner_contour.size(); polygon_index++) { @@ -898,8 +897,7 @@ void Infill::connectLines(Polygons& result_lines) InfillLineSegment* new_segment; // If the segment is near zero length, we avoid creating it but still want to connect the crossing with the previous segment. - const auto connect_distance_squared = vSize2(previous_point - next_point); - if (connect_distance_squared < epsilon_squared) + if (previous_point == next_point) { (previous_forward ? previous_segment->previous : previous_segment->next) = crossing; new_segment = previous_segment; @@ -908,7 +906,7 @@ void Infill::connectLines(Polygons& result_lines) { // Resolve any intersections of the fill lines close to the boundary, by inserting extra points so the lines don't create a tiny 'loop'. Point intersect; - if (connect_distance_squared < half_line_distance_squared + if (vSize2(previous_point - next_point) < half_line_distance_squared && LinearAlg2D::lineLineIntersection(previous_segment->start, previous_segment->end, crossing->start, crossing->end, intersect) && LinearAlg2D::pointIsProjectedBeyondLine(intersect, previous_segment->start, previous_segment->end) == 0 && LinearAlg2D::pointIsProjectedBeyondLine(intersect, crossing->start, crossing->end) == 0) @@ -939,7 +937,7 @@ void Infill::connectLines(Polygons& result_lines) const bool choose_side = (vertex_index == previous_segment->start_segment && polygon_index == previous_segment->start_polygon); const auto& previous_side = choose_side ? previous_segment->start : previous_segment->end; - if (vSize2(previous_side - vertex_after) < epsilon_squared) + if (previous_side == vertex_after) { // Edge case when an infill line ends directly on top of vertex_after: We skip the extra connecting line segment, as that would be 0-length. previous_segment = nullptr; From 9d035ebe15d4f29e081cbb840d118a6d97cf64f5 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 29 Aug 2023 13:02:35 +0200 Subject: [PATCH 478/656] remove postfix Contributes to CURA-10951 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 42fcd716de..58e40c0719 100644 --- a/conanfile.py +++ b/conanfile.py @@ -43,7 +43,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-alpha.1" + self.version = "5.5.0-alpha" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) From 02c6ac4e5438d6d74f3b0c5d2c08b8e13c5ebc65 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 29 Aug 2023 13:30:45 +0200 Subject: [PATCH 479/656] Don't group outerwalls CURA-10971 --- src/InsetOrderOptimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 46f8c97e44..006296fc68 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -93,7 +93,7 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool detect_loops = false; constexpr Polygons* combing_boundary = nullptr; - constexpr bool group_outer_walls = true; + constexpr bool group_outer_walls = false; // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. PathOrderOptimizer From c755fb1f11b0767d74e8b84ae3c0eac77f77d52e Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 29 Aug 2023 14:52:16 +0200 Subject: [PATCH 480/656] Use arcus from GRPC build conan build fix --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index aca65ece35..8ce9d7a7e1 100644 --- a/conanfile.py +++ b/conanfile.py @@ -72,7 +72,7 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/5.2.2") + self.requires("arcus/(latest)@ultimaker/cura_10475") self.requires("zlib/1.2.12") self.requires("clipper/6.4.2") self.requires("boost/1.79.0") From e23740839befc688084b465f892323e4d37758b5 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 29 Aug 2023 15:01:16 +0200 Subject: [PATCH 481/656] protobuf bumpup conan build fix --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 8ce9d7a7e1..adfca7304e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -64,7 +64,7 @@ def validate(self): def build_requirements(self): self.test_requires("standardprojectsettings/[>=0.1.0]@ultimaker/stable") if self.options.enable_arcus: - self.test_requires("protobuf/3.21.4") + self.test_requires("protobuf/3.21.9") if self.options.enable_testing: self.test_requires("gtest/1.12.1") if self.options.enable_benchmarks: From e537fa6c8caf8aca5c8411283b59be25531f808e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 29 Aug 2023 15:17:24 +0200 Subject: [PATCH 482/656] add line Contributes to CURA-10951 --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 58e40c0719..4778c863bb 100644 --- a/conanfile.py +++ b/conanfile.py @@ -83,6 +83,7 @@ def build_requirements(self): if self.options.enable_benchmarks: self.test_requires("benchmark/1.7.0") + def requirements(self): if self.options.enable_arcus: self.requires("arcus/(latest)@ultimaker/cura_10475") From a56ffc63fb5580bf75b0e738cd4a13ae935c2cce Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 29 Aug 2023 15:37:53 +0200 Subject: [PATCH 483/656] Proposed fix min support area for X/Y-Z override when tree-supports are on. part of CURA-10854 co-authored-by: Thomas Rahm --- include/TreeSupport.h | 2 +- include/TreeSupportTipGenerator.h | 8 ++++---- src/TreeSupportTipGenerator.cpp | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 32f67cd074..1236b6fbb7 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -32,7 +32,7 @@ constexpr auto TREE_PROGRESS_GENERATE_BRANCH_AREAS = TREE_PROGRESS_DRAW_AREAS / constexpr auto TREE_PROGRESS_SMOOTH_BRANCH_AREAS = TREE_PROGRESS_DRAW_AREAS / 3; constexpr auto TREE_PROGRESS_FINALIZE_BRANCH_AREAS = TREE_PROGRESS_DRAW_AREAS / 3; -constexpr auto SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA = 100; +constexpr auto SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA = 100.0; constexpr auto SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS = 1; constexpr auto SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT = false; constexpr auto SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL = false; diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 16985e90dc..7c34f9147c 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -183,14 +183,14 @@ class TreeSupportTipGenerator /*! - * \brief Minimum area an overhang has to have to become a roof. + * \brief Minimum area an overhang has to have to be supported. */ - const double minimum_roof_area; + const double minimum_support_area; /*! - * \brief Minimum area an overhang has to have to be supported. + * \brief Minimum area an overhang has to have to become a roof. */ - const double minimum_support_area; + const double minimum_roof_area; /*! * \brief Amount of layers of roof. Zero if roof is disabled diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index b0ddb5a41b..05d2d989f0 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -31,8 +31,8 @@ namespace cura TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_s) : config(mesh.settings) , use_fake_roof(! mesh.settings.get("support_roof_enable")) - , minimum_roof_area(! use_fake_roof ? mesh.settings.get("minimum_roof_area") : SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA) , minimum_support_area(mesh.settings.get("minimum_support_area")) + , minimum_roof_area(! use_fake_roof ? mesh.settings.get("minimum_roof_area") : std::max(SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA, minimum_support_area)) , support_roof_layers( mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config.layer_height) : use_fake_roof ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS @@ -976,6 +976,11 @@ void TreeSupportTipGenerator::generateTips( { for (Polygons& remaining_overhang_part : remaining_overhang.splitIntoParts(false)) { + if (remaining_overhang_part.area() <= MM2_2INT(minimum_support_area)) + { + continue; + } + std::vector overhang_lines; Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), config.min_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. From 49461d4c85b3eb4322f8bbf53049d2ceeec0f105 Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 29 Aug 2023 13:43:28 +0000 Subject: [PATCH 484/656] Applied clang-format. --- include/TreeSupport.h | 105 ++++++++++++------------------ include/TreeSupportTipGenerator.h | 58 ++++++++++------- 2 files changed, 78 insertions(+), 85 deletions(-) diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 1236b6fbb7..6ad2a5fac4 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -1,5 +1,5 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef TREESUPPORT_H #define TREESUPPORT_H @@ -69,8 +69,6 @@ class TreeSupport private: - - /*! * \brief Precalculates all avoidances, that could be required. * @@ -101,17 +99,14 @@ class TreeSupport * \param reduced_aabb[in,out] The already processed elements. * \param input_aabb[in] Not yet processed elements * \param to_bp_areas[in] The Elements of the current Layer that will reach the buildplate. Value is the influence area where the center of a circle of support may be placed. - * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is not forced to. - * Value is the influence area where the center of a circle of support may be placed. - * \param influence_areas[in] The influence areas without avoidance removed. + * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is + * not forced to. Value is the influence area where the center of a circle of support may be placed. \param influence_areas[in] The influence areas without avoidance removed. * \param insert_bp_areas[out] Elements to be inserted into the main dictionary after the Helper terminates. * \param insert_model_areas[out] Elements to be inserted into the secondary dictionary after the Helper terminates. - * \param insert_influence[out] Elements to be inserted into the dictionary containing the largest possibly valid influence area (ignoring if the area may not be there because of avoidance) - * \param erase[out] Elements that should be deleted from the above dictionaries. - * \param layer_idx[in] The Index of the current Layer. + * \param insert_influence[out] Elements to be inserted into the dictionary containing the largest possibly valid influence area (ignoring if the area may not be there because + * of avoidance) \param erase[out] Elements that should be deleted from the above dictionaries. \param layer_idx[in] The Index of the current Layer. */ - void mergeHelper - ( + void mergeHelper( std::map& reduced_aabb, std::map& input_aabb, const PropertyAreasUnordered& to_bp_areas, @@ -121,8 +116,7 @@ class TreeSupport PropertyAreasUnordered& insert_model_areas, PropertyAreasUnordered& insert_influence, std::vector& erase, - const LayerIndex layer_idx - ); + const LayerIndex layer_idx); /*! * \brief Merges Influence Areas if possible. @@ -132,28 +126,23 @@ class TreeSupport * * \param to_bp_areas[in] The Elements of the current Layer that will reach the buildplate. * Value is the influence area where the center of a circle of support may be placed. - * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is not forced to. - * Value is the influence area where the center of a circle of support may be placed. - * \param influence_areas[in] The Elements of the current Layer without avoidances removed. This is the largest possible influence area for this layer. - * Value is the influence area where the center of a circle of support may be placed. - * \param layer_idx[in] The current layer. + * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is + * not forced to. Value is the influence area where the center of a circle of support may be placed. \param influence_areas[in] The Elements of the current Layer without + * avoidances removed. This is the largest possible influence area for this layer. Value is the influence area where the center of a circle of support may be placed. \param + * layer_idx[in] The current layer. */ - void mergeInfluenceAreas - ( - PropertyAreasUnordered& to_bp_areas, - PropertyAreas& to_model_areas, - PropertyAreas& influence_areas, - LayerIndex layer_idx - ); + void mergeInfluenceAreas(PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, LayerIndex layer_idx); /*! * \brief Checks if an influence area contains a valid subsection and returns the corresponding metadata and the new Influence area. * * Calculates an influence areas of the layer below, based on the influence area of one element on the current layer. - * Increases every influence area by maximum_move_distance_slow. If this is not enough, as in we would change our gracious or to_buildplate status the influence areas are instead increased by maximum_move_distance_slow. - * Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead. + * Increases every influence area by maximum_move_distance_slow. If this is not enough, as in we would change our gracious or to_buildplate status the influence areas are + * instead increased by maximum_move_distance_slow. Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this + * were the case, the radius is not increased instead. * - * Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations. + * Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was + * done to avoid not needed heap allocations. * * \param settings[in] Which settings have to be used to check validity. * \param layer_idx[in] Number of the current layer. @@ -161,13 +150,12 @@ class TreeSupport * \param relevant_offset[in] The maximal possible influence area. No guarantee regarding validity with current layer collision required, as it is ensured in-function! * \param to_bp_data[out] The part of the Influence area that can reach the buildplate. * \param to_model_data[out] The part of the Influence area that do not have to reach the buildplate. This has overlap with new_layer_data. - * \param increased[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings. - * \param overspeed[in] How much should the already offset area be offset again. Usually this is 0. - * \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging. - * \return A valid support element for the next layer regarding the calculated influence areas. Empty if no influence are can be created using the supplied influence area and settings. + * \param increased[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the + * user-supplied settings. \param overspeed[in] How much should the already offset area be offset again. Usually this is 0. \param mergelayer[in] Will the merge method be + * called on this layer. This information is required as some calculation can be avoided if they are not required for merging. \return A valid support element for the next + * layer regarding the calculated influence areas. Empty if no influence are can be created using the supplied influence area and settings. */ - std::optional increaseSingleArea - ( + std::optional increaseSingleArea( AreaIncreaseSettings settings, LayerIndex layer_idx, TreeSupportElement* parent, @@ -176,37 +164,35 @@ class TreeSupport Polygons& to_model_data, Polygons& increased, const coord_t overspeed, - const bool mergelayer - ); + const bool mergelayer); /*! * \brief Increases influence areas as far as required. * * Calculates influence areas of the layer below, based on the influence areas of the current layer. - * Increases every influence area by maximum_move_distance_slow. If this is not enough, as in it would change the gracious or to_buildplate status, the influence areas are instead increased by maximum_move_distance. - * Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead. + * Increases every influence area by maximum_move_distance_slow. If this is not enough, as in it would change the gracious or to_buildplate status, the influence areas are + * instead increased by maximum_move_distance. Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were + * the case, the radius is not increased instead. * - * Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations. + * Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was + * done to avoid not needed heap allocations. * * \param to_bp_areas[out] Influence areas that can reach the buildplate - * \param to_model_areas[out] Influence areas that do not have to reach the buildplate. This has overlap with new_layer_data, as areas that can reach the buildplate are also considered valid areas to the model. - * This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area. - * \param influence_areas[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings. - * \param bypass_merge_areas[out] Influence areas ready to be added to the layer below that do not need merging. - * \param last_layer[in] Influence areas of the current layer. - * \param layer_idx[in] Number of the current layer. - * \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging. + * \param to_model_areas[out] Influence areas that do not have to reach the buildplate. This has overlap with new_layer_data, as areas that can reach the buildplate are also + * considered valid areas to the model. This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area. \param + * influence_areas[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the + * user-supplied settings. \param bypass_merge_areas[out] Influence areas ready to be added to the layer below that do not need merging. \param last_layer[in] Influence areas + * of the current layer. \param layer_idx[in] Number of the current layer. \param mergelayer[in] Will the merge method be called on this layer. This information is required as + * some calculation can be avoided if they are not required for merging. */ - void increaseAreas - ( + void increaseAreas( PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, std::vector& bypass_merge_areas, const std::vector& last_layer, const LayerIndex layer_idx, - const bool mergelayer - ); + const bool mergelayer); /*! * \brief Propagates influence downwards, and merges overlapping ones. @@ -245,15 +231,13 @@ class TreeSupport * * \param linear_data[in] All currently existing influence areas with the layer they are on * \param layer_tree_polygons[out] Resulting branch areas with the layerindex they appear on. - * layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector) corresponding branch area in layer_tree_polygons. - * \param inverse_tree_order[in] A mapping that returns the child of every influence area. + * layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector) + * corresponding branch area in layer_tree_polygons. \param inverse_tree_order[in] A mapping that returns the child of every influence area. */ - void generateBranchAreas - ( + void generateBranchAreas( std::vector>& linear_data, std::vector>& layer_tree_polygons, - const std::map& inverse_tree_order - ); + const std::map& inverse_tree_order); /*! * \brief Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole. @@ -270,13 +254,11 @@ class TreeSupport * \param dropped_down_areas[out] Areas that have to be added to support all non-graceful areas. * \param inverse_tree_order[in] A mapping that returns the child of every influence area. */ - void dropNonGraciousAreas - ( + void dropNonGraciousAreas( std::vector>& layer_tree_polygons, const std::vector>& linear_data, std::vector>>& dropped_down_areas, - const std::map& inverse_tree_order - ); + const std::map& inverse_tree_order); void filterFloatingLines(std::vector& support_layer_storage); @@ -335,7 +317,6 @@ class TreeSupport * Required for the progress bar the behave as expected when areas have to be calculated multiple times */ double progress_offset = 0; - }; diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 7c34f9147c..8d5def3997 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -2,6 +2,7 @@ #define TREESUPPORTTIPGENERATOR_H #include "TreeModelVolumes.h" +#include "TreeSupport.h" #include "TreeSupportBaseCircle.h" #include "TreeSupportElement.h" #include "TreeSupportEnums.h" @@ -12,7 +13,6 @@ #include "sliceDataStorage.h" #include "utils/Coord_t.h" #include "utils/polygon.h" -#include "TreeSupport.h" namespace cura { @@ -20,12 +20,10 @@ namespace cura class TreeSupportTipGenerator { - public: - TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_); - ~ TreeSupportTipGenerator() + ~TreeSupportTipGenerator() { if (cross_fill_provider) { @@ -43,10 +41,14 @@ class TreeSupportTipGenerator * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. */ - void generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh ,std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas); + void generateTips( + SliceDataStorage& storage, + const SliceMeshStorage& mesh, + std::vector>& move_bounds, + std::vector& additional_support_areas, + std::vector& placed_support_lines_support_areas); private: - enum class LineStatus { INVALID, @@ -85,17 +87,16 @@ class TreeSupportTipGenerator std::function)> getEvaluatePointForNextLayerFunction(size_t current_layer); /*! - * \brief Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using supplied lambda. + * \brief Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using + * supplied lambda. * * \param lines[in] The lines that have to be evaluated. * \param evaluatePoint[in] The function used to evaluate the points. * \return A pair with which points are still valid in the first slot and which are not in the second slot. */ - std::pair, std::vector> splitLines - ( - std::vector lines, - std::function)> evaluatePoint - ); // assumes all Points on the current line are valid + std::pair, std::vector> splitLines( + std::vector lines, + std::function)> evaluatePoint); // assumes all Points on the current line are valid /*! * \brief Ensures that every line segment is about distance in length. The resulting lines may differ from the original but all points are on the original @@ -125,7 +126,7 @@ class TreeSupportTipGenerator * \param result[out] The dropped overhang ares * \param roof[in] Whether the result is for roof generation. */ - void dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof ); + void dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof); /*! * \brief Calculates which areas should be supported with roof, and saves these in roof support_roof_drawn @@ -143,7 +144,15 @@ class TreeSupportTipGenerator * \param roof[in] Whether the tip supports a roof. * \param skip_ovalisation[in] Whether the tip may be ovalized when drawn later. */ - void addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); + void addPointAsInfluenceArea( + std::vector>& move_bounds, + std::pair p, + size_t dtt, + LayerIndex insert_layer, + size_t dont_move_until, + bool roof, + bool skip_ovalisation, + std::vector additional_ovalization_targets = std::vector()); /*! @@ -155,7 +164,14 @@ class TreeSupportTipGenerator * \param supports_roof[in] Whether the tip supports a roof. * \param dont_move_until[in] Until which dtt the branch should not move if possible. */ - void addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, size_t dont_move_until, bool connect_points); + void addLinesAsInfluenceAreas( + std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + bool supports_roof, + size_t dont_move_until, + bool connect_points); /*! * \brief Remove tips that should not have been added in the first place. @@ -163,7 +179,7 @@ class TreeSupportTipGenerator * \param storage[in] Background storage, required for adding roofs. * \param additional_support_areas[in] Areas that should have been roofs, but are now support, as they would not generate any lines as roof. */ - void removeUselessAddedPoints(std::vector>& move_bounds,SliceDataStorage& storage, std::vector& additional_support_areas); + void removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage, std::vector& additional_support_areas); /*! @@ -264,7 +280,8 @@ class TreeSupportTipGenerator const bool only_gracious = SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL; /*! - * \brief Whether minimum_roof_area is a hard limit. If false the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof further up/down. + * \brief Whether minimum_roof_area is a hard limit. If false the roof will be combined with roof above and below, to see if a part of this roof may be part of a valid roof + * further up/down. */ const bool force_minimum_roof_area = SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT; @@ -294,15 +311,10 @@ class TreeSupportTipGenerator std::vector roof_tips_drawn; - - std::mutex critical_move_bounds; std::mutex critical_roof_tips; - - - }; -} +} // namespace cura #endif /* TREESUPPORT_H */ \ No newline at end of file From 14c3036d1dead4b1b16b2eb206b2d37f36146f38 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 29 Aug 2023 15:54:16 +0200 Subject: [PATCH 485/656] Fix initial bed temperature with one-at-a-time Bed temperature was not applied for Griffin-flavored GCode printers CURA-8889 --- include/gcodeExport.h | 5 +++++ src/gcodeExport.cpp | 37 +++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 47e423fb65..890b328b7c 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -461,6 +461,11 @@ class GCodeExport : public NoCopy */ void writeMoveBFB(const int x, const int y, const int z, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature); + /*! + * Set bed temperature for the initial layer. Called by 'processInitialLayerTemperatures'. + */ + void processInitialLayerBedTemperature(); + public: /*! * Get ready for extrusion moves: diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 0b51882f45..3fbd298ac8 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -706,6 +706,26 @@ bool GCodeExport::initializeExtruderTrains(const SliceDataStorage& storage, cons return should_prime_extruder; } +void GCodeExport::processInitialLayerBedTemperature() +{ + Scene& scene = Application::getInstance().current_slice->scene; + + if (scene.current_mesh_group->settings.get("material_bed_temp_prepend") && scene.current_mesh_group->settings.get("machine_heated_bed")) + { + const Temperature bed_temp = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); + if (scene.current_mesh_group == scene.mesh_groups.begin() // Always write bed temperature for first mesh group. + || bed_temp + != (scene.current_mesh_group - 1) + ->settings.get("material_bed_temperature")) // Don't write bed temperature if identical to temperature of previous group. + { + if (bed_temp != 0) + { + writeBedTemperatureCommand(bed_temp, scene.current_mesh_group->settings.get("material_bed_temp_wait")); + } + } + } +} + void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage, const size_t start_extruder_nr) { Scene& scene = Application::getInstance().current_slice->scene; @@ -713,6 +733,8 @@ void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage if (getFlavor() == EGCodeFlavor::GRIFFIN) { + processInitialLayerBedTemperature(); + ExtruderTrain& train = scene.extruders[start_extruder_nr]; constexpr bool wait = true; const Temperature print_temp_0 = train.settings.get("material_print_temperature_layer_0"); @@ -728,20 +750,7 @@ void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage writeLine(tmp.str().c_str()); } - if (scene.current_mesh_group->settings.get("material_bed_temp_prepend") && scene.current_mesh_group->settings.get("machine_heated_bed")) - { - const Temperature bed_temp = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); - if (scene.current_mesh_group == scene.mesh_groups.begin() // Always write bed temperature for first mesh group. - || bed_temp - != (scene.current_mesh_group - 1) - ->settings.get("material_bed_temperature")) // Don't write bed temperature if identical to temperature of previous group. - { - if (bed_temp != 0) - { - writeBedTemperatureCommand(bed_temp, scene.current_mesh_group->settings.get("material_bed_temp_wait")); - } - } - } + processInitialLayerBedTemperature(); if (scene.current_mesh_group->settings.get("material_print_temp_prepend")) { From 49dd39dc7d980884e5e2ac80833c5c4df1609429 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 29 Aug 2023 17:09:26 +0200 Subject: [PATCH 486/656] Fix negative flows We were trying to retract the whole model back in the nozzle --- src/settings/PathConfigStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index e32e9afb15..2f9dd2acea 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -138,7 +138,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye GCodePathConfig{ .type = PrintFeatureType::Support, .line_width = static_cast(support_infill_train.settings.get("support_line_width") * support_infill_line_width_factor), .layer_thickness = layer_thickness, - .flow = -support_infill_train.settings.get("support_material_flow") + .flow = support_infill_train.settings.get("support_material_flow") * ((layer_nr == 0) ? support_infill_train.settings.get("material_flow_layer_0") : Ratio(1.0)) * (combine_idx + 1), .speed_derivatives = SpeedDerivatives{ .speed = support_infill_train.settings.get("speed_support_infill"), .acceleration = support_infill_train.settings.get("acceleration_support_infill"), From 326437db0c857a4fbc5d909cc543aa536f58ef54 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Wed, 30 Aug 2023 11:25:28 +0200 Subject: [PATCH 487/656] Update comment to reflect revert CURA-10410 --- src/infill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infill.cpp b/src/infill.cpp index 59505e74e0..75196336a6 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -896,7 +896,7 @@ void Infill::connectLines(Polygons& result_lines) Point& next_point = next_forward ? crossing->start : crossing->end; InfillLineSegment* new_segment; - // If the segment is near zero length, we avoid creating it but still want to connect the crossing with the previous segment. + // If the segment is near length, we avoid creating it but still want to connect the crossing with the previous segment. if (previous_point == next_point) { (previous_forward ? previous_segment->previous : previous_segment->next) = crossing; From 02c83d610120f1f39f552d81529333ca9239d101 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 16:35:34 +0200 Subject: [PATCH 488/656] Extend grpc modify path messages CURA-10446 --- src/plugins/converters.cpp | 93 +++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index e0e9c27a3b..8214f402f4 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -329,13 +329,6 @@ gcode_paths_modify_request::value_type for (const auto& path : paths) { auto* gcode_path = gcode_paths->Add(); - gcode_path->set_space_fill_type(getSpaceFillType(path.space_fill_type)); - - gcode_path->set_flow(path.flow); - gcode_path->set_width_factor(path.width_factor); - gcode_path->set_spiralize(path.spiralize); - gcode_path->set_speed_factor(path.speed_factor); - gcode_path->set_mesh_name(path.mesh ? path.mesh->mesh_name : ""); // Construct the OpenPath from the points in a GCodePath for (const auto& point : path.points) { @@ -343,17 +336,27 @@ gcode_paths_modify_request::value_type points->set_x(point.X); points->set_y(point.Y); } - - auto* config_msg = gcode_path->mutable_config(); - config_msg->set_feature(getPrintFeature(path.config.type)); - config_msg->set_line_width(path.config.getLineWidth()); - config_msg->set_layer_thickness(path.config.getLayerThickness()); - config_msg->set_flow_ratio(path.config.getFlowRatio()); - config_msg->set_is_bridge_path(path.config.isBridgePath()); - config_msg->set_fan_speed(path.config.getFanSpeed()); - config_msg->mutable_speed_derivatives()->set_velocity(path.config.getSpeed()); - config_msg->mutable_speed_derivatives()->set_acceleration(path.config.getAcceleration()); - config_msg->mutable_speed_derivatives()->set_jerk(path.config.getJerk()); + gcode_path->set_space_fill_type(getSpaceFillType(path.space_fill_type)); + gcode_path->set_flow(path.flow); + gcode_path->set_width_factor(path.width_factor); + gcode_path->set_spiralize(path.spiralize); + gcode_path->set_speed_factor(path.speed_factor); + gcode_path->set_speed_back_pressure_factor(path.speed_back_pressure_factor); + gcode_path->set_retract(path.retract); + gcode_path->set_unretract_before_last_travel_move(path.unretract_before_last_travel_move); + gcode_path->set_perform_z_hop(path.perform_z_hop); + gcode_path->set_skip_agressive_merge_hint(path.skip_agressive_merge_hint); + gcode_path->set_done(path.done); + gcode_path->set_fan_speed(path.getFanSpeed()); + gcode_path->set_mesh_name(path.mesh ? path.mesh->mesh_name : ""); + gcode_path->set_feature(getPrintFeature(path.config.type)); + gcode_path->mutable_speed_derivatives()->set_velocity(path.config.getSpeed()); + gcode_path->mutable_speed_derivatives()->set_acceleration(path.config.getAcceleration()); + gcode_path->mutable_speed_derivatives()->set_jerk(path.config.getJerk()); + gcode_path->set_line_width(path.config.getLineWidth()); + gcode_path->set_layer_thickness(path.config.getLayerThickness()); + gcode_path->set_flow_ratio(path.config.getFlowRatio()); + gcode_path->set_is_bridge_path(path.config.isBridgePath()); } return message; @@ -413,22 +416,14 @@ gcode_paths_modify_request::value_type [[nodiscard]] GCodePathConfig gcode_paths_modify_response::buildConfig(const v0::GCodePath& path) { - const coord_t line_width = path.config().line_width(); - const coord_t layer_height = path.config().layer_thickness(); - const Ratio flow = path.config().flow_ratio(); - const SpeedDerivatives speed_derivatives{ .speed = path.config().speed_derivatives().velocity(), - .acceleration = path.config().speed_derivatives().acceleration(), - .jerk = path.config().speed_derivatives().jerk() }; - const bool is_bridge_path = path.config().is_bridge_path(); - const double fan_speed = path.config().fan_speed(); - const auto feature_type = getPrintFeatureType(path.config().feature()); - return { .type = feature_type, - .line_width = line_width, - .layer_thickness = layer_height, - .flow = flow, - .speed_derivatives = speed_derivatives, - .is_bridge_path = is_bridge_path, - .fan_speed = fan_speed }; + return { .type = getPrintFeatureType(path.feature()), + .line_width = path.line_width(), + .layer_thickness = path.layer_thickness(), + .flow = path.flow_ratio(), + .speed_derivatives + = SpeedDerivatives{ .speed = path.speed_derivatives().velocity(), .acceleration = path.speed_derivatives().acceleration(), .jerk = path.speed_derivatives().jerk() }, + .is_bridge_path = path.is_bridge_path(), + .fan_speed = path.fan_speed() }; } gcode_paths_modify_response::native_value_type @@ -451,19 +446,23 @@ gcode_paths_modify_response::native_value_type for (const auto& gcode_path_msg : message.gcode_paths()) { - const GCodePathConfig config = buildConfig(gcode_path_msg); - const Ratio flow = gcode_path_msg.flow(); - const Ratio width_factor = gcode_path_msg.width_factor(); - const bool spiralize = gcode_path_msg.spiralize(); - const Ratio speed_factor = gcode_path_msg.speed_factor(); - const auto space_fill_type = getSpaceFillType(gcode_path_msg.space_fill_type()); - GCodePath path{ .config = config, - .mesh = gcode_path_msg.mesh_name().empty() ? nullptr : meshes.at(gcode_path_msg.mesh_name()), - .space_fill_type = space_fill_type, - .flow = flow, - .width_factor = width_factor, - .spiralize = spiralize, - .speed_factor = speed_factor }; + GCodePath path { + .config = buildConfig(gcode_path_msg), + .mesh = gcode_path_msg.mesh_name().empty() ? nullptr : meshes.at(gcode_path_msg.mesh_name()), + .space_fill_type = getSpaceFillType(gcode_path_msg.space_fill_type()), + .flow = gcode_path_msg.flow(), + .width_factor = gcode_path_msg.width_factor(), + .spiralize = gcode_path_msg.spiralize(), + .speed_factor = gcode_path_msg.speed_factor(), + .speed_back_pressure_factor = gcode_path_msg.speed_back_pressure_factor(), + .retract = gcode_path_msg.retract(), + .unretract_before_last_travel_move = gcode_path_msg.unretract_before_last_travel_move(), + .perform_z_hop = gcode_path_msg.perform_z_hop(), + .skip_agressive_merge_hint = gcode_path_msg.skip_agressive_merge_hint(), + .done = gcode_path_msg.done(), + .fan_speed = gcode_path_msg.fan_speed(), + }; + path.points = gcode_path_msg.path().path() | ranges::views::transform( [](const auto& point_msg) From 4568c0ed6c6659d8ad22b3a501bdf102e286a3f4 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Wed, 30 Aug 2023 14:36:18 +0000 Subject: [PATCH 489/656] Applied clang-format. --- src/plugins/converters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 8214f402f4..33e7d4882e 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -446,7 +446,7 @@ gcode_paths_modify_response::native_value_type for (const auto& gcode_path_msg : message.gcode_paths()) { - GCodePath path { + GCodePath path{ .config = buildConfig(gcode_path_msg), .mesh = gcode_path_msg.mesh_name().empty() ? nullptr : meshes.at(gcode_path_msg.mesh_name()), .space_fill_type = getSpaceFillType(gcode_path_msg.space_fill_type()), From 9614f94bb5292f838a3c3bfc91b6da5684e3c0bc Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 16:57:51 +0200 Subject: [PATCH 490/656] Don't print wall type line comment with each path Resolves gcode comment spam CURA-10446 --- src/LayerPlan.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index a5553a8346..0ba0bf6cbc 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2070,8 +2070,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeZhopEnd(); } } - // TODO re-enable the config_changed check - const auto& extruder_changed = ! last_extrusion_config.has_value() /* || last_extrusion_config.value() != path.config */; + const auto& extruder_changed = ! last_extrusion_config.has_value() || (last_extrusion_config.value().type != path.config.type); if (! path.config.isTravelPath() && extruder_changed) { gcode.writeTypeComment(path.config.type); @@ -2079,8 +2078,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { gcode.writeComment("BRIDGE"); } - // TODO uncomment next line, make path.config copyable - // last_extrusion_config = path.config; + last_extrusion_config = path.config; update_extrusion_offset = true; } else From bdc14ab76a1fb91570d75b1670c8bd3290a6910b Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 17:31:36 +0200 Subject: [PATCH 491/656] Use latest grpc definitions CURA-10446 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index fd4966cd98..02bb418c9a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -88,7 +88,7 @@ def requirements(self): self.requires("arcus/(latest)@ultimaker/cura_10475") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/latest@ultimaker/cura_10446") + self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 60bd5dd82e2486131554a1dc4bfe2337a32c8e6d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 31 Aug 2023 04:52:49 +0200 Subject: [PATCH 492/656] updated required conan version Contribute to CURA-10951 --- conanfile.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 5f0f1993e1..49751fb6b0 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,7 +2,6 @@ # CuraEngine is released under the terms of the AGPLv3 or higher from os import path -from pathlib import Path from conan import ConanFile from conan.errors import ConanInvalidConfiguration @@ -11,7 +10,7 @@ from conan.tools.build import check_min_cppstd from conan.tools.scm import Version -required_conan_version = ">=1.54 <=1.56.0 || >=1.58.0 <2.0.0" +required_conan_version = ">=1.58.0 <2.0.0" class CuraEngineConan(ConanFile): From a56b5d5c14aac2fc39a4dea944dadf69ce93537f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 31 Aug 2023 05:52:35 +0200 Subject: [PATCH 493/656] Use skip_test and update gcc --- .github/workflows/benchmark.yml | 21 ++++++++++++--------- .github/workflows/gcodeanalyzer.yml | 21 ++++++++++++--------- .github/workflows/lint-tidier.yml | 13 ++++++++----- .github/workflows/unit-test.yml | 15 +++++++++------ conanfile.py | 10 ++++------ 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index ef2ca4ee88..e3b80ad8ce 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -76,7 +76,7 @@ jobs: - name: Setup Python and pip uses: actions/setup-python@v4 with: - python-version: '3.10.x' + python-version: '3.11.x' architecture: 'x64' cache: 'pip' cache-dependency-path: .github/workflows/requirements-conan-package.txt @@ -103,16 +103,19 @@ jobs: sudo apt upgrade sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - name: Install GCC-12 on ubuntu-22.04 + - name: Install GCC-132 on ubuntu run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 + + - name: Create the default Conan profile + run: conan profile new default --detect - name: Get Conan configuration run: | conan config install https://github.com/Ultimaker/conan-config.git - conan profile new default --detect + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Use Conan download cache (Bash) if: ${{ runner.os != 'Windows' }} @@ -161,7 +164,7 @@ jobs: tool: 'googlecpp' github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true -# alert-threshold: '175%' -# summary-always: true -# comment-on-alert: true + # alert-threshold: '175%' + # summary-always: true + # comment-on-alert: true max-items-in-chart: 250 diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index e31eb1e2fb..123aa31d1b 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -135,22 +135,25 @@ jobs: sudo apt upgrade sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - name: Install GCC-12 on ubuntu-22.04 + - name: Install GCC-132 on ubuntu run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 + + - name: Create the default Conan profile + run: conan profile new default --detect - name: Get Conan configuration run: | conan config install https://github.com/Ultimaker/conan-config.git - conan profile new default --detect + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Use Conan download cache (Bash) run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=False -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=False -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv -c tools.build:skip_test=True working-directory: CuraEngine - name: Upload the Dependency package(s) @@ -303,7 +306,7 @@ jobs: tool: customBiggerIsBetter github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true -# alert-threshold: '110%' -# summary-always: true -# comment-on-alert: true + # alert-threshold: '110%' + # summary-always: true + # comment-on-alert: true max-items-in-chart: 250 diff --git a/.github/workflows/lint-tidier.yml b/.github/workflows/lint-tidier.yml index 2504eeda4a..d178f52f3a 100644 --- a/.github/workflows/lint-tidier.yml +++ b/.github/workflows/lint-tidier.yml @@ -49,16 +49,19 @@ jobs: sudo apt upgrade sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - name: Install GCC-12 on ubuntu-22.04 + - name: Install GCC-132 on ubuntu run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 + + - name: Create the default Conan profile + run: conan profile new default --detect - name: Get Conan configuration run: | - conan profile new default --detect conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Install dependencies run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_testing=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 9d8b9a0ed6..04e325df79 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -83,16 +83,19 @@ jobs: sudo apt upgrade sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - name: Install GCC-12 on ubuntu-22.04 + - name: Install GCC-132 on ubuntu run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 + + - name: Create the default Conan profile + run: conan profile new default --detect - name: Get Conan configuration run: | conan config install https://github.com/Ultimaker/conan-config.git - conan profile new default --detect + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Use Conan download cache (Bash) if: ${{ runner.os != 'Windows' }} @@ -108,7 +111,7 @@ jobs: key: conan-${{ runner.os }}-${{ runner.arch }} - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_testing=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv - name: Upload the Dependency package(s) run: conan upload "*" -r cura --all -c diff --git a/conanfile.py b/conanfile.py index adfca7304e..cf22b1e40e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ from conan.tools.build import check_min_cppstd from conan.tools.scm import Version -required_conan_version = ">=1.54 <=1.56.0 || >=1.58.0 <2.0.0" +required_conan_version = ">=1.58.0 <2.0.0" class CuraEngineConan(ConanFile): @@ -25,13 +25,11 @@ class CuraEngineConan(ConanFile): options = { "enable_arcus": [True, False], - "enable_testing": [True, False], "enable_benchmarks": [True, False], "enable_extensive_warnings": [True, False] } default_options = { "enable_arcus": True, - "enable_testing": False, "enable_benchmarks": False, "enable_extensive_warnings": False, } @@ -65,7 +63,7 @@ def build_requirements(self): self.test_requires("standardprojectsettings/[>=0.1.0]@ultimaker/stable") if self.options.enable_arcus: self.test_requires("protobuf/3.21.9") - if self.options.enable_testing: + if not self.conf.get("tools.build:skip_test", False, check_type=bool): self.test_requires("gtest/1.12.1") if self.options.enable_benchmarks: self.test_requires("benchmark/1.7.0") @@ -90,7 +88,7 @@ def generate(self): tc = CMakeToolchain(self) tc.variables["CURA_ENGINE_VERSION"] = self.version tc.variables["ENABLE_ARCUS"] = self.options.enable_arcus - tc.variables["ENABLE_TESTING"] = self.options.enable_testing + tc.variables["ENABLE_TESTING"] = not self.conf.get("tools.build:skip_test", False, check_type=bool) tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings tc.variables["OLDER_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) < "14" @@ -102,7 +100,7 @@ def generate(self): copy(self, "*.dll", dep.cpp_info.libdirs[0], self.build_folder) if len(dep.cpp_info.bindirs) > 0: copy(self, "*.dll", dep.cpp_info.bindirs[0], self.build_folder) - if self.options.enable_testing: + if not self.conf.get("tools.build:skip_test", False, check_type=bool): test_path = path.join(self.build_folder, "tests") if not path.exists(test_path): mkdir(self, test_path) From 1d94f07a304536d0d84fbb36aa7273d94afe7e43 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 31 Aug 2023 06:14:33 +0200 Subject: [PATCH 494/656] Enable the test_helper target for benchmarks as well Contribute to CURA-10475 --- CMakeLists.txt | 28 ++++++++++++++++++++++++++++ tests/CMakeLists.txt | 18 ------------------ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86cc733242..a206a00c2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,34 @@ target_link_libraries(CuraEngine PRIVATE _CuraEngine) target_compile_definitions(CuraEngine PRIVATE VERSION=\"${CURA_ENGINE_VERSION}\") # Compiling the test environment. +if (ENABLE_TESTING OR ENABLE_BENCHMARKS) + set(TESTS_HELPERS_SRC tests/ReadTestPolygons.cpp) + + set(TESTS_SRC_ARCUS) + if (ENABLE_ARCUS) + list(APPEND TESTS_SRC_ARCUS + ArcusCommunicationTest + ArcusCommunicationPrivateTest) + list(APPEND TESTS_HELPERS_SRC tests/arcus/MockSocket.cpp) + endif () + + add_library(test_helpers ${TESTS_HELPERS_SRC}) + target_compile_definitions(test_helpers PUBLIC $<$:BUILD_TESTS> $<$:ARCUS>) + target_include_directories(test_helpers PUBLIC "include" ${CMAKE_BINARY_DIR}/generated) + target_link_libraries(test_helpers PRIVATE + _CuraEngine + GTest::gtest + GTest::gmock + clipper::clipper + curaengine_grpc_definitions::curaengine_grpc_definitions + asio-grpc::asio-grpc + grpc::grpc + protobuf::libprotobuf) + if (ENABLE_ARCUS) + target_link_libraries(test_helpers PUBLIC arcus::arcus) + endif () +endif () + if (ENABLE_TESTING) enable_testing() add_subdirectory(tests) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b776a270b1..1e36893cee 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,24 +40,6 @@ set(TESTS_SRC_UTILS UnionFindTest ) -set(TESTS_HELPERS_SRC ReadTestPolygons.cpp) - -set(TESTS_SRC_ARCUS) -if (ENABLE_ARCUS) - list(APPEND TESTS_SRC_ARCUS - ArcusCommunicationTest - ArcusCommunicationPrivateTest) - list(APPEND TESTS_HELPERS_SRC arcus/MockSocket.cpp) -endif () - -add_library(test_helpers ${TESTS_HELPERS_SRC}) -target_compile_definitions(test_helpers PUBLIC $<$:BUILD_TESTS> $<$:ARCUS>) -target_include_directories(test_helpers PUBLIC "../include" ${CMAKE_BINARY_DIR}/generated) -target_link_libraries(test_helpers PRIVATE _CuraEngine GTest::gtest GTest::gmock clipper::clipper) -if (ENABLE_ARCUS) - target_link_libraries(test_helpers PUBLIC arcus::arcus protobuf::libprotobuf) -endif () - foreach (test ${TESTS_SRC_BASE}) add_executable(${test} main.cpp ${test}.cpp) add_test(NAME ${test} COMMAND "${test}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") From e3480d99bd957a541566472f09903e048bd6257d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 31 Aug 2023 07:36:49 +0200 Subject: [PATCH 495/656] enable threads for executable hoping this will solve the `undefined reference to `__libc_single_threaded@GLIBC_2.32'` on ubuntu-20.04 with gcc-13 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 202fb5bf61..6df47ac89d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,7 +213,7 @@ else () add_executable(CuraEngine src/main.cpp ${RES_FILES}) # ..., but don't forget the glitter! endif (NOT WIN32) -# Create the executable +use_threads(CuraEngine) target_link_libraries(CuraEngine PRIVATE _CuraEngine) target_compile_definitions(CuraEngine PRIVATE VERSION=\"${CURA_ENGINE_VERSION}\") @@ -225,4 +225,4 @@ endif () if (ENABLE_BENCHMARKS) add_subdirectory(benchmark) -endif() \ No newline at end of file +endif() From ac58f6b04fad6cc5c84fd29c75ef96d2f79ce99f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 31 Aug 2023 08:44:07 +0200 Subject: [PATCH 496/656] use latest version as specified as conan 2 Contribute to CURA-10475 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index be2badae8f..51fdfe6adb 100644 --- a/conanfile.py +++ b/conanfile.py @@ -86,7 +86,7 @@ def requirements(self): self.requires("arcus/(latest)@ultimaker/cura_10475") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/latest@ultimaker/testing") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From b3c5816fbcaa802a4ce3ffebd0d7cee677748d82 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 31 Aug 2023 09:24:20 +0200 Subject: [PATCH 497/656] Force gh runners --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 51fdfe6adb..0176fd6091 100644 --- a/conanfile.py +++ b/conanfile.py @@ -105,6 +105,7 @@ def generate(self): deps = CMakeDeps(self) deps.generate() + tc = CMakeToolchain(self) tc.variables["CURA_ENGINE_VERSION"] = self.version tc.variables["ENABLE_ARCUS"] = self.options.enable_arcus From 7a433a83e5155088629dff03f14800c53665573b Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 31 Aug 2023 09:55:21 +0200 Subject: [PATCH 498/656] Fix building mac CURA-10951 --- tests/ExtruderPlanTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ExtruderPlanTest.cpp b/tests/ExtruderPlanTest.cpp index 026d87f0e4..d0d2772979 100644 --- a/tests/ExtruderPlanTest.cpp +++ b/tests/ExtruderPlanTest.cpp @@ -71,8 +71,8 @@ class ExtruderPlanTestPathCollection GCodePathConfig travel_config; ExtruderPlanTestPathCollection() - : extrusion_config(PrintFeatureType::OuterWall, 400, 100, 1.0_r, SpeedDerivatives(50.0, 1000.0, 10.0)) - , travel_config(PrintFeatureType::MoveCombing, 0, 100, 0.0_r, SpeedDerivatives(120.0, 5000.0, 30.0)) + : extrusion_config(GCodePathConfig{ .type = PrintFeatureType::OuterWall, .line_width = 400, .layer_thickness = 100, .flow = 1.0_r, .speed_derivatives = SpeedDerivatives { .speed = 50.0, .acceleration = 1000.0, .jerk = 10.0 } }) + , travel_config(GCodePathConfig{ .type = PrintFeatureType::MoveCombing, .line_width = 0, .layer_thickness = 100, .flow = 0.0_r, .speed_derivatives = SpeedDerivatives { .speed = 120.0, .acceleration = 5000.0, .jerk = 30.0 } }) { std::shared_ptr mesh = nullptr; constexpr Ratio flow_1 = 1.0_r; From d48b5fae04618b61b32a743a1fb607eaa9e3e6db Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 31 Aug 2023 09:58:53 +0200 Subject: [PATCH 499/656] Better handling of changing bed temperature CURA-8889 --- src/LayerPlan.cpp | 3 +-- src/gcodeExport.cpp | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 9122bbb414..3ec1ae09c5 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1886,8 +1886,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) mesh_group_settings.get("flow_rate_extrusion_offset_factor")); // Offset is in mm. static LayerIndex layer_1{ 1 - static_cast(Raft::getTotalExtraLayers()) }; - if (layer_nr == layer_1 && mesh_group_settings.get("machine_heated_bed") - && mesh_group_settings.get("material_bed_temperature") != mesh_group_settings.get("material_bed_temperature_layer_0")) + if (layer_nr == layer_1 && mesh_group_settings.get("machine_heated_bed")) { constexpr bool wait = false; gcode.writeBedTemperatureCommand(mesh_group_settings.get("material_bed_temperature"), wait); diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 3fbd298ac8..e8b421b94a 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -110,6 +110,21 @@ void GCodeExport::preSetup(const size_t start_extruder) } estimateCalculator.setFirmwareDefaults(mesh_group->settings); + + if (mesh_group != scene.mesh_groups.begin()) + { + // Current bed temperature is the one of the previous group + bed_temperature = (scene.current_mesh_group - 1)->settings.get("material_bed_temperature"); + } + else if (! scene.current_mesh_group->settings.get("material_bed_temp_prepend")) + { + // Current bed temperature is the one of the first layer (has already been set in header) + bed_temperature = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); + } + else + { + // Current bed temperature has not been set yet + } } void GCodeExport::setInitialAndBuildVolumeTemps(const unsigned int start_extruder_nr) @@ -708,21 +723,12 @@ bool GCodeExport::initializeExtruderTrains(const SliceDataStorage& storage, cons void GCodeExport::processInitialLayerBedTemperature() { - Scene& scene = Application::getInstance().current_slice->scene; - - if (scene.current_mesh_group->settings.get("material_bed_temp_prepend") && scene.current_mesh_group->settings.get("machine_heated_bed")) + const Scene& scene = Application::getInstance().current_slice->scene; + const bool heated = scene.current_mesh_group->settings.get("machine_heated_bed"); + const Temperature bed_temp = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); + if (heated && bed_temp != 0) { - const Temperature bed_temp = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); - if (scene.current_mesh_group == scene.mesh_groups.begin() // Always write bed temperature for first mesh group. - || bed_temp - != (scene.current_mesh_group - 1) - ->settings.get("material_bed_temperature")) // Don't write bed temperature if identical to temperature of previous group. - { - if (bed_temp != 0) - { - writeBedTemperatureCommand(bed_temp, scene.current_mesh_group->settings.get("material_bed_temp_wait")); - } - } + writeBedTemperatureCommand(bed_temp, scene.current_mesh_group->settings.get("material_bed_temp_wait")); } } From 6595207a83326bd8f5daf4d37671c5994d29ccd2 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 31 Aug 2023 13:32:20 +0200 Subject: [PATCH 500/656] Bed temperature management refinement This actually fixes a case where the temperature was not set properly with one-at-a-time mode CURA-8889 --- src/gcodeExport.cpp | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index e8b421b94a..396f702508 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -111,19 +111,22 @@ void GCodeExport::preSetup(const size_t start_extruder) estimateCalculator.setFirmwareDefaults(mesh_group->settings); - if (mesh_group != scene.mesh_groups.begin()) + if (mesh_group == scene.mesh_groups.begin()) { - // Current bed temperature is the one of the previous group - bed_temperature = (scene.current_mesh_group - 1)->settings.get("material_bed_temperature"); - } - else if (! scene.current_mesh_group->settings.get("material_bed_temp_prepend")) - { - // Current bed temperature is the one of the first layer (has already been set in header) - bed_temperature = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); + if (! scene.current_mesh_group->settings.get("material_bed_temp_prepend")) + { + // Current bed temperature is the one of the first layer (has already been set in header) + bed_temperature = scene.current_mesh_group->settings.get("material_bed_temperature_layer_0"); + } + else + { + // Bed temperature has not been set yet + } } else { - // Current bed temperature has not been set yet + // Current bed temperature is the one of the previous group + bed_temperature = (scene.current_mesh_group - 1)->settings.get("material_bed_temperature"); } } @@ -1517,10 +1520,10 @@ void GCodeExport::writeBedTemperatureCommand(const Temperature& temperature, con { // The UM2 family doesn't support temperature commands (they are fixed in the firmware) return; } - bool wrote_command = false; - if (wait) + + if (bed_temperature != temperature) // Not already at the desired temperature. { - if (bed_temperature != temperature) // Not already at the desired temperature. + if (wait) { if (flavor == EGCodeFlavor::MARLIN) { @@ -1528,20 +1531,17 @@ void GCodeExport::writeBedTemperatureCommand(const Temperature& temperature, con *output_stream << PrecisionedDouble{ 1, temperature } << new_line; *output_stream << "M105" << new_line; } + *output_stream << "M190 S"; } - *output_stream << "M190 S"; - wrote_command = true; - } - else if (bed_temperature != temperature) - { - *output_stream << "M140 S"; - wrote_command = true; - } - if (wrote_command) - { + else + { + *output_stream << "M140 S"; + } + *output_stream << PrecisionedDouble{ 1, temperature } << new_line; + + bed_temperature = temperature; } - bed_temperature = temperature; } void GCodeExport::writeBuildVolumeTemperatureCommand(const Temperature& temperature, const bool wait) From 18abd192fd19dcb0cf1adcb04d7ff468afe9820d Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 1 Sep 2023 16:09:23 +0200 Subject: [PATCH 501/656] Don't resolve crossing after all in certain cases. Most of this is defensive programming, but it can actually happen -- perhaps not anymore after the 'large_enough_vec' (for bisector calculation) will also have been upped (see next commit), but it's better to be safe and keep this as well. part of CURA-10410 second edition --- src/infill.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 75196336a6..6d83b49cbe 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -793,7 +793,9 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse auto& end_a = forward_line_a ? a->altered_end : a->altered_start; auto& end_b = forward_line_b ? b->altered_start : b->altered_end; - // Set values ('pre existing' values are needed when feeging these as reference parameters to functions that need a value). + // Set values ('pre existing' values are needed when feeding these as reference parameters to functions that need a value). + assert(! bend_a.has_value()); + assert(! bend_b.has_value()); bend_a.emplace(0, 0); bend_b.emplace(0, 0); @@ -806,17 +808,29 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse const auto s = intersect - offset; const auto t = s + bisect; + // In certain rare conditions, the lines do not actually intersect in a way that we can solve with the current algorithm. + bool is_resolved = true; + // Use both of the resulting lines to place the 'bends' by intersecting with the original line-segments. - LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value()); - LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value()); + is_resolved &= LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value()); + is_resolved &= LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value()); // Also set the new end-points. - LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a); - LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b); + is_resolved &= LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a); + is_resolved &= LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b); - // The connecting line will be made from the end-points. - connect_start = end_a; - connect_end = end_b; + if (is_resolved) + { + // The connecting line will be made from the end-points. + connect_start = end_a; + connect_end = end_b; + } + else + { + // Put everything that has now potentially become messed up, back. + bend_a.reset(); + bend_b.reset(); + } } void Infill::connectLines(Polygons& result_lines) From a77182eca1aa6c0d727aeb43d6bf4a758c2b8c10 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 1 Sep 2023 16:12:31 +0200 Subject: [PATCH 502/656] Large wasn't large enough. The smaller value caused rounding errors still. Since coord_t should always be 64-bit, even if people compile it for 32-bit systems (which we don't officially support, but still...), it's safe to go to the 32-bit max. This will solve most or all of the remaining issues with the infill-line-crossing-resolve. (It's a bit more important than the previous commit in that regard.) part of CURA-10410 second edition --- src/infill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infill.cpp b/src/infill.cpp index 6d83b49cbe..2a137f71a9 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -800,7 +800,7 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse bend_b.emplace(0, 0); // Find a bisector of the intersection; specifically, the one that crosses the connection & offset it by 1/2 distance to each side. - constexpr auto large_enough_vec_len = 0xFFFF; + constexpr auto large_enough_vec_len = 0xFFFFFFFF; const auto bisect = LinearAlg2D::getBisectorVector(intersect, connect_start, connect_end, large_enough_vec_len); const auto offset = ((at_distance / 2) * Point(-bisect.Y, bisect.X)) / large_enough_vec_len; const auto q = intersect + offset; From e54f5719fbc11f352ff60f2a60d84c04521c3af6 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 1 Sep 2023 16:25:33 +0200 Subject: [PATCH 503/656] infill area calculated irrespective CURA-10823 --- include/skin.h | 2 +- src/skin.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/skin.h b/include/skin.h index 6beeae1643..65eb1dcaa8 100644 --- a/include/skin.h +++ b/include/skin.h @@ -125,7 +125,7 @@ class SkinInfillAreaComputation * where the infill areas (output) are stored. * \param skin The skin areas on the layer of the \p part */ - void generateInfill(SliceLayerPart& part, const Polygons& skin); + void generateInfill(SliceLayerPart& part); /*! * Remove the areas which are 'directly' under air from the \ref SkinPart::inner_infill and diff --git a/src/skin.cpp b/src/skin.cpp index c29fec0b63..227591a8c4 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -146,11 +146,12 @@ void SkinInfillAreaComputation::generateSkinAndInfillAreas(SliceLayerPart& part) Polygons skin = top_skin.unionPolygons(bottom_skin); skin.removeSmallAreas(MIN_AREA_SIZE); - + // Create infill area irrespective if the infill is to be generated or not(would be used for bridging). + part.infill_area = part.inner_area.difference(skin); if (process_infill) { // process infill when infill density > 0 // or when other infill meshes want to modify this infill - generateInfill(part, skin); + generateInfill(part); } for (PolygonsPart& skin_area_part : skin.splitIntoParts()) @@ -306,9 +307,9 @@ void SkinInfillAreaComputation::applySkinExpansion(const Polygons& original_outl * * generateInfill read mesh.layers[n].parts[*].{insets,skin_parts,boundingBox} and write mesh.layers[n].parts[*].infill_area */ -void SkinInfillAreaComputation::generateInfill(SliceLayerPart& part, const Polygons& skin) +void SkinInfillAreaComputation::generateInfill(SliceLayerPart& part) { - part.infill_area = part.inner_area.difference(skin); // Generate infill everywhere where there wasn't any skin. + // Generate infill everywhere where there wasn't any skin. part.infill_area.removeSmallAreas(MIN_AREA_SIZE); } From 46496189d356dad5eff2e96fe76278345f653125 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Fri, 1 Sep 2023 14:26:21 +0000 Subject: [PATCH 504/656] Applied clang-format. --- include/skin.h | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/include/skin.h b/include/skin.h index 65eb1dcaa8..de96d6e1c3 100644 --- a/include/skin.h +++ b/include/skin.h @@ -1,5 +1,5 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SKIN_H #define SKIN_H @@ -7,7 +7,7 @@ #include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" -namespace cura +namespace cura { class Polygons; @@ -21,10 +21,9 @@ class SliceMeshStorage; class SkinInfillAreaComputation { public: - /*! * \brief Initialize the parameters for skin and infill area computation. - * + * * \param layer_nr The index of the layer for which to generate the skins * and infill. * \param mesh The storage where the layer outline information (input) is @@ -41,11 +40,11 @@ class SkinInfillAreaComputation /*! * \brief Combines the infill of multiple layers for a specified mesh. - * + * * The infill layers are combined while the thickness of each layer is * multiplied such that the infill should fill up again to the full height of * all combined layers. - * + * * \param mesh The mesh to combine the infill layers of. */ static void combineInfillLayers(SliceMeshStorage& mesh); @@ -69,10 +68,10 @@ class SkinInfillAreaComputation /*! * Limit the infill areas to places where they support internal overhangs. - * + * * This function uses the part.infill_area and part.infill_area_own * and computes a new part.infill_area_own - * + * * \param mesh The mesh for which to recalculate the infill areas */ static void generateInfillSupport(SliceMeshStorage& mesh); @@ -85,7 +84,7 @@ class SkinInfillAreaComputation /*! * Generate the skin areas (outlines) of one part in a layer - * + * * \param part The part for which to generate skins. */ void generateSkinAndInfillAreas(SliceLayerPart& part); @@ -112,7 +111,7 @@ class SkinInfillAreaComputation * Apply skin expansion: * expand skins into infill area * where the skin is broad enough - * + * * \param original_outline The outline within which skin and infill lie (inner bounds of innermost walls) * \param[in,out] upskin The top skin areas to grow * \param[in,out] downskin The bottom skin areas to grow @@ -128,9 +127,9 @@ class SkinInfillAreaComputation void generateInfill(SliceLayerPart& part); /*! - * Remove the areas which are 'directly' under air from the \ref SkinPart::inner_infill and + * Remove the areas which are 'directly' under air from the \ref SkinPart::inner_infill and * save them in the \ref SkinPart::roofing_fill of the \p part. - * + * * \param[in,out] part Where to get the SkinParts to get the outline info from and to store the roofing areas */ void generateRoofingFillAndSkinFill(SliceLayerPart& part); @@ -194,6 +193,6 @@ class SkinInfillAreaComputation Polygons getOutlineOnLayer(const SliceLayerPart& part_here, const LayerIndex layer2_nr); }; -}//namespace cura +} // namespace cura -#endif//SKIN_H +#endif // SKIN_H From 0651e1cb7a0158dbc7867908c74fe0805ce7f86a Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 5 Sep 2023 14:39:20 +0200 Subject: [PATCH 505/656] Extra skin walls now do not influence bridging CURA-9521 --- include/FffGcodeWriter.h | 2 +- include/infill.h | 6 ++++-- src/FffGcodeWriter.cpp | 9 ++++++--- src/infill.cpp | 10 ++++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 014e29f059..4b236d03b8 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -491,7 +491,7 @@ class FffGcodeWriter : public NoCopy * \param[out] added_something Whether this function added anything to the layer plan * \param fan_speed fan speed override for this skin area */ - void processSkinPrintFeature(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, EFillMethod pattern, const AngleDegrees skin_angle, const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; + void processSkinPrintFeature(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, EFillMethod pattern, const AngleDegrees skin_angle, const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, const bool is_bridge_skin = false) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to diff --git a/include/infill.h b/include/infill.h index 269c695887..ecf22d59fe 100644 --- a/include/infill.h +++ b/include/infill.h @@ -131,7 +131,8 @@ class Infill const SierpinskiFillProvider* cross_fill_provider = nullptr, const LightningLayer* lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr, - const Polygons& prevent_small_exposed_to_air = Polygons()); + const Polygons& prevent_small_exposed_to_air = Polygons(), + const bool is_bridge_skin = false); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -153,7 +154,8 @@ class Infill const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type); + SectionType section_type, + const bool is_bridge_skin = false); private: /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index b165e4e931..60efcd8a48 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2724,7 +2724,8 @@ void FffGcodeWriter::processTopBottom( skin_density, monotonic, added_something, - fan_speed); + fan_speed, + is_bridge_skin); } void FffGcodeWriter::processSkinPrintFeature( @@ -2741,7 +2742,8 @@ void FffGcodeWriter::processSkinPrintFeature( const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed) const + double fan_speed, + const bool is_bridge_skin) const { Polygons skin_polygons; Polygons skin_lines; @@ -2801,7 +2803,8 @@ void FffGcodeWriter::processSkinPrintFeature( nullptr, nullptr, nullptr, - small_areas_on_surface ? Polygons() : exposed_to_air); + small_areas_on_surface ? Polygons() : exposed_to_air, + is_bridge_skin); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) diff --git a/src/infill.cpp b/src/infill.cpp index 2a137f71a9..79d68cb7a5 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -59,13 +59,14 @@ Polygons Infill::generateWallToolPaths( const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type) + SectionType section_type, + const bool is_bridge_skin) { outer_contour = outer_contour.offset(infill_overlap); scripta::log("infill_outer_contour", outer_contour, section_type, layer_idx, scripta::CellVDI{ "infill_overlap", infill_overlap }); Polygons inner_contour; - if (wall_line_count > 0) + if ((wall_line_count > 0) && (! is_bridge_skin)) { constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. WallToolPaths wall_toolpaths(outer_contour, line_width, wall_line_count, wall_0_inset, settings, layer_idx, section_type); @@ -89,14 +90,15 @@ void Infill::generate( const SierpinskiFillProvider* cross_fill_provider, const LightningLayer* lightning_trees, const SliceMeshStorage* mesh, - const Polygons& prevent_small_exposed_to_air) + const Polygons& prevent_small_exposed_to_air, + const bool is_bridge_skin) { if (outer_contour.empty()) { return; } - inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type); + inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type, is_bridge_skin); scripta::log("infill_inner_contour_0", inner_contour, section_type, layer_idx); // It does not make sense to print a pattern in a small region. So the infill region From f55e8a9f9d399728bc6bf298b6c3550172148cde Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Tue, 5 Sep 2023 12:40:02 +0000 Subject: [PATCH 506/656] Applied clang-format. --- include/FffGcodeWriter.h | 233 +++++++++++++++++++++++++++------------ 1 file changed, 162 insertions(+), 71 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 4b236d03b8..0db186ce36 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -4,17 +4,17 @@ #ifndef GCODE_WRITER_H #define GCODE_WRITER_H -#include -#include - #include "FanSpeedLayerTime.h" -#include "gcodeExport.h" #include "LayerPlanBuffer.h" +#include "gcodeExport.h" #include "settings/PathConfigStorage.h" //For the MeshPathConfigs subclass. #include "utils/ExtrusionLine.h" //Processing variable-width paths. #include "utils/NoCopy.h" -namespace cura +#include +#include + +namespace cura { class AngleDegrees; @@ -28,28 +28,28 @@ class TimeKeeper; /*! * Secondary stage in Fused Filament Fabrication processing: The generated polygons are used in the gcode generation. - * Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines, + * Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines, * while other polygons signify the contours which should be printed. - * + * * The main function of this class is FffGcodeWriter::writeGCode(). */ class FffGcodeWriter : public NoCopy { - friend class FffProcessor; //Because FffProcessor exposes finalize (TODO) + friend class FffProcessor; // Because FffProcessor exposes finalize (TODO) private: coord_t max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print. /* * Buffer for all layer plans (of type LayerPlan) - * + * * The layer plans are buffered so that we can start heating up a nozzle several layers before it needs to be used. * Another reason is to perform Auto Temperature. */ - LayerPlanBuffer layer_plan_buffer; + LayerPlanBuffer layer_plan_buffer; /*! * The class holding the current state of the gcode being written. - * + * * It holds information such as the last written position etc. */ GCodeExport gcode; @@ -104,18 +104,18 @@ class FffGcodeWriter : public NoCopy /*! * Set the target to write gcode to: an output stream. - * + * * Used when CuraEngine is NOT used as command line tool. - * + * * \param stream The stream to write gcode to. */ void setTargetStream(std::ostream* stream); /*! * Get the total extruded volume for a specific extruder in mm^3 - * + * * Retractions and unretractions don't contribute to this. - * + * * \param extruder_nr The extruder number for which to get the total netto extruded volume * \return total filament printed in mm^3 */ @@ -123,7 +123,7 @@ class FffGcodeWriter : public NoCopy /*! * Get the total estimated print time in seconds for each feature - * + * * \return total print time in seconds for each feature */ std::vector getTotalPrintTimePerFeature(); @@ -131,7 +131,7 @@ class FffGcodeWriter : public NoCopy /*! * Write all the gcode for the current meshgroup. * This is the primary function of this class. - * + * * \param[in] storage The data storage from which to get the polygons to print and the areas to fill. * \param timeKeeper The stop watch to see how long it takes for each of the stages in the slicing process. */ @@ -146,28 +146,28 @@ class FffGcodeWriter : public NoCopy /*! * Set the retraction and wipe config globally, per extruder and per mesh. - * + * * \param[out] storage The data storage to which to save the configurations */ void setConfigRetractionAndWipe(SliceDataStorage& storage); /*! * Get the extruder with which to start the print. - * + * * Generally this is the extruder of the adhesion type in use, but in case * the platform adhesion type is none, the support extruder is used. If * support is also disabled, the extruder with lowest number which is used * on the first layer is used as initial extruder. - * + * * \param[in] storage where to get settings from. */ size_t getStartExtruder(const SliceDataStorage& storage); /*! * Set the infill angles and skin angles in the SliceDataStorage. - * + * * These lists of angles are cycled through to get the infill angle of a specific layer. - * + * * \param mesh The mesh for which to determine the infill and skin angles. */ void setInfillAndSkinAngles(SliceMeshStorage& mesh); @@ -186,24 +186,24 @@ class FffGcodeWriter : public NoCopy /*! * Move up and over the already printed meshgroups to print the next meshgroup. - * + * * \param[in] storage where the slice data is stored. */ void processNextMeshGroupCode(const SliceDataStorage& storage); - + /*! * Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer - * + * * \param[in,out] storage where the slice data is stored. */ void processRaft(const SliceDataStorage& storage); /*! * Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer - * + * * In case of negative layer numbers, create layers only containing the data from * the helper parts (support etc) to fill up the gap between the raft and the model. - * + * * \param[in] storage where the slice data is stored. * \param layer_nr The index of the layer to write the gcode of. * \param total_layers The total number of layers. @@ -217,17 +217,17 @@ class FffGcodeWriter : public NoCopy * * Technically, this function checks whether any extruder needs to be primed (with a prime blob) * separately just before they are used. - * + * * \return whether any extruder need to be primed separately just before they are used */ bool getExtruderNeedPrimeBlobDuringFirstLayer(const SliceDataStorage& storage, const size_t extruder_nr) const; /*! * Add the skirt or the brim to the layer plan \p gcodeLayer if it hasn't already been added yet. - * + * * This function should be called for only one layer; * calling it for multiple layers results in the skirt/brim being printed on multiple layers. - * + * * \param storage where the slice data is stored. * \param gcodeLayer The initial planning of the g-code of the layer. * \param extruder_nr The extruder train for which to process the skirt or @@ -238,15 +238,15 @@ class FffGcodeWriter : public NoCopy /*! * Adds the ooze shield to the layer plan \p gcodeLayer. - * + * * \param[in] storage where the slice data is stored. * \param gcodeLayer The initial planning of the gcode of the layer. */ void processOozeShield(const SliceDataStorage& storage, LayerPlan& gcodeLayer) const; - + /*! * Adds the draft protection screen to the layer plan \p gcodeLayer. - * + * * \param[in] storage where the slice data is stored. * \param gcodeLayer The initial planning of the gcode of the layer. */ @@ -256,14 +256,14 @@ class FffGcodeWriter : public NoCopy * Calculate in which order to plan the extruders for each layer * Store the order of extruders for each layer in extruder_order_per_layer for normal layers * and the order of extruders for raft/filler layers in extruder_order_per_layer_negative_layers. - * + * * Only extruders which are (most probably) going to be used are planned - * + * * \note At the planning stage we only have information on areas, not how those are filled. * If an area is too small to be filled with anything it will still get specified as being used with the extruder for that area. - * + * * Computes \ref FffGcodeWriter::extruder_order_per_layer and \ref FffGcodeWriter::extruder_order_per_layer_negative_layers - * + * * \param[in] storage where the slice data is stored. */ void calculateExtruderOrderPerLayer(const SliceDataStorage& storage); @@ -280,10 +280,10 @@ class FffGcodeWriter : public NoCopy /*! * Gets a list of extruders that are used on the given layer, but excluding the given starting extruder. * When it's on the first layer, the prime blob will also be taken into account. - * + * * \note At the planning stage we only have information on areas, not how those are filled. * If an area is too small to be filled with anything it will still get specified as being used with the extruder for that area. - * + * * \param[in] storage where the slice data is stored. * \param current_extruder The current extruder with which we last printed * \return The order of extruders for a layer beginning with \p current_extruder @@ -294,7 +294,7 @@ class FffGcodeWriter : public NoCopy * Calculate in which order to plan the meshes of a specific extruder * Each mesh which has some feature printed with the extruder is included in this order. * One mesh can occur in the mesh order of multiple extruders. - * + * * \param[in] storage where the slice data is stored. * \param extruder_nr The extruder for which to determine the order * \return A vector of mesh indices ordered on print order for that extruder. @@ -303,41 +303,50 @@ class FffGcodeWriter : public NoCopy /*! * Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode. - * + * * \param[in] storage where the slice data is stored. * \param mesh The mesh to add to the layer plan \p gcodeLayer. * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcodeLayer) const; - + void addMeshLayerToGCode_meshSurfaceMode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcodeLayer) const; + /*! * Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes. - * + * * \param[in] storage where the slice data is stored. * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ void addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; - + /*! * Add all features of a given extruder from a single layer from a single mesh-volume to the layer plan \p gcode_layer. - * + * * This adds all features (e.g. walls, skin etc.) of this \p mesh to the gcode which are printed using \p extruder_nr - * + * * \param[in] storage where the slice data is stored. * \param mesh The mesh to add to the layer plan \p gcode_layer. * \param extruder_nr The extruder for which to print all features of the mesh which should be printed with this extruder * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; + void addMeshLayerToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. * This only adds the features which are printed with \p extruder_nr. - * + * * \param[in] storage where the slice data is stored. * \param storage Storage to get global settings from. * \param mesh The mesh to add to the layer plan \p gcode_layer. @@ -346,7 +355,13 @@ class FffGcodeWriter : public NoCopy * \param part The part to add * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, LayerPlan& gcode_layer) const; + void addMeshPartToGCode( + const SliceDataStorage& storage, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + LayerPlan& gcode_layer) const; /*! * \brief Add infill for a given part in a layer plan. @@ -359,12 +374,18 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processInfill( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * \brief Add thicker (multiple layers) sparse infill for a given part in a * layer plan. - * + * * \param gcodeLayer The initial planning of the gcode of the layer. * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. * \param extruder_nr The extruder for which to print all features of the @@ -373,7 +394,13 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processMultiLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processMultiLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * \brief Add normal sparse infill for a given part in a layer. @@ -385,7 +412,13 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode. * \return Whether this function added anything to the layer plan. */ - bool processSingleLayerInfill(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processSingleLayerInfill( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * Generate the insets for the walls of a given layer part. @@ -397,7 +430,13 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processInsets(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processInsets( + const SliceDataStorage& storage, + LayerPlan& gcodeLayer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * Generate the a spiralized wall for a given layer part. @@ -407,7 +446,12 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. */ - void processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) const; + void processSpiralizedWall( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part, + const SliceMeshStorage& mesh) const; /*! * Add the gcode of the top/bottom skin of the given part and of the perimeter gaps. @@ -420,21 +464,27 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processSkin(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part) const; + bool processSkin( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SliceLayerPart& part) const; /*! * Add the gcode of the top/bottom skin of the given skin part and of the perimeter gaps. - * + * * Perimeter gaps are handled for the current extruder for the following features if they are printed with this extruder. * - skin outlines * - roofing (if concentric) * - top/bottom (if concentric) * They are all printed at the end of printing the skin part features which are printed with this extruder. - * + * * Note that the normal perimeter gaps are printed with the outer wall extruder, * while newly generated perimeter gaps * are printed with the extruder with which the feature was printed which generated the gaps. - * + * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. * \param mesh The mesh for which to add to the layer plan \p gcode_layer. @@ -443,7 +493,13 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \return Whether this function added anything to the layer plan */ - bool processSkinPart(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part) const; + bool processSkinPart( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part) const; /*! * Add the roofing which is the area inside the innermost skin inset which has air 'directly' above @@ -456,7 +512,14 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processRoofing(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; + void processRoofing( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const; /*! * Add the normal skinfill which is the area inside the innermost skin inset @@ -470,11 +533,18 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processTopBottom(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SkinPart& skin_part, bool& added_something) const; + void processTopBottom( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const size_t extruder_nr, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const SkinPart& skin_part, + bool& added_something) const; /*! * Process a dense skin feature like roofing or top/bottom - * + * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. * \param mesh The mesh for which to add to the layer plan \p gcode_layer. @@ -491,7 +561,22 @@ class FffGcodeWriter : public NoCopy * \param[out] added_something Whether this function added anything to the layer plan * \param fan_speed fan speed override for this skin area */ - void processSkinPrintFeature(const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const size_t extruder_nr, const Polygons& area, const GCodePathConfig& config, EFillMethod pattern, const AngleDegrees skin_angle, const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, const bool is_bridge_skin = false) const; + void processSkinPrintFeature( + const SliceDataStorage& storage, + LayerPlan& gcode_layer, + const SliceMeshStorage& mesh, + const PathConfigStorage::MeshPathConfigs& mesh_config, + const size_t extruder_nr, + const Polygons& area, + const GCodePathConfig& config, + EFillMethod pattern, + const AngleDegrees skin_angle, + const coord_t skin_overlap, + const Ratio skin_density, + const bool monotonic, + bool& added_something, + double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, + const bool is_bridge_skin = false) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to @@ -510,7 +595,7 @@ class FffGcodeWriter : public NoCopy * +------+ +------+ * 1, 2 = start locations of skin segments * # = seam - * + * * \param filling_part The part which we are going to fill with a linear filling type * \param filling_angle The angle of the filling lines * \param last_position The position the print head is in before going to fill the part @@ -573,9 +658,9 @@ class FffGcodeWriter : public NoCopy /*! * Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last. - * + * * On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower. - * + * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. * \param extruder_nr The extruder to switch to. @@ -589,7 +674,7 @@ class FffGcodeWriter : public NoCopy * \param prev_extruder The current extruder with which we last printed. */ void addPrimeTower(const SliceDataStorage& storage, LayerPlan& gcodeLayer, const size_t prev_extruder) const; - + /*! * Add the end gcode and set all temperatures to zero. */ @@ -628,9 +713,15 @@ class FffGcodeWriter : public NoCopy * \param infill_line_width line width of the infill * \return true if there needs to be a skin edge support wall in this layer, otherwise false */ - static bool 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) ; + static bool 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); }; -}//namespace cura +} // namespace cura #endif // GCODE_WRITER_H From 65df641e3d96a911362f51afcb2244e7fc4e8c7a Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 5 Sep 2023 14:42:44 +0200 Subject: [PATCH 507/656] comment for param addition CURA-9521 --- include/infill.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/infill.h b/include/infill.h index ecf22d59fe..b048670db0 100644 --- a/include/infill.h +++ b/include/infill.h @@ -144,6 +144,7 @@ class Infill * \param line_width [in] The optimum wall line width of the walls * \param infill_overlap [in] The overlap of the infill * \param settings [in] A settings storage to use for generating variable-width walls. + * \param is_bridge_skin [in] Setting to filter out the extra skin walls while bridging * \return The inner contour of the wall toolpaths */ static Polygons generateWallToolPaths( From ce2394894e339f06d2aeb93555183bd48c3a34fc Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 5 Sep 2023 14:55:40 +0200 Subject: [PATCH 508/656] Bend- and end-points should not be beyond the original infill lines. part of CURA-10410 --- src/infill.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/infill.cpp b/src/infill.cpp index 2a137f71a9..e14ed2130f 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -812,12 +812,12 @@ void Infill::resolveIntersection(const coord_t at_distance, const Point& interse bool is_resolved = true; // Use both of the resulting lines to place the 'bends' by intersecting with the original line-segments. - is_resolved &= LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value()); - is_resolved &= LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value()); + is_resolved &= LinearAlg2D::lineLineIntersection(q, r, a->start, a->end, bend_a.value()) && LinearAlg2D::pointIsProjectedBeyondLine(bend_a.value(), a->start, a->end) == 0; + is_resolved &= LinearAlg2D::lineLineIntersection(s, t, b->start, b->end, bend_b.value()) && LinearAlg2D::pointIsProjectedBeyondLine(bend_b.value(), b->start, b->end) == 0; - // Also set the new end-points. - is_resolved &= LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a); - is_resolved &= LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b); + // Also set the new end-points + is_resolved &= LinearAlg2D::lineLineIntersection(connect_start, connect_end, q, r, end_a) && LinearAlg2D::pointIsProjectedBeyondLine(end_a, connect_start, connect_end) == 0; + is_resolved &= LinearAlg2D::lineLineIntersection(connect_start, connect_end, s, t, end_b) && LinearAlg2D::pointIsProjectedBeyondLine(end_b, connect_start, connect_end) == 0; if (is_resolved) { From 9ca2d9790a26c9a3cacca1268e6cf6db2019a1ae Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 5 Sep 2023 15:37:22 +0200 Subject: [PATCH 509/656] Reordering headers elsewhere exposed missed header here. --- include/LayerPlanBuffer.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index 4e05ffcade..da86303817 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -4,11 +4,12 @@ #ifndef LAYER_PLAN_BUFFER_H #define LAYER_PLAN_BUFFER_H -#include - +#include "gcodeExport.h" #include "Preheat.h" #include "settings/types/Duration.h" +#include + namespace cura { From d9b92f2bb41ab3261050059508708b9b00d5e72b Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 5 Sep 2023 13:38:06 +0000 Subject: [PATCH 510/656] Applied clang-format. --- include/LayerPlanBuffer.h | 72 +++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index da86303817..7a61e414db 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -1,16 +1,16 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef LAYER_PLAN_BUFFER_H #define LAYER_PLAN_BUFFER_H -#include "gcodeExport.h" #include "Preheat.h" +#include "gcodeExport.h" #include "settings/types/Duration.h" #include -namespace cura +namespace cura { class ExtruderPlan; @@ -18,16 +18,16 @@ class LayerPlan; /*! * Class for buffering multiple layer plans (\ref LayerPlan) / extruder plans within those layer plans, so that temperature commands can be inserted in earlier layer plans. - * + * * This class handles where to insert temperature commands for: * - initial layer temperature * - flow dependent temperature * - starting to heat up from the standby temperature * - initial printing temperature | printing temperature | final printing temperature - * + * * \image html assets/precool.png "Temperature Regulation" width=10cm * \image latex assets/precool.png "Temperature Regulation" width=10cm - * + * */ class LayerPlanBuffer { @@ -35,25 +35,30 @@ class LayerPlanBuffer Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train. - static constexpr size_t buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value + static constexpr size_t buffer_size + = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value // this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted. - static constexpr Duration extra_preheat_time = 1.0_s; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones. + static constexpr Duration extra_preheat_time + = 1.0_s; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones. - std::vector extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to the initial_print_temp or to the extrusion_temperature + std::vector extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to + //!< the initial_print_temp or to the extrusion_temperature /*! * The buffer containing several layer plans (LayerPlan) before writing them to gcode. - * + * * The front is the lowest/oldest layer. * The back is the highest/newest layer. */ std::list buffer; + public: LayerPlanBuffer(GCodeExport& gcode) - : gcode(gcode) - , extruder_used_in_meshgroup(MAX_EXTRUDERS, false) - { } + : gcode(gcode) + , extruder_used_in_meshgroup(MAX_EXTRUDERS, false) + { + } void setPreheatConfig(); @@ -65,7 +70,7 @@ class LayerPlanBuffer /*! * Push a new layer onto the buffer and handle the buffer. * Write a layer to gcode if it is popped out of the buffer. - * + * * \param layer_plan The layer to handle * \param gcode The exporter with which to write a layer to gcode if the buffer is too large after pushing the new layer. */ @@ -82,7 +87,7 @@ class LayerPlanBuffer * This inserts the temperature commands to start warming for a given layer in earlier layers; * the fan speeds and layer time settings of the most recently pushed layer are processed; * the correctly combing travel move between the last added layer and the layer before is added. - * + * * Pop out the earliest layer in the buffer if the buffer size is exceeded * \return A nullptr or the popped gcode_layer */ @@ -90,7 +95,7 @@ class LayerPlanBuffer /*! * Add the travel move to properly travel from the end location of the previous layer to the starting location of the next - * + * * \param prev_layer The layer before the just added layer, to which to add the combing travel move. * \param newest_layer The newly added layer, with a non-combing travel move as first path. */ @@ -103,7 +108,7 @@ class LayerPlanBuffer /*! * Insert a preheat command for @p extruder into @p extruder_plan_before - * + * * \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command * \param time_before_extruder_plan_end The time before the end of the extruder plan, before which to insert the preheat command * \param extruder_nr The extruder for which to set the temperature @@ -114,9 +119,9 @@ class LayerPlanBuffer /*! * Compute the time needed to preheat from standby to required (initial) printing temperature at the start of an extruder plan, * based on the time the extruder has been on standby. - * + * * Also computes the temperature to which we cool before starting to heat agian. - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to find the preheat time needed * \return the time needed to preheat and the temperature from which heating starts @@ -124,11 +129,11 @@ class LayerPlanBuffer Preheat::WarmUpResult computeStandbyTempPlan(std::vector& extruder_plans, unsigned int extruder_plan_idx); /*! - * For two consecutive extruder plans of the same extruder (so on different layers), + * For two consecutive extruder plans of the same extruder (so on different layers), * preheat the extruder to the temperature corresponding to the average flow of the second extruder plan. - * + * * The preheat commands are inserted such that the middle of the temperature change coincides with the start of the next layer. - * + * * \param prev_extruder_plan The former extruder plan (of the former layer) * \param extruder_nr The extruder for which too set the temperature * \param required_temp The required temperature for the second extruder plan @@ -140,7 +145,7 @@ class LayerPlanBuffer * Find the time window in which this extruder hasn't been used * and compute at what time the preheat command needs to be inserted. * Then insert the preheat command in the right extruder plan. - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to find the preheat time needed */ @@ -149,15 +154,15 @@ class LayerPlanBuffer /*! * Insert temperature commands related to the extruder plan corersponding to @p extruder_plan_idx * and the extruder plan before: - * + * * In case the extruder plan before has the same extruder: * - gradually change printing temperature around the layer change (\ref LayerPlanBuffer::insertPreheatCommand_singleExtrusion) - * + * * In case the previous extruder plan is a different extruder * - insert preheat command from standby to initial temp in the extruder plan(s) before (\ref LayerPlanBuffer::insertPreheatCommand_multiExtrusion) * - insert the final print temp command of the previous extruder plan (\ref LayerPlanBuffer::insertFinalPrintTempCommand) * - insert the normal extrusion temp command for the current extruder plan (\ref LayerPlanBuffer::insertPrintTempCommand) - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to generate the preheat command */ @@ -165,20 +170,20 @@ class LayerPlanBuffer /*! * Insert the temperature command to heat from the initial print temperature to the printing temperature - * + * * The temperature command is insert at the start of the very first extrusion move - * + * * \param extruder_plan The extruder plan in which to insert the heat up command */ void insertPrintTempCommand(ExtruderPlan& extruder_plan); /*! * Insert the temp command to start cooling from the printing temperature to the final print temp - * + * * The print temp is inserted before the last extrusion move of the extruder plan corresponding to \p last_extruder_plan_idx - * + * * The command is inserted at a timed offset before the end of the last extrusion move - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param last_extruder_plan_idx The index of the last extruder plan in \p extruder_plans with the same extruder as previous extruder plans */ @@ -193,7 +198,7 @@ class LayerPlanBuffer * Reconfigure the standby temperature during which we didn't print with this extruder. * Find the previous extruder plan with the same extruder as layers[layer_plan_idx].extruder_plans[extruder_plan_idx] * Set the prev_extruder_standby_temp in the next extruder plan - * + * * \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers) * \param extruder_plan_idx The index of the extruder plan in \p extruder_plans before which to reconfigure the standby temperature * \param standby_temp The temperature to which to cool down when the extruder is in standby mode. @@ -202,7 +207,6 @@ class LayerPlanBuffer }; - } // namespace cura #endif // LAYER_PLAN_BUFFER_H \ No newline at end of file From 1ff2e4905c0cccd8227dd7678769ba4b941bf715 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 8 Sep 2023 12:08:16 +0200 Subject: [PATCH 511/656] Fix strange variations in width when bridging. part of CURA-10933 Co-authored-by: Saumya Jain --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 3ec1ae09c5..679af6a215 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -718,7 +718,7 @@ void LayerPlan::addWallLine( // flow required for the next line segment - when accelerating after a bridge segment, the flow is increased in inverse proportion to the speed_factor // so the slower the feedrate, the greater the flow - the idea is to get the extruder back to normal pressure as quickly as possible - const float segment_flow = (speed_factor < 1) ? flow * (1 / speed_factor) : flow; + const float segment_flow = (speed_factor > 1) ? flow * (1 / speed_factor) : flow; // if a bridge is present in this wall, this particular segment may need to be partially or wholely coasted if (distance_to_bridge_start > 0) From 1b325a31d1e00e8864029836c57ef89df01acaa4 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 11 Sep 2023 07:33:43 +0200 Subject: [PATCH 512/656] Pin Arcus version to arcus release (5.3.0) CURA-10475 --- conanfile.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/conanfile.py b/conanfile.py index 0176fd6091..7d4d5dc601 100644 --- a/conanfile.py +++ b/conanfile.py @@ -80,10 +80,9 @@ def build_requirements(self): if self.options.enable_benchmarks: self.test_requires("benchmark/1.7.0") - def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/cura_10475") + self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") @@ -100,12 +99,10 @@ def requirements(self): self.requires("zlib/1.2.12") self.requires("openssl/1.1.1l") - def generate(self): deps = CMakeDeps(self) deps.generate() - tc = CMakeToolchain(self) tc.variables["CURA_ENGINE_VERSION"] = self.version tc.variables["ENABLE_ARCUS"] = self.options.enable_arcus From a4cf30758a28b51cdcbdc98bc1af611c661a843d Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 11 Sep 2023 06:41:30 +0000 Subject: [PATCH 513/656] Applied clang-format. --- include/FffGcodeWriter.h | 23 ++++++----------------- include/LayerPlanBuffer.h | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index b7381f6bb5..2d2e7c7162 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -310,11 +310,7 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcodeLayer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode_meshSurfaceMode( - const SliceDataStorage& storage, - const SliceMeshStorage& mesh, - const MeshPathConfigs& mesh_config, - LayerPlan& gcodeLayer) const; + void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const MeshPathConfigs& mesh_config, LayerPlan& gcodeLayer) const; /*! * Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes. @@ -337,12 +333,8 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode( - const SliceDataStorage& storage, - const SliceMeshStorage& mesh, - const size_t extruder_nr, - const MeshPathConfigs& mesh_config, - LayerPlan& gcode_layer) const; + void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) + const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. @@ -447,12 +439,9 @@ class FffGcodeWriter : public NoCopy * \param part The part for which to create gcode * \param mesh The mesh for which to add to the layer plan \p gcodeLayer. */ - void processSpiralizedWall( - const SliceDataStorage& storage, - LayerPlan& gcode_layer, - const MeshPathConfigs& mesh_config, - const SliceLayerPart& part, - const SliceMeshStorage& mesh) const; + void + processSpiralizedWall(const SliceDataStorage& storage, LayerPlan& gcode_layer, const MeshPathConfigs& mesh_config, const SliceLayerPart& part, const SliceMeshStorage& mesh) + const; /*! * Add the gcode of the top/bottom skin of the given part and of the perimeter gaps. diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index 220afc1680..8898137b87 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -7,8 +7,8 @@ #include "ExtruderPlan.h" #include "LayerPlan.h" #include "Preheat.h" -#include "settings/Settings.h" #include "gcodeExport.h" +#include "settings/Settings.h" #include "settings/types/Duration.h" #include From f278ed959b8fbdc3d82138791986bd29092cbfed Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 11 Sep 2023 09:15:06 +0200 Subject: [PATCH 514/656] fix benchmarks Contributes to CURA-10475 and CURA-10951 --- benchmark/wall_benchmark.h | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmark/wall_benchmark.h b/benchmark/wall_benchmark.h index 2af20fc862..314377c911 100644 --- a/benchmark/wall_benchmark.h +++ b/benchmark/wall_benchmark.h @@ -58,6 +58,7 @@ class WallTestFixture : public benchmark::Fixture settings.add("meshfix_maximum_deviation", "0.1"); settings.add("meshfix_maximum_extrusion_area_deviation", "0.01"); settings.add("meshfix_maximum_resolution", "0.01"); + settings.add("meshfix_fluid_motion_enabled", "false"); settings.add("min_wall_line_width", "0.3"); settings.add("min_bead_width", "0"); settings.add("min_feature_size", "0"); From 115f2865c8c19c80f7b1cc1ab7145ddd1394d37e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Mon, 11 Sep 2023 12:28:38 +0200 Subject: [PATCH 515/656] Don't build legacy linux Dropping support for this with GCC-13 Contributes to CURA-10475 and CURA-10951 --- .github/workflows/conan-package.yml | 278 +++++++++++++--------------- 1 file changed, 132 insertions(+), 146 deletions(-) diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index 922a549aff..749c67ac9d 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -7,151 +7,137 @@ name: conan-package # It should run on pushes against main or CURA-* branches, but it will only create the binaries for main and release branches on: - workflow_dispatch: - inputs: - # FIXME: Not yet implemented - conan_id: - required: false - type: string - description: 'The full conan package ID, e.g. "curaengine/1.2.3@ultimaker/stable"' - create_latest_alias: - required: true - default: false - type: boolean - description: 'Create latest alias' - create_binaries_windows: - required: true - default: false - type: boolean - description: 'create binaries Windows' - create_binaries_linux: - required: true - default: false - type: boolean - description: 'create binaries Linux' - create_binaries_macos: - required: true - default: false - type: boolean - description: 'create binaries Macos' - - push: - paths: - - 'include/**' - - 'src/**' - - 'cmake/**' - - 'tests/**' - - 'test_package/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - - '.github/workflows/conan-package.yml' - - '.github/worflows/requirements-conan-package.txt' - branches: - - main - - 'CURA-*' - - '[1-9].[0-9]*' - - '[1-9].[0-9][0-9]*' - tags: - - '[1-9]+.[0-9]+.[0-9]*' - - '[1-9]+.[0-9]+.[0-9]' + workflow_dispatch: + inputs: + # FIXME: Not yet implemented + conan_id: + required: false + type: string + description: 'The full conan package ID, e.g. "curaengine/1.2.3@ultimaker/stable"' + create_latest_alias: + required: true + default: false + type: boolean + description: 'Create latest alias' + create_binaries_windows: + required: true + default: false + type: boolean + description: 'create binaries Windows' + create_binaries_linux: + required: true + default: false + type: boolean + description: 'create binaries Linux' + create_binaries_macos: + required: true + default: false + type: boolean + description: 'create binaries Macos' + + push: + paths: + - 'include/**' + - 'src/**' + - 'cmake/**' + - 'tests/**' + - 'test_package/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - '.github/workflows/conan-package.yml' + - '.github/worflows/requirements-conan-package.txt' + branches: + - main + - 'CURA-*' + - '[1-9].[0-9]*' + - '[1-9].[0-9][0-9]*' + tags: + - '[1-9]+.[0-9]+.[0-9]*' + - '[1-9]+.[0-9]+.[0-9]' jobs: - conan-recipe-version: - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main - with: - project_name: curaengine - - conan-package-export: - needs: [ conan-recipe-version ] - uses: ultimaker/cura/.github/workflows/conan-recipe-export.yml@main - with: - recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - recipe_id_latest: ${{ needs.conan-recipe-version.outputs.recipe_id_latest }} - runs_on: 'ubuntu-22.04' - python_version: '3.11.x' - conan_logging_level: 'info' - secrets: inherit - - conan-package-create-macos: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_macos) }} - needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main - with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} - recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 3 - runs_on: 'macos-11' - python_version: '3.11.x' - conan_logging_level: 'info' - secrets: inherit - - conan-package-create-windows: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true' )) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_windows) }} - needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main - with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} - recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 4 - runs_on: 'windows-2022' - python_version: '3.11.x' - conan_config_branch: '' - conan_logging_level: 'info' - secrets: inherit - - conan-package-create-linux: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux) }} - needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main - with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} - recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 1 - runs_on: 'ubuntu-20.04' - python_version: '3.11.x' - conan_logging_level: 'info' - secrets: inherit - - conan-package-create-linux-modern: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux) }} - needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main - with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} - recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 2 - runs_on: 'ubuntu-22.04' - python_version: '3.11.x' - conan_logging_level: 'info' - secrets: inherit - - notify-export: - if: ${{ always() }} - needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/notify.yml@main - with: - success: ${{ contains(join(needs.*.result, ','), 'success') }} - success_title: "New Conan recipe exported in ${{ github.repository }}" - success_body: "Exported ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - failure_title: "Failed to export Conan Export in ${{ github.repository }}" - failure_body: "Failed to exported ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - secrets: inherit - - notify-create: - if: ${{ always() && ((github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux)) }} - needs: [ conan-recipe-version, conan-package-create-macos, conan-package-create-windows, conan-package-create-linux, conan-package-create-linux-modern ] - - uses: ultimaker/cura/.github/workflows/notify.yml@main - with: - success: ${{ contains(join(needs.*.result, ','), 'success') }} - success_title: "New binaries created in ${{ github.repository }}" - success_body: "Created binaries for ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - failure_title: "Failed to create binaries in ${{ github.repository }}" - failure_body: "Failed to created binaries for ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - secrets: inherit + conan-recipe-version: + uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + with: + project_name: curaengine + + conan-package-export: + needs: [ conan-recipe-version ] + uses: ultimaker/cura/.github/workflows/conan-recipe-export.yml@main + with: + recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} + recipe_id_latest: ${{ needs.conan-recipe-version.outputs.recipe_id_latest }} + runs_on: 'ubuntu-22.04' + python_version: '3.11.x' + conan_logging_level: 'info' + secrets: inherit + + conan-package-create-macos: + if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_macos) }} + needs: [ conan-recipe-version, conan-package-export ] + + uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main + with: + project_name: ${{ needs.conan-recipe-version.outputs.project_name }} + recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} + build_id: 3 + runs_on: 'macos-11' + python_version: '3.11.x' + conan_logging_level: 'info' + secrets: inherit + + conan-package-create-windows: + if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true' )) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_windows) }} + needs: [ conan-recipe-version, conan-package-export ] + + uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main + with: + project_name: ${{ needs.conan-recipe-version.outputs.project_name }} + recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} + build_id: 4 + runs_on: 'windows-2022' + python_version: '3.11.x' + conan_config_branch: '' + conan_logging_level: 'info' + secrets: inherit + + conan-package-create-linux: + if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux) }} + needs: [ conan-recipe-version, conan-package-export ] + + uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main + with: + project_name: ${{ needs.conan-recipe-version.outputs.project_name }} + recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} + build_id: 2 + runs_on: 'ubuntu-22.04' + python_version: '3.11.x' + conan_logging_level: 'info' + secrets: inherit + + notify-export: + if: ${{ always() }} + needs: [ conan-recipe-version, conan-package-export ] + + uses: ultimaker/cura/.github/workflows/notify.yml@main + with: + success: ${{ contains(join(needs.*.result, ','), 'success') }} + success_title: "New Conan recipe exported in ${{ github.repository }}" + success_body: "Exported ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" + failure_title: "Failed to export Conan Export in ${{ github.repository }}" + failure_body: "Failed to exported ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" + secrets: inherit + + notify-create: + if: ${{ always() && ((github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux)) }} + needs: [ conan-recipe-version, conan-package-create-macos, conan-package-create-windows, conan-package-create-linux ] + + uses: ultimaker/cura/.github/workflows/notify.yml@main + with: + success: ${{ contains(join(needs.*.result, ','), 'success') }} + success_title: "New binaries created in ${{ github.repository }}" + success_body: "Created binaries for ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" + failure_title: "Failed to create binaries in ${{ github.repository }}" + failure_body: "Failed to created binaries for ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" + secrets: inherit From aa60cddca7d935e589158af7bcad18569bf609bd Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 12 Sep 2023 13:49:44 +0000 Subject: [PATCH 516/656] Applied clang-format. --- include/gcodeExport.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/gcodeExport.h b/include/gcodeExport.h index 25c20efa80..c332ef0f52 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -90,8 +90,8 @@ class GCodeExport : public NoCopy double last_e_value_after_wipe; //!< The current material amount extruded since last wipe unsigned fan_number; // nozzle print cooling fan number - Point nozzle_offset; //!< Cache of setting machine_nozzle_offset_[xy] - bool machine_firmware_retract; //!< Cache of setting machine_firmware_retract + Point nozzle_offset; //!< Cache of setting machine_nozzle_offset_[xy] + bool machine_firmware_retract; //!< Cache of setting machine_firmware_retract std::deque extruded_volume_at_previous_n_retractions; // in mm^3 From 4aa73919b616223d9bb9c00b6ecd4c02951ef5b6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 14 Sep 2023 21:01:40 +0200 Subject: [PATCH 517/656] Pass shared pointers, per preference, by reference. Otherwise they're passed by value. Not as bad as passing the entire _original_ object of course, but a ref is still a little lighter weight than a shared pointer object. done as part of, but not nescesary for, CURA-11019 --- include/SkeletalTrapezoidationEdge.h | 6 +++--- include/SkeletalTrapezoidationJoint.h | 2 +- include/infill.h | 10 +++++----- src/communication/ArcusCommunication.cpp | 2 +- src/infill.cpp | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/SkeletalTrapezoidationEdge.h b/include/SkeletalTrapezoidationEdge.h index aa897d830f..4c0bfb365c 100644 --- a/include/SkeletalTrapezoidationEdge.h +++ b/include/SkeletalTrapezoidationEdge.h @@ -80,7 +80,7 @@ class SkeletalTrapezoidationEdge { return transitions.use_count() > 0 && (ignore_empty || ! transitions.lock()->empty()); } - void setTransitions(std::shared_ptr> storage) + void setTransitions(std::shared_ptr>& storage) { transitions = storage; } @@ -93,7 +93,7 @@ class SkeletalTrapezoidationEdge { return transition_ends.use_count() > 0 && (ignore_empty || ! transition_ends.lock()->empty()); } - void setTransitionEnds(std::shared_ptr> storage) + void setTransitionEnds(std::shared_ptr>& storage) { transition_ends = storage; } @@ -106,7 +106,7 @@ class SkeletalTrapezoidationEdge { return extrusion_junctions.use_count() > 0 && (ignore_empty || ! extrusion_junctions.lock()->empty()); } - void setExtrusionJunctions(std::shared_ptr storage) + void setExtrusionJunctions(std::shared_ptr& storage) { extrusion_junctions = storage; } diff --git a/include/SkeletalTrapezoidationJoint.h b/include/SkeletalTrapezoidationJoint.h index 603b9b452f..7c7575a07b 100644 --- a/include/SkeletalTrapezoidationJoint.h +++ b/include/SkeletalTrapezoidationJoint.h @@ -43,7 +43,7 @@ class SkeletalTrapezoidationJoint { return beading.use_count() > 0; } - void setBeading(std::shared_ptr storage) + void setBeading(std::shared_ptr& storage) { beading = storage; } diff --git a/include/infill.h b/include/infill.h index 7c6a342499..40ca7fbd59 100644 --- a/include/infill.h +++ b/include/infill.h @@ -203,8 +203,8 @@ class Infill const Settings& settings, int layer_idx, SectionType section_type, - const std::shared_ptr cross_fill_provider = nullptr, - const std::shared_ptr lightning_layer = nullptr, + const std::shared_ptr& cross_fill_provider = nullptr, + const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr, const Polygons& prevent_small_exposed_to_air = Polygons(), const bool is_bridge_skin = false); @@ -242,8 +242,8 @@ class Infill Polygons& result_polygons, Polygons& result_lines, const Settings& settings, - const std::shared_ptr cross_fill_pattern = nullptr, - const std::shared_ptr lightning_layer = nullptr, + const std::shared_ptr& cross_fill_pattern = nullptr, + const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! @@ -402,7 +402,7 @@ class Infill * see https://hal.archives-ouvertes.fr/hal-02155929/document * \param result (output) The resulting polygons */ - void generateLightningInfill(const std::shared_ptr lightning_layer, Polygons& result_lines); + void generateLightningInfill(const std::shared_ptr& lightning_layer, Polygons& result_lines); /*! * Generate sparse concentric infill diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index cf17d69454..44cc58cbe2 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -420,7 +420,7 @@ void ArcusCommunication::sendOptimizedLayerData() } spdlog::info("Sending {} layers.", data.current_layer_count); - for (std::pair> entry : data.slice_data) // Note: This is in no particular order! + for (const auto& entry : data.slice_data) // Note: This is in no particular order! { spdlog::debug("Sending layer data for layer {} of {}.", entry.first, data.slice_data.size()); private_data->socket->sendMessage(entry.second); // Send the actual layers. diff --git a/src/infill.cpp b/src/infill.cpp index 6bdd545aaa..9137aa66ca 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -88,8 +88,8 @@ void Infill::generate( const Settings& settings, int layer_idx, SectionType section_type, - const std::shared_ptr cross_fill_provider, - const std::shared_ptr lightning_trees, + const std::shared_ptr& cross_fill_provider, + const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh, const Polygons& prevent_small_exposed_to_air, const bool is_bridge_skin) @@ -255,8 +255,8 @@ void Infill::_generate( Polygons& result_polygons, Polygons& result_lines, const Settings& settings, - const std::shared_ptr cross_fill_provider, - const std::shared_ptr lightning_trees, + const std::shared_ptr& cross_fill_provider, + const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh) { if (inner_contour.empty()) @@ -432,7 +432,7 @@ void Infill::generateGyroidInfill(Polygons& result_lines, Polygons& result_polyg PolylineStitcher::stitch(line_segments, result_lines, result_polygons, infill_line_width); } -void Infill::generateLightningInfill(const std::shared_ptr trees, Polygons& result_lines) +void Infill::generateLightningInfill(const std::shared_ptr& trees, Polygons& result_lines) { // Don't need to support areas smaller than line width, as they are always within radius: if (std::abs(inner_contour.area()) < infill_line_width || ! trees) From 9b5da0ecf482c2d49164b19f1aea7eb2abb4b907 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 14 Sep 2023 21:39:25 +0200 Subject: [PATCH 518/656] Fix slowdown issue due to copy of large data. Since `make_shared` is a constructor, the mesh data was copied over each time we started a new layer. Properly fix this by using smart-pointers from the beginning, so we only have the creation overhead, which is nearly nothing. In fact, this solution may be _faster_ than the original, perhaps due to some constness that is now also enforced. fixes internal ticket CURA-11019 --- include/FffGcodeWriter.h | 5 ++-- include/LayerPlan.h | 4 +-- include/pathPlanning/GCodePath.h | 2 +- include/sliceDataStorage.h | 2 +- src/FffGcodeWriter.cpp | 39 +++++++++++++++++------------- src/FffPolygonGenerator.cpp | 30 +++++++++++++---------- src/LayerPlan.cpp | 11 +++++---- src/TreeModelVolumes.cpp | 5 ++-- src/TreeSupport.cpp | 16 ++++++------ src/bridge.cpp | 3 ++- src/plugins/converters.cpp | 2 +- src/settings/PathConfigStorage.cpp | 6 ++--- src/sliceDataStorage.cpp | 22 ++++++++--------- src/support.cpp | 19 ++++++++------- 14 files changed, 90 insertions(+), 76 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 2d2e7c7162..2cde3c40d4 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -328,13 +328,12 @@ class FffGcodeWriter : public NoCopy * This adds all features (e.g. walls, skin etc.) of this \p mesh to the gcode which are printed using \p extruder_nr * * \param[in] storage where the slice data is stored. - * \param mesh The mesh to add to the layer plan \p gcode_layer. + * \param mesh_ptr The mesh to add to the layer plan \p gcode_layer. * \param extruder_nr The extruder for which to print all features of the mesh which should be printed with this extruder * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) - const; + void addMeshLayerToGCode(const SliceDataStorage& storage, const std::shared_ptr& mesh_ptr, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 3eda752178..c894ab2a27 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -70,7 +70,7 @@ class LayerPlan : public NoCopy std::vector has_prime_tower_planned_per_extruder; //!< For each extruder, whether the prime tower is planned yet or not. std::optional last_planned_position; //!< The last planned XY position of the print head (if known) - std::shared_ptr current_mesh; //!< The mesh of the last planned move. + std::shared_ptr current_mesh; //!< The mesh of the last planned move. /*! * Whether the skirt or brim polygons have been processed into planned paths @@ -238,7 +238,7 @@ class LayerPlan : public NoCopy * Track the currently printing mesh. * \param mesh_id A unique ID indicating the current mesh. */ - void setMesh(const std::shared_ptr& mesh); + void setMesh(const std::shared_ptr& mesh); /*! * Set bridge_wall_mask. diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 8c3c34dcfe..7bbd56c709 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -30,7 +30,7 @@ namespace cura struct GCodePath { GCodePathConfig config{}; //!< The configuration settings of the path. - std::shared_ptr mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; + std::shared_ptr mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; SpaceFillType space_fill_type{}; //!< The type of space filling of which this path is a part Ratio flow{}; //!< A type-independent flow configuration Ratio width_factor{}; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 32a391c24c..9b0661ed36 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -324,7 +324,7 @@ class SliceDataStorage : public NoCopy Point3 model_size, model_min, model_max; AABB3D machine_size; //!< The bounding box with the width, height and depth of the printer. - std::vector meshes; + std::vector> meshes; std::vector retraction_wipe_config_per_extruder; //!< Config for retractions, extruder switch retractions, and wipes, per extruder. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 648e9082ce..0d6ea404fd 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -106,8 +106,9 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep } size_t total_layers = 0; - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; size_t mesh_layer_num = mesh.layers.size(); // calculation of _actual_ number of layers in loop. @@ -269,7 +270,7 @@ void FffGcodeWriter::findLayerSeamsForSpiralize(SliceDataStorage& storage, size_ const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (unsigned int mesh_idx : mesh_order) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + auto& mesh = *storage.meshes[mesh_idx]; // if this mesh has layer data for this layer process it if (! done_this_layer && mesh.layers.size() > layer_nr) { @@ -371,9 +372,9 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh : storage.meshes) { - retractionAndWipeConfigFromSettings(mesh.settings, &mesh.retraction_wipe_config); + retractionAndWipeConfigFromSettings(mesh->settings, &mesh->retraction_wipe_config); } } @@ -505,9 +506,9 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) } else { - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh : storage.meshes) { - if (mesh.settings.get(interface_height_setting) + if (mesh->settings.get(interface_height_setting) >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) { // Some roofs are quite thick. @@ -913,10 +914,11 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } else { - z = storage.meshes[0].layers[layer_nr].printZ; // stub default + z = storage.meshes[0]->layers[layer_nr].printZ; // stub default // find printZ of first actual printed mesh - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("cutting_mesh") || mesh.settings.get("infill_mesh")) { @@ -951,8 +953,9 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } coord_t max_inner_wall_width = 0; - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; coord_t mesh_inner_wall_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (layer_nr == 0) { @@ -1022,14 +1025,14 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (size_t mesh_idx : mesh_order) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const auto& mesh = storage.meshes[mesh_idx]; const MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; - if (mesh.settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE + if (mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE && extruder_nr - == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! + == mesh->settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! ) { - addMeshLayerToGCode_meshSurfaceMode(storage, mesh, mesh_config, gcode_layer); + addMeshLayerToGCode_meshSurfaceMode(storage, *mesh, mesh_config, gcode_layer); } else { @@ -1382,7 +1385,7 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s std::vector::iterator mesh_group = Application::getInstance().current_slice->scene.current_mesh_group; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const auto& mesh = *storage.meshes[mesh_idx]; if (mesh.getExtruderIsUsed(extruder_nr)) { const Mesh& mesh_data = mesh_group->meshes[mesh_idx]; @@ -1449,11 +1452,12 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, c void FffGcodeWriter::addMeshLayerToGCode( const SliceDataStorage& storage, - const SliceMeshStorage& mesh, + const std::shared_ptr& mesh_ptr, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const { + const auto& mesh = *mesh_ptr; if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { return; @@ -1471,7 +1475,7 @@ void FffGcodeWriter::addMeshLayerToGCode( return; } - gcode_layer.setMesh(std::make_shared(mesh)); + gcode_layer.setMesh(mesh_ptr); ZSeamConfig z_seam_config; if (mesh.isPrinted()) //"normal" meshes with walls, skin, infill, etc. get the traditional part ordering based on the z-seam settings. @@ -2259,8 +2263,9 @@ bool FffGcodeWriter::processInsets( Polygons outlines_below; AABB boundaryBox(part.outline); - for (const SliceMeshStorage& m : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& m = *mesh_ptr; if (m.isPrinted()) { for (const SliceLayerPart& prevLayerPart : m.layers[gcode_layer.getLayerNr() - 1].parts) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 03c83c0627..942345ccc1 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -275,8 +275,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe Mesh& mesh = scene.current_mesh_group->meshes[meshIdx]; // always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes - storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh - SliceMeshStorage& meshStorage = storage.meshes.back(); + storage.meshes.push_back(std::make_shared(& meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh + auto& meshStorage = *storage.meshes.back(); // only create layer parts for normal meshes const bool is_support_modifier = AreaSupport::handleSupportModifierMesh(storage, mesh.settings, slicer); @@ -347,8 +347,9 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // compute layer count and remove first empty layers // there is no separate progress stage for removeEmptyFisrtLayer (TODO) unsigned int slice_layer_count = 0; - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (! mesh.settings.get("infill_mesh") && ! mesh.settings.get("anti_overhang_mesh")) { slice_layer_count = std::max(slice_layer_count, mesh.layers.size()); @@ -369,7 +370,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& std::multimap order_to_mesh_indices; for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - order_to_mesh_indices.emplace(storage.meshes[mesh_idx].settings.get("infill_mesh_order"), mesh_idx); + order_to_mesh_indices.emplace(storage.meshes[mesh_idx]->settings.get("infill_mesh_order"), mesh_idx); } for (std::pair& order_and_mesh_idx : order_to_mesh_indices) { @@ -432,9 +433,9 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& spdlog::debug("Meshes post-processing"); // meshes post processing - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh : storage.meshes) { - processDerivedWallsSkinInfill(mesh); + processDerivedWallsSkinInfill(*mesh); } spdlog::debug("Processing gradual support"); @@ -449,7 +450,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( ProgressStageEstimator& inset_skin_progress_estimate) { size_t mesh_idx = mesh_order[mesh_order_idx]; - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; size_t mesh_layer_count = mesh.layers.size(); if (mesh.settings.get("infill_mesh")) { @@ -513,7 +514,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( for (size_t other_mesh_order_idx = mesh_order_idx + 1; other_mesh_order_idx < mesh_order.size(); ++other_mesh_order_idx) { const size_t other_mesh_idx = mesh_order[other_mesh_order_idx]; - SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx]; + SliceMeshStorage& other_mesh = *storage.meshes[other_mesh_idx]; if (other_mesh.settings.get("infill_mesh")) { AABB3D aabb = scene.current_mesh_group->meshes[mesh_idx].getAABB(); @@ -553,7 +554,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order) { size_t mesh_idx = mesh_order[mesh_order_idx]; - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; coord_t surface_line_width = mesh.settings.get("wall_line_width_0"); mesh.layer_nr_max_filled_layer = -1; @@ -585,7 +586,7 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const siz { break; // all previous meshes have been processed } - SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx]; + SliceMeshStorage& other_mesh = *storage.meshes[other_mesh_idx]; if (layer_idx >= static_cast(other_mesh.layers.size())) { // there can be no interaction between the infill mesh and this other non-infill mesh continue; @@ -741,8 +742,9 @@ bool FffPolygonGenerator::isEmptyLayer(SliceDataStorage& storage, const LayerInd return false; } } - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (layer_idx >= mesh.layers.size()) { continue; @@ -782,8 +784,9 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, size { spdlog::info("Removing {} layers because they are empty", n_empty_first_layers); const coord_t layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; std::vector& layers = mesh.layers; if (layers.size() > n_empty_first_layers) { @@ -852,8 +855,9 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage max_print_height_per_extruder.resize(extruder_count, -(raft_layers + 1)); // Initialize all as -1 (or lower in case of raft). { // compute max_object_height_per_extruder // Height of the meshes themselves. - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh")) { continue; // Special type of mesh that doesn't get printed. diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 600869daac..b7d24b1b11 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -159,8 +159,9 @@ Polygons LayerPlan::computeCombBoundary(const CombBoundary boundary_type) } else { - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; const SliceLayer& layer = mesh.layers[static_cast(layer_nr)]; // don't process infill_mesh or anti_overhang_mesh if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) @@ -282,7 +283,7 @@ bool LayerPlan::setExtruder(const size_t extruder_nr) } return true; } -void LayerPlan::setMesh(const std::shared_ptr& mesh) +void LayerPlan::setMesh(const std::shared_ptr& mesh) { current_mesh = mesh; } @@ -1187,9 +1188,9 @@ void LayerPlan::addLinesByOptimizer( if (layer_nr >= 0) { // determine how much the skin/infill lines overlap the combing boundary - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh : storage.meshes) { - const coord_t overlap = std::max(mesh.settings.get("skin_overlap_mm"), mesh.settings.get("infill_overlap_mm")); + const coord_t overlap = std::max(mesh->settings.get("skin_overlap_mm"), mesh->settings.get("infill_overlap_mm")); if (overlap > dist) { dist = overlap; @@ -1846,7 +1847,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const bool acceleration_travel_enabled = mesh_group_settings.get("acceleration_travel_enabled"); const bool jerk_enabled = mesh_group_settings.get("jerk_enabled"); const bool jerk_travel_enabled = mesh_group_settings.get("jerk_travel_enabled"); - std::shared_ptr current_mesh; + std::shared_ptr current_mesh; for (size_t extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++) { diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index fef53433e7..6ddcef2c93 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -49,8 +49,9 @@ TreeModelVolumes::TreeModelVolumes( coord_t min_maximum_area_deviation = std::numeric_limits::max(); support_rests_on_model = false; - for (auto [mesh_idx, mesh] : storage.meshes | ranges::views::enumerate) + for (auto [mesh_idx, mesh_ptr] : storage.meshes | ranges::views::enumerate) { + auto& mesh = *mesh_ptr; bool added = false; for (auto [idx, layer_outline] : layer_outlines_ | ranges::views::enumerate) { @@ -103,7 +104,7 @@ TreeModelVolumes::TreeModelVolumes( { // Workaround for compiler bug on apple-clang -- Closure won't properly capture variables in capture lists in outer scope. const auto& mesh_idx_l = mesh_idx; - const auto& mesh_l = mesh; + const auto& mesh_l = *mesh; // ^^^ Remove when fixed (and rename accordingly in the below parallel-for). cura::parallel_for( diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 5c7585cfbb..d9e56458c4 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -38,8 +38,9 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) { size_t largest_printed_mesh_idx = 0; - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; TreeSupportSettings::some_model_contains_thick_roof |= mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); TreeSupportSettings::has_to_rely_on_min_xy_dist_only |= @@ -48,8 +49,9 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) // Group all meshes that can be processed together. NOTE this is different from mesh-groups! // Only one setting object is needed per group, as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes. - for (auto [mesh_idx, mesh] : storage.meshes | ranges::views::enumerate) + for (auto [mesh_idx, mesh_ptr] : storage.meshes | ranges::views::enumerate) { + auto& mesh = *mesh_ptr; const bool non_supportable_mesh = mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh"); if (mesh.settings.get("support_structure") != ESupportStructure::TREE || ! mesh.settings.get("support_enable") || non_supportable_mesh) { @@ -76,14 +78,14 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) } // no need to do this per mesh group as adaptive layers and raft setting are not setable per mesh. - if (storage.meshes[largest_printed_mesh_idx].layers.back().printZ < mesh.layers.back().printZ) + if (storage.meshes[largest_printed_mesh_idx]->layers.back().printZ < mesh.layers.back().printZ) { largest_printed_mesh_idx = mesh_idx; } } - std::vector known_z(storage.meshes[largest_printed_mesh_idx].layers.size()); + std::vector known_z(storage.meshes[largest_printed_mesh_idx]->layers.size()); - for (auto [z, layer] : ranges::views::enumerate(storage.meshes[largest_printed_mesh_idx].layers)) + for (auto [z, layer] : ranges::views::enumerate(storage.meshes[largest_printed_mesh_idx]->layers)) { known_z[z] = layer.printZ; } @@ -152,7 +154,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) // ### Place tips of the support tree for (size_t mesh_idx : processing.second) { - generateInitialAreas(storage.meshes[mesh_idx], move_bounds, storage); + generateInitialAreas(*storage.meshes[mesh_idx], move_bounds, storage); } const auto t_gen = std::chrono::high_resolution_clock::now(); @@ -206,7 +208,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); const size_t z_distance_top_layers = round_up_divide(z_distance_top, diff --git a/src/bridge.cpp b/src/bridge.cpp index f6f4b41f04..a395f7bd79 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -24,8 +24,9 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl const Ratio sparse_infill_max_density = settings.get("bridge_sparse_infill_max_density"); // include parts from all meshes - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; if (mesh.isPrinted()) { const coord_t infill_line_distance = mesh.settings.get("infill_line_distance"); diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 33e7d4882e..88a716e47a 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -430,7 +430,7 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::operator()(gcode_paths_modify_response::native_value_type& original_value, const gcode_paths_modify_response::value_type& message) const { std::vector paths; - using map_t = std::unordered_map>; + using map_t = std::unordered_map>; auto meshes = original_value | ranges::views::filter( [](const auto& path) diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 2f9dd2acea..467b130f5c 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -124,9 +124,9 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } mesh_configs.reserve(storage.meshes.size()); - for (const SliceMeshStorage& mesh_storage : storage.meshes) + for (const auto& mesh_storage : storage.meshes) { - mesh_configs.emplace_back(mesh_storage, layer_thickness, layer_nr, line_width_factor_per_extruder); + mesh_configs.emplace_back(*mesh_storage, layer_thickness, layer_nr, line_width_factor_per_extruder); } support_infill_config.reserve(MAX_INFILL_COMBINE); @@ -224,7 +224,7 @@ void PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storag { // meshes for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; const SpeedDerivatives initial_layer_speed_config{ .speed = mesh.settings.get("speed_print_layer_0"), .acceleration = mesh.settings.get("acceleration_print_layer_0"), diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 788ac2e45a..c890783f8c 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -300,16 +300,16 @@ Polygons Polygons total; if (layer_nr >= 0) { - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { - if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh") - || (extruder_nr != -1 && extruder_nr != int(mesh.settings.get("wall_0_extruder_nr").extruder_nr))) + if (mesh->settings.get("infill_mesh") || mesh->settings.get("anti_overhang_mesh") + || (extruder_nr != -1 && extruder_nr != int(mesh->settings.get("wall_0_extruder_nr").extruder_nr))) { continue; } - const SliceLayer& layer = mesh.layers[layer_nr]; + const SliceLayer& layer = mesh->layers[layer_nr]; layer.getOutlines(total, external_polys_only); - if (mesh.settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) + if (mesh->settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) { total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(MM2INT(0.1))); } @@ -381,9 +381,9 @@ std::vector SliceDataStorage::getExtrudersUsed() const // support // support is presupposed to be present... - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { - if (mesh.settings.get("support_enable") || mesh.settings.get("support_mesh")) + if (mesh->settings.get("support_enable") || mesh->settings.get("support_mesh")) { ret[mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr] = true; ret[mesh_group_settings.get("support_infill_extruder_nr").extruder_nr] = true; @@ -399,11 +399,11 @@ std::vector SliceDataStorage::getExtrudersUsed() const } // all meshes are presupposed to actually have content - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { - ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr); + ret[extruder_nr] = ret[extruder_nr] || mesh->getExtruderIsUsed(extruder_nr); } } return ret; @@ -508,11 +508,11 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) if (include_models) { - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { - ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr, layer_nr); + ret[extruder_nr] = ret[extruder_nr] || mesh->getExtruderIsUsed(extruder_nr, layer_nr); } } } diff --git a/src/support.cpp b/src/support.cpp index 1adb0b1ae7..a55d21b53f 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -599,8 +599,9 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp void AreaSupport::generateOverhangAreas(SliceDataStorage& storage) { - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; @@ -645,14 +646,14 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; } - Settings* infill_settings = &storage.meshes[mesh_idx].settings; - Settings* roof_settings = &storage.meshes[mesh_idx].settings; - Settings* bottom_settings = &storage.meshes[mesh_idx].settings; + Settings* infill_settings = &storage.meshes[mesh_idx]->settings; + Settings* roof_settings = &storage.meshes[mesh_idx]->settings; + Settings* bottom_settings = &storage.meshes[mesh_idx]->settings; if (mesh.settings.get("support_mesh")) { if ((mesh.settings.get("support_mesh_drop_down") && support_meshes_drop_down_handled) @@ -694,7 +695,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) // handle support interface for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; @@ -725,12 +726,12 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) AABB3D aabb; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; } - Settings& infill_settings = storage.meshes[mesh_idx].settings; + Settings& infill_settings = storage.meshes[mesh_idx]->settings; if (mesh.settings.get("support_mesh")) { // use extruder train settings rather than the per-object settings of the first support mesh encountered. @@ -1039,7 +1040,7 @@ void AreaSupport::generateSupportAreasForMesh( const size_t layer_count, std::vector& support_areas) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; const ESupportStructure support_structure = mesh.settings.get("support_structure"); const bool is_support_mesh_place_holder From e171220546f94a513991d83bc5dd87e2ca7f44dd Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 15 Sep 2023 07:21:23 +0000 Subject: [PATCH 519/656] Applied clang-format. --- include/FffGcodeWriter.h | 7 +- include/SkeletalTrapezoidationEdge.h | 38 +- include/SkeletalTrapezoidationJoint.h | 25 +- src/FffPolygonGenerator.cpp | 2 +- src/TreeSupport.cpp | 1280 +++++++++++++------------ src/bridge.cpp | 46 +- 6 files changed, 756 insertions(+), 642 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 2cde3c40d4..0e3dea645c 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -333,7 +333,12 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const std::shared_ptr& mesh_ptr, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; + void addMeshLayerToGCode( + const SliceDataStorage& storage, + const std::shared_ptr& mesh_ptr, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. diff --git a/include/SkeletalTrapezoidationEdge.h b/include/SkeletalTrapezoidationEdge.h index 4c0bfb365c..60f464deb5 100644 --- a/include/SkeletalTrapezoidationEdge.h +++ b/include/SkeletalTrapezoidationEdge.h @@ -4,19 +4,24 @@ #ifndef SKELETAL_TRAPEZOIDATION_EDGE_H #define SKELETAL_TRAPEZOIDATION_EDGE_H -#include // smart pointers +#include "utils/ExtrusionJunction.h" + #include +#include // smart pointers #include -#include "utils/ExtrusionJunction.h" - namespace cura { class SkeletalTrapezoidationEdge { private: - enum class Central : int { UNKNOWN = -1, NO = 0, YES = 1}; + enum class Central : int + { + UNKNOWN = -1, + NO = 0, + YES = 1 + }; public: /*! @@ -28,9 +33,11 @@ class SkeletalTrapezoidationEdge int lower_bead_count; coord_t feature_radius; // The feature radius at which this transition is placed TransitionMiddle(coord_t pos, int lower_bead_count, coord_t feature_radius) - : pos(pos), lower_bead_count(lower_bead_count) + : pos(pos) + , lower_bead_count(lower_bead_count) , feature_radius(feature_radius) - {} + { + } }; /*! @@ -42,8 +49,11 @@ class SkeletalTrapezoidationEdge int lower_bead_count; bool is_lower_end; // Whether this is the ed of the transition with lower bead count TransitionEnd(coord_t pos, int lower_bead_count, bool is_lower_end) - : pos(pos), lower_bead_count(lower_bead_count), is_lower_end(is_lower_end) - {} + : pos(pos) + , lower_bead_count(lower_bead_count) + , is_lower_end(is_lower_end) + { + } }; enum class EdgeType : int @@ -55,12 +65,14 @@ class SkeletalTrapezoidationEdge EdgeType type; SkeletalTrapezoidationEdge() - : SkeletalTrapezoidationEdge(EdgeType::NORMAL) - {} + : SkeletalTrapezoidationEdge(EdgeType::NORMAL) + { + } SkeletalTrapezoidationEdge(const EdgeType& type) - : type(type) - , is_central(Central::UNKNOWN) - {} + : type(type) + , is_central(Central::UNKNOWN) + { + } bool isCentral() const { diff --git a/include/SkeletalTrapezoidationJoint.h b/include/SkeletalTrapezoidationJoint.h index 7c7575a07b..e8c5ad42a7 100644 --- a/include/SkeletalTrapezoidationJoint.h +++ b/include/SkeletalTrapezoidationJoint.h @@ -1,20 +1,21 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2020 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SKELETAL_TRAPEZOIDATION_JOINT_H #define SKELETAL_TRAPEZOIDATION_JOINT_H -#include // smart pointers - #include "BeadingStrategy/BeadingStrategy.h" #include "utils/IntPoint.h" +#include // smart pointers + namespace cura { class SkeletalTrapezoidationJoint { using Beading = BeadingStrategy::Beading; + public: struct BeadingPropagation { @@ -27,17 +28,20 @@ class SkeletalTrapezoidationJoint , dist_to_bottom_source(0) , dist_from_top_source(0) , is_upward_propagated_only(false) - {} + { + } }; coord_t distance_to_boundary; coord_t bead_count; - float transition_ratio; //! The distance near the skeleton to leave free because this joint is in the middle of a transition, as a fraction of the inner bead width of the bead at the higher transition. + float transition_ratio; //! The distance near the skeleton to leave free because this joint is in the middle of a transition, as a fraction of the inner bead width of the bead + //! at the higher transition. SkeletalTrapezoidationJoint() - : distance_to_boundary(-1) - , bead_count(-1) - , transition_ratio(0) - {} + : distance_to_boundary(-1) + , bead_count(-1) + , transition_ratio(0) + { + } bool hasBeading() const { @@ -53,7 +57,6 @@ class SkeletalTrapezoidationJoint } private: - std::weak_ptr beading; }; diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 942345ccc1..edd5e48d6e 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -275,7 +275,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe Mesh& mesh = scene.current_mesh_group->meshes[meshIdx]; // always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes - storage.meshes.push_back(std::make_shared(& meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh + storage.meshes.push_back(std::make_shared(&meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh auto& meshStorage = *storage.meshes.back(); // only create layer parts for normal meshes diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index d9e56458c4..9cc4c7d404 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2,6 +2,7 @@ // CuraEngine is released under the terms of the AGPLv3 or higher #include "TreeSupport.h" + #include "Application.h" //To get settings. #include "TreeSupportTipGenerator.h" #include "TreeSupportUtils.h" @@ -17,20 +18,20 @@ #include "utils/polygonUtils.h" //For moveInside. #include "utils/section_type.h" -#include -#include -#include #include #include #include #include +#include #include + +#include +#include +#include #include #include #include -#include - namespace cura { @@ -41,10 +42,10 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) for (const auto& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; - TreeSupportSettings::some_model_contains_thick_roof |= - mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); - TreeSupportSettings::has_to_rely_on_min_xy_dist_only |= - mesh.settings.get("support_top_distance") == 0 || mesh.settings.get("support_bottom_distance") == 0 || mesh.settings.get("min_feature_size") < (FUDGE_LENGTH * 2); + TreeSupportSettings::some_model_contains_thick_roof |= mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); + TreeSupportSettings::has_to_rely_on_min_xy_dist_only |= mesh.settings.get("support_top_distance") == 0 + || mesh.settings.get("support_bottom_distance") == 0 + || mesh.settings.get("min_feature_size") < (FUDGE_LENGTH * 2); } // Group all meshes that can be processed together. NOTE this is different from mesh-groups! @@ -68,8 +69,10 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) { added = true; grouped_mesh.second.emplace_back(mesh_idx); - // Handle some settings that are only used for performance reasons. This ensures that a horrible set setting intended to improve performance can not reduce it drastically. - grouped_mesh.first.performance_interface_skip_layers = std::min(grouped_mesh.first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers); + // Handle some settings that are only used for performance reasons. This ensures that a horrible set setting intended to improve performance can not reduce it + // drastically. + grouped_mesh.first.performance_interface_skip_layers + = std::min(grouped_mesh.first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers); } } if (! added) @@ -95,8 +98,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) mesh.first.setActualZ(known_z); } - placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(),Polygons()); - + placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(), Polygons()); } void TreeSupport::generateSupportAreas(SliceDataStorage& storage) @@ -112,12 +114,14 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) } // Process every mesh group. These groups can not be processed parallel, as the processing in each group is parallelized, and nested parallelization is disables and slow. - for (auto [counter, processing] : grouped_meshes | ranges::views::enumerate ) + for (auto [counter, processing] : grouped_meshes | ranges::views::enumerate) { // process each combination of meshes - std::vector> move_bounds(storage.support.supportLayers.size()); // Value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas. + std::vector> move_bounds( + storage.support.supportLayers + .size()); // Value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas. - additional_required_support_area=std::vector(storage.support.supportLayers.size(),Polygons()); + additional_required_support_area = std::vector(storage.support.supportLayers.size(), Polygons()); spdlog::info("Processing support tree mesh group {} of {} containing {} meshes.", counter + 1, grouped_meshes.size(), grouped_meshes[counter].second.size()); @@ -125,8 +129,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) auto t_start = std::chrono::high_resolution_clock::now(); // get all already existing support areas and exclude them - cura::parallel_for - ( + cura::parallel_for( LayerIndex(0), LayerIndex(storage.support.supportLayers.size()), [&](const LayerIndex layer_idx) @@ -140,12 +143,20 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) } exclude[layer_idx] = exlude_at_layer.unionPolygons(); scripta::log("tree_support_exclude", exclude[layer_idx], SectionType::SUPPORT, layer_idx); - } - ); - config = processing.first; // This struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generateInitialAreas have knowledge of the existence of multiple meshes being processed. + }); + config = processing.first; // This struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generateInitialAreas have knowledge of + // the existence of multiple meshes being processed. progress_multiplier = 1.0 / double(grouped_meshes.size()); progress_offset = counter == 0 ? 0 : TREE_PROGRESS_TOTAL * (double(counter) * progress_multiplier); - volumes_ = TreeModelVolumes(storage, config.maximum_move_distance, config.maximum_move_distance_slow, config.support_line_width / 2, processing.second.front(), progress_multiplier, progress_offset, exclude); + volumes_ = TreeModelVolumes( + storage, + config.maximum_move_distance, + config.maximum_move_distance_slow, + config.support_line_width / 2, + processing.second.front(), + progress_multiplier, + progress_offset, + exclude); // ### Precalculate avoidances, collision etc. precalculate(storage, processing.second); @@ -176,19 +187,18 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) const auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); const auto dur_draw = 0.001 * std::chrono::duration_cast(t_draw - t_place).count(); const auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); - spdlog::info - ( + spdlog::info( "Total time used creating Tree support for the currently grouped meshes: {} ms. Different subtasks:\n" - "Calculating Avoidance: {} ms Creating inital influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as support {} ms", + "Calculating Avoidance: {} ms Creating inital influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as " + "support {} ms", dur_total, dur_pre_gen, dur_gen, dur_path, dur_place, - dur_draw - ); + dur_draw); + - for (auto& layer : move_bounds) { for (auto elem : layer) @@ -212,7 +222,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); const size_t z_distance_top_layers = round_up_divide(z_distance_top, - layer_height) + 1; // Support must always be 1 layer below overhang. + layer_height) + 1; // Support must always be 1 layer below overhang. if (mesh.overhang_areas.size() <= z_distance_top_layers) { continue; @@ -222,7 +232,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector max_layer) // iterates over multiple meshes { @@ -244,8 +254,7 @@ void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, std::vector tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas); } -void TreeSupport::mergeHelper -( +void TreeSupport::mergeHelper( std::map& reduced_aabb, std::map& input_aabb, const PropertyAreasUnordered& to_bp_areas, @@ -254,12 +263,14 @@ void TreeSupport::mergeHelper PropertyAreasUnordered& insert_bp_areas, PropertyAreasUnordered& insert_model_areas, PropertyAreasUnordered& insert_influence, - std::vector& erase, const LayerIndex layer_idx -) + std::vector& erase, + const LayerIndex layer_idx) { const bool first_merge_iteration = reduced_aabb.empty(); // If this is the first iteration, all elements in input have to be merged with each other - const std::function getRadiusFunction = - [&](const size_t distance_to_top, const double buildplate_radius_increases) { return config.getRadius(distance_to_top, buildplate_radius_increases); }; + const std::function getRadiusFunction = [&](const size_t distance_to_top, const double buildplate_radius_increases) + { + return config.getRadius(distance_to_top, buildplate_radius_increases); + }; for (auto& influence : input_aabb) { bool merged = false; @@ -271,7 +282,7 @@ void TreeSupport::mergeHelper AABB aabb = reduced_check.second; if (aabb.hit(influence_aabb)) { - if (!first_merge_iteration && input_aabb.count(reduced_check.first)) + if (! first_merge_iteration && input_aabb.count(reduced_check.first)) { break; // Do not try to merge elements that already should have been merged. Done for potential performance improvement. } @@ -280,14 +291,15 @@ void TreeSupport::mergeHelper // ^^^ We do not want to merge a gracious with a non gracious area as bad placement could negatively impact the dependability of the whole subtree. const bool merging_to_bp = reduced_check.first.to_buildplate && influence.first.to_buildplate; const bool merging_min_and_regular_xy = reduced_check.first.use_min_xy_dist != influence.first.use_min_xy_dist; - // ^^^ Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased by the delta to the larger it is engulfed by it already. + // ^^^ Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased by the delta to the larger it is engulfed by it + // already. // But because a different collision may be removed from the in drawArea generated circles, this assumption could be wrong. - const bool merging_different_range_limits = - reduced_check.first.influence_area_limit_active && influence.first.influence_area_limit_active && influence.first.influence_area_limit_range != reduced_check.first.influence_area_limit_range; + const bool merging_different_range_limits = reduced_check.first.influence_area_limit_active && influence.first.influence_area_limit_active + && influence.first.influence_area_limit_range != reduced_check.first.influence_area_limit_range; coord_t increased_to_model_radius = 0; size_t larger_to_model_dtt = 0; - if (!merging_to_bp) + if (! merging_to_bp) { const coord_t infl_radius = config.getRadius(influence.first); // Get the real radius increase as the user does not care for the collision model. const coord_t redu_radius = config.getRadius(reduced_check.first); @@ -313,14 +325,9 @@ void TreeSupport::mergeHelper // If a merge could place a stable branch on unstable ground, would be increasing the radius further than allowed to when merging to model and to_bp trees or // would merge to model before it is known they will even been drawn the merge is skipped - if - ( - merging_min_and_regular_xy || - merging_gracious_and_non_gracious || - increased_to_model_radius > config.max_to_model_radius_increase || - (!merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && !reduced_check.first.supports_roof && !influence.first.supports_roof) || - merging_different_range_limits - ) + if (merging_min_and_regular_xy || merging_gracious_and_non_gracious || increased_to_model_radius > config.max_to_model_radius_increase + || (! merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && ! reduced_check.first.supports_roof && ! influence.first.supports_roof) + || merging_different_range_limits) { continue; } @@ -329,41 +336,29 @@ void TreeSupport::mergeHelper Polygons relevant_redu; if (merging_to_bp) { - relevant_infl = to_bp_areas.count(influence.first) ? to_bp_areas.at(influence.first) : Polygons(); // influence.first is a new element => not required to check if it was changed - relevant_redu = - insert_bp_areas.count - ( - reduced_check.first) ? - insert_bp_areas[reduced_check.first] : - (to_bp_areas.count(reduced_check.first) ? to_bp_areas.at(reduced_check.first) : Polygons() - ); + relevant_infl = to_bp_areas.count(influence.first) ? to_bp_areas.at(influence.first) + : Polygons(); // influence.first is a new element => not required to check if it was changed + relevant_redu = insert_bp_areas.count(reduced_check.first) ? insert_bp_areas[reduced_check.first] + : (to_bp_areas.count(reduced_check.first) ? to_bp_areas.at(reduced_check.first) : Polygons()); } else { relevant_infl = to_model_areas.count(influence.first) ? to_model_areas.at(influence.first) : Polygons(); - relevant_redu = - insert_model_areas.count - ( - reduced_check.first) ? - insert_model_areas[reduced_check.first] : - (to_model_areas.count(reduced_check.first) ? to_model_areas.at(reduced_check.first) : Polygons() - ); + relevant_redu = insert_model_areas.count(reduced_check.first) + ? insert_model_areas[reduced_check.first] + : (to_model_areas.count(reduced_check.first) ? to_model_areas.at(reduced_check.first) : Polygons()); } const bool red_bigger = config.getCollisionRadius(reduced_check.first) > config.getCollisionRadius(influence.first); - std::pair smaller_rad = - red_bigger ? - std::pair(influence.first, relevant_infl) : - std::pair(reduced_check.first, relevant_redu); - std::pair bigger_rad = - red_bigger ? - std::pair(reduced_check.first, relevant_redu) : - std::pair(influence.first, relevant_infl); + std::pair smaller_rad = red_bigger ? std::pair(influence.first, relevant_infl) + : std::pair(reduced_check.first, relevant_redu); + std::pair bigger_rad = red_bigger ? std::pair(reduced_check.first, relevant_redu) + : std::pair(influence.first, relevant_infl); const coord_t real_radius_delta = std::abs(config.getRadius(bigger_rad.first) - config.getRadius(smaller_rad.first)); const coord_t smaller_collision_radius = config.getCollisionRadius(smaller_rad.first); // the area of the bigger radius is used to ensure correct placement regarding the relevant avoidance, so if that would change an invalid area may be created - if (!bigger_rad.first.can_use_safe_radius && smaller_rad.first.can_use_safe_radius) + if (! bigger_rad.first.can_use_safe_radius && smaller_rad.first.can_use_safe_radius) { continue; } @@ -378,23 +373,23 @@ void TreeSupport::mergeHelper // a branch (of the larger collision radius) placed in this intersection, has already engulfed the branch of the smaller collision radius. // Because of this a merge may happen even if the influence areas (that represent possible center points of branches) do not intersect yet. // Remember that collision radius <= real radius as otherwise this assumption would be false. - const Polygons small_rad_increased_by_big_minus_small = - TreeSupportUtils::safeOffsetInc - ( - smaller_rad.second, - real_radius_delta, volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), - 2 * (config.xy_distance + smaller_collision_radius - EPSILON), // Epsilon avoids possible rounding errors - 0, - 0, - config.support_line_distance / 2, - &config.simplifier - ); + const Polygons small_rad_increased_by_big_minus_small = TreeSupportUtils::safeOffsetInc( + smaller_rad.second, + real_radius_delta, + volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), + 2 * (config.xy_distance + smaller_collision_radius - EPSILON), // Epsilon avoids possible rounding errors + 0, + 0, + config.support_line_distance / 2, + &config.simplifier); Polygons intersect = small_rad_increased_by_big_minus_small.intersection(bigger_rad.second); - if (intersect.area() > 1) // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines) + if (intersect.area() + > 1) // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines) { - // Check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change it. - if (intersect.offset(-FUDGE_LENGTH/2).area() <= 1) + // Check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change + // it. + if (intersect.offset(-FUDGE_LENGTH / 2).area() <= 1) { continue; } @@ -415,8 +410,7 @@ void TreeSupport::mergeHelper increased_to_model_radius = std::max(reduced_check.first.increased_to_model_radius, influence.first.increased_to_model_radius); } - const TreeSupportElement key - ( + const TreeSupportElement key( reduced_check.first, influence.first, layer_idx - 1, @@ -425,31 +419,30 @@ void TreeSupport::mergeHelper getRadiusFunction, config.diameter_scale_bp_radius, config.branch_radius, - config.diameter_angle_scale_factor - ); + config.diameter_angle_scale_factor); - const auto getIntersectInfluence = - [&] (const PropertyAreasUnordered& insert_infl, const PropertyAreas& infl_areas) - { - const Polygons infl_small = insert_infl.count(smaller_rad.first) ? insert_infl.at(smaller_rad.first) : (infl_areas.count(smaller_rad.first) ? infl_areas.at(smaller_rad.first) : Polygons()); - const Polygons infl_big = insert_infl.count(bigger_rad.first) ? insert_infl.at(bigger_rad.first) : (infl_areas.count(bigger_rad.first) ? infl_areas.at(bigger_rad.first) : Polygons()); - const Polygons small_rad_increased_by_big_minus_small_infl = - TreeSupportUtils::safeOffsetInc - ( - infl_small, - real_radius_delta, - volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), - 2 * (config.xy_distance + smaller_collision_radius - EPSILON), - 0, - 0, - config.support_line_distance / 2, - &config.simplifier - ); - return small_rad_increased_by_big_minus_small_infl.intersection(infl_big); // If the one with the bigger radius with the lower radius removed overlaps we can merge. - }; + const auto getIntersectInfluence = [&](const PropertyAreasUnordered& insert_infl, const PropertyAreas& infl_areas) + { + const Polygons infl_small = insert_infl.count(smaller_rad.first) ? insert_infl.at(smaller_rad.first) + : (infl_areas.count(smaller_rad.first) ? infl_areas.at(smaller_rad.first) : Polygons()); + const Polygons infl_big = insert_infl.count(bigger_rad.first) ? insert_infl.at(bigger_rad.first) + : (infl_areas.count(bigger_rad.first) ? infl_areas.at(bigger_rad.first) : Polygons()); + const Polygons small_rad_increased_by_big_minus_small_infl = TreeSupportUtils::safeOffsetInc( + infl_small, + real_radius_delta, + volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), + 2 * (config.xy_distance + smaller_collision_radius - EPSILON), + 0, + 0, + config.support_line_distance / 2, + &config.simplifier); + return small_rad_increased_by_big_minus_small_infl.intersection( + infl_big); // If the one with the bigger radius with the lower radius removed overlaps we can merge. + }; Polygons intersect_influence; - intersect_influence = TreeSupportUtils::safeUnion(intersect, getIntersectInfluence(insert_influence, influence_areas)); // Rounding errors again. Do not ask me where or why. + intersect_influence + = TreeSupportUtils::safeUnion(intersect, getIntersectInfluence(insert_influence, influence_areas)); // Rounding errors again. Do not ask me where or why. Polygons intersect_to_model; if (merging_to_bp && config.support_rests_on_model) @@ -475,8 +468,10 @@ void TreeSupport::mergeHelper erase.emplace_back(reduced_check.first); erase.emplace_back(influence.first); - const Polygons merge = intersect.unionPolygons(intersect_to_model).offset(config.getRadius(key), ClipperLib::jtRound).difference(volumes_.getCollision(0, layer_idx - 1)); - // ^^^ Regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). + const Polygons merge + = intersect.unionPolygons(intersect_to_model).offset(config.getRadius(key), ClipperLib::jtRound).difference(volumes_.getCollision(0, layer_idx - 1)); + // ^^^ Regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a + // negative area.). // And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer. reduced_aabb.erase(reduced_check.first); // This invalidates reduced_check. @@ -495,13 +490,7 @@ void TreeSupport::mergeHelper } } -void TreeSupport::mergeInfluenceAreas -( - PropertyAreasUnordered& to_bp_areas, - PropertyAreas& to_model_areas, - PropertyAreas& influence_areas, - LayerIndex layer_idx -) +void TreeSupport::mergeInfluenceAreas(PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, LayerIndex layer_idx) { /* * Idea behind this is that the calculation of merges can be accelerated a bit using divide and conquer: @@ -521,13 +510,15 @@ void TreeSupport::mergeInfluenceAreas // max_bucket_count is input_size/min_elements_per_bucket round down to the next 2^n. // The rounding to 2^n is to ensure improved performance, as every iteration two buckets will be merged, halving the amount of buckets. - // If halving would cause an uneven count, e.g. 3 Then bucket 0 and 1 would have to be merged, and in the next iteration the last remaining buckets. This is assumed to not be optimal performance-wise. + // If halving would cause an uneven count, e.g. 3 Then bucket 0 and 1 would have to be merged, and in the next iteration the last remaining buckets. This is assumed to not be + // optimal performance-wise. const size_t max_bucket_count = std::pow(2, std::floor(std::log(round_up_divide(input_size, min_elements_per_bucket)))); int bucket_count = std::min(max_bucket_count, num_threads); // do not use more buckets than available threads. // To achieve that every element in a bucket is already correctly merged with other elements in this bucket // an extra empty bucket is created for each bucket, and the elements are merged into the empty one. - // Each thread will then process two buckets by merging all elements in the second bucket into the first one as mergeHelper will disable not trying to merge elements from the same bucket in this case. + // Each thread will then process two buckets by merging all elements in the second bucket into the first one as mergeHelper will disable not trying to merge elements from the + // same bucket in this case. std::vector buckets_area(2 * bucket_count); std::vector> buckets_aabb(2 * bucket_count); @@ -551,8 +542,7 @@ void TreeSupport::mergeInfluenceAreas } // Precalculate the AABBs from the influence areas. - cura::parallel_for - ( + cura::parallel_for( 0, buckets_area.size() / 2, [&](size_t idx) // +=2 as in the beginning only uneven buckets will be filled @@ -564,8 +554,7 @@ void TreeSupport::mergeInfluenceAreas outer_support_wall_aabb.expand(config.getRadius(input_pair.first)); buckets_aabb[idx].emplace(input_pair.first, outer_support_wall_aabb); } - } - ); + }); while (buckets_area.size() > 1) { @@ -575,16 +564,14 @@ void TreeSupport::mergeInfluenceAreas std::vector insert_influence(buckets_area.size() / 2); std::vector> erase(buckets_area.size() / 2); - cura::parallel_for - ( + cura::parallel_for( 0, (coord_t)buckets_area.size() / 2, [&](size_t bucket_pair_idx) { bucket_pair_idx *= 2; // this is eqivalent to a parallel for(size_t idx=0;idx x) mutable { return x.empty(); }); + const auto position_aabb = std::remove_if( + buckets_aabb.begin(), + buckets_aabb.end(), + [&](const std::map x) mutable + { + return x.empty(); + }); buckets_aabb.erase(position_aabb, buckets_aabb.end()); } } -std::optional TreeSupport::increaseSingleArea -( +std::optional TreeSupport::increaseSingleArea( AreaIncreaseSettings settings, LayerIndex layer_idx, TreeSupportElement* parent, const Polygons& relevant_offset, - Polygons& to_bp_data, Polygons& to_model_data, + Polygons& to_bp_data, + Polygons& to_model_data, Polygons& increased, const coord_t overspeed, - const bool mergelayer -) + const bool mergelayer) { TreeSupportElement current_elem(parent); // Also increases DTT by one. Polygons check_layer_data; @@ -659,22 +655,19 @@ std::optional TreeSupport::increaseSingleArea increased = relevant_offset; if (overspeed > 0) { - const coord_t safe_movement_distance = - (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); + const coord_t safe_movement_distance = (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); // The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later. // The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later. - increased = - TreeSupportUtils::safeOffsetInc - ( - increased, - overspeed, - volumes_.getWallRestriction(config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist), - safe_movement_distance, - safe_movement_distance + radius, - 1, - config.support_line_distance / 2, - nullptr - ); + increased = TreeSupportUtils::safeOffsetInc( + increased, + overspeed, + volumes_.getWallRestriction(config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist), + safe_movement_distance, + safe_movement_distance + radius, + 1, + config.support_line_distance / 2, + nullptr); } if (settings.no_error && settings.move) { @@ -711,7 +704,8 @@ std::optional TreeSupport::increaseSingleArea } else { - to_model_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, AvoidanceType::COLLISION, true, settings.use_min_distance))); + to_model_data + = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, AvoidanceType::COLLISION, true, settings.use_min_distance))); } } } @@ -720,29 +714,35 @@ std::optional TreeSupport::increaseSingleArea if (settings.increase_radius && check_layer_data.area() > 1) { - std::function validWithRadius = - [&](coord_t next_radius) + std::function validWithRadius = [&](coord_t next_radius) + { + if (volumes_.ceilRadius(next_radius, settings.use_min_distance) <= volumes_.ceilRadius(radius, settings.use_min_distance)) { - if (volumes_.ceilRadius(next_radius, settings.use_min_distance) <= volumes_.ceilRadius(radius, settings.use_min_distance)) - { - return true; - } + return true; + } - Polygons to_bp_data_2; - if (current_elem.to_buildplate) - { - // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. - to_bp_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)).unionPolygons(); - } - Polygons to_model_data_2; - if (config.support_rests_on_model && !current_elem.to_buildplate) - { - to_model_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, current_elem.to_model_gracious? settings.type:AvoidanceType::COLLISION, true, settings.use_min_distance)).unionPolygons(); - } - Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; + Polygons to_bp_data_2; + if (current_elem.to_buildplate) + { + // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. + to_bp_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)).unionPolygons(); + } + Polygons to_model_data_2; + if (config.support_rests_on_model && ! current_elem.to_buildplate) + { + to_model_data_2 = increased + .difference(volumes_.getAvoidance( + next_radius, + layer_idx - 1, + current_elem.to_model_gracious ? settings.type : AvoidanceType::COLLISION, + true, + settings.use_min_distance)) + .unionPolygons(); + } + Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; - return check_layer_data_2.area() > 1; - }; + return check_layer_data_2.area() > 1; + }; coord_t ceil_radius_before = volumes_.ceilRadius(radius, settings.use_min_distance); // If the Collision Radius is smaller than the actual radius, check if it can catch up without violating the avoidance. @@ -756,18 +756,15 @@ std::optional TreeSupport::increaseSingleArea current_ceil_radius = volumes_.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance); } size_t resulting_eff_dtt = current_elem.effective_radius_height; - while - ( - resulting_eff_dtt + 1 < current_elem.distance_to_top && - config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= current_ceil_radius && - config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= config.getRadius(current_elem) - ) + while (resulting_eff_dtt + 1 < current_elem.distance_to_top && config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= current_ceil_radius + && config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= config.getRadius(current_elem)) { resulting_eff_dtt++; } current_elem.effective_radius_height = resulting_eff_dtt; - // If catchup is not possible, it is likely that there is a hole below. Assuming the branches are in some kind of bowl, the branches should still stay away from the wall of the bowl if possible. + // If catchup is not possible, it is likely that there is a hole below. Assuming the branches are in some kind of bowl, the branches should still stay away from the + // wall of the bowl if possible. if (config.getCollisionRadius(current_elem) < config.increase_radius_until_radius && config.getCollisionRadius(current_elem) < config.getRadius(current_elem)) { Polygons new_to_bp_data; @@ -781,7 +778,7 @@ std::optional TreeSupport::increaseSingleArea to_bp_data = new_to_bp_data; } } - if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) + if (config.support_rests_on_model && (! current_elem.to_buildplate || mergelayer)) { new_to_model_data = to_model_data.difference(volumes_.getCollision(config.getRadius(current_elem), layer_idx - 1, current_elem.use_min_xy_dist)); if (new_to_model_data.area() > EPSILON) @@ -790,21 +787,24 @@ std::optional TreeSupport::increaseSingleArea } } } - } radius = config.getCollisionRadius(current_elem); const coord_t foot_radius_increase = config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0)); const double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - config.getRadius(current_elem)) / foot_radius_increase); - // ^^^ Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become bigger than precalculated. + // ^^^ Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become + // bigger than precalculated. - // If the support_rest_preference is GRACEFUL, increase buildplate_radius_increases anyway. This does ONLY affect the CollisionRadius, as the regular radius only includes the buildplate_radius_increases when the SupportElement is to_buildplate (which it can not be when support_rest_preference is GRACEFUL). - // If the branch later rests on the buildplate the to_buildplate flag will only need to be updated to ensure that the radius is also correctly increased. - // Downside is that the enlargement of the CollisionRadius can cause branches, that could rest on the model if the radius was not increased, to instead rest on the buildplate. - // A better way could be changing avoidance to model to not include the buildplate and then calculate avoidances by combining the to model avoidance without the radius increase with the to buildplate avoidance with the larger radius. - // This would require ensuring all requests for the avoidance would have to ensure that the correct hybrid avoidance is requested (which would only be relevant when support_rest_preference is GRACEFUL) - // Also unioning areas when an avoidance is requested may also have a relevant performance impact, so there can be an argument made that the current workaround is preferable. - const bool increase_bp_foot = planned_foot_increase > 0 && (current_elem.to_buildplate || (current_elem.to_model_gracious && config.support_rest_preference == RestPreference::GRACEFUL)); + // If the support_rest_preference is GRACEFUL, increase buildplate_radius_increases anyway. This does ONLY affect the CollisionRadius, as the regular radius only includes + // the buildplate_radius_increases when the SupportElement is to_buildplate (which it can not be when support_rest_preference is GRACEFUL). If the branch later rests on the + // buildplate the to_buildplate flag will only need to be updated to ensure that the radius is also correctly increased. Downside is that the enlargement of the + // CollisionRadius can cause branches, that could rest on the model if the radius was not increased, to instead rest on the buildplate. A better way could be changing + // avoidance to model to not include the buildplate and then calculate avoidances by combining the to model avoidance without the radius increase with the to buildplate + // avoidance with the larger radius. This would require ensuring all requests for the avoidance would have to ensure that the correct hybrid avoidance is requested (which + // would only be relevant when support_rest_preference is GRACEFUL) Also unioning areas when an avoidance is requested may also have a relevant performance impact, so there + // can be an argument made that the current workaround is preferable. + const bool increase_bp_foot + = planned_foot_increase > 0 && (current_elem.to_buildplate || (current_elem.to_model_gracious && config.support_rest_preference == RestPreference::GRACEFUL)); if (increase_bp_foot && config.getRadius(current_elem) >= config.branch_radius && config.getRadius(current_elem) >= config.increase_radius_until_radius) @@ -822,26 +822,30 @@ std::optional TreeSupport::increaseSingleArea { to_bp_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); } - if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) + if (config.support_rests_on_model && (! current_elem.to_buildplate || mergelayer)) { - to_model_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious? settings.type:AvoidanceType::COLLISION, true, settings.use_min_distance))); + to_model_data = TreeSupportUtils::safeUnion(increased.difference( + volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious ? settings.type : AvoidanceType::COLLISION, true, settings.use_min_distance))); } check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; if (check_layer_data.area() < 1) { - spdlog::error("Lost area by doing catch up from {} to radius {}", ceil_radius_before, volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance)); + spdlog::error( + "Lost area by doing catch up from {} to radius {}", + ceil_radius_before, + volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance)); } } } - if (current_elem.influence_area_limit_active && !current_elem.use_min_xy_dist && check_layer_data.area() > 1 && (current_elem.to_model_gracious || current_elem.distance_to_top <= config.min_dtt_to_model)) + if (current_elem.influence_area_limit_active && ! current_elem.use_min_xy_dist && check_layer_data.area() > 1 + && (current_elem.to_model_gracious || current_elem.distance_to_top <= config.min_dtt_to_model)) { - const coord_t max_radius_increase = - std::max - ( - static_cast((config.branch_radius - config.min_radius) / config.tip_layers), - static_cast((config.branch_radius * config.diameter_angle_scale_factor) + config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0))) - ); + const coord_t max_radius_increase = std::max( + static_cast((config.branch_radius - config.min_radius) / config.tip_layers), + static_cast( + (config.branch_radius * config.diameter_angle_scale_factor) + + config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0)))); bool limit_range_validated = false; // Rounding errors in a while loop can cause non-termination, so better safe than sorry. See https://github.com/Ultimaker/Cura/issues/14133 for an example. to_bp_data = TreeSupportUtils::safeUnion(to_bp_data); @@ -868,7 +872,7 @@ std::optional TreeSupport::increaseSingleArea limit_range_validated = true; } } - if (!limit_range_validated) + if (! limit_range_validated) { const coord_t reach_increase = std::max(current_elem.influence_area_limit_range / 4, (config.maximum_move_distance + max_radius_increase)); current_elem.influence_area_limit_range += reach_increase; @@ -880,19 +884,17 @@ std::optional TreeSupport::increaseSingleArea return check_layer_data.area() > 1 ? std::optional(current_elem) : std::optional(); } -void TreeSupport::increaseAreas -( +void TreeSupport::increaseAreas( PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, std::vector& bypass_merge_areas, const std::vector& last_layer, - const LayerIndex layer_idx, const bool mergelayer -) + const LayerIndex layer_idx, + const bool mergelayer) { std::mutex critical_sections; - cura::parallel_for - ( + cura::parallel_for( 0, last_layer.size(), [&](const size_t idx) @@ -912,25 +914,28 @@ void TreeSupport::increaseAreas // As the branch may have become larger the distance between these 2 walls is smaller than the distance of the center points. // These extra distance is added to the movement distance possible for this layer. - coord_t extra_speed = EPSILON; // The extra speed is added to both movement distances. Also move 5 microns faster than allowed to avoid rounding errors, this may cause issues at VERY VERY small layer heights. + coord_t extra_speed = EPSILON; // The extra speed is added to both movement distances. Also move 5 microns faster than allowed to avoid rounding errors, this may cause + // issues at VERY VERY small layer heights. coord_t extra_slow_speed = 0; // Only added to the slow movement distance. const coord_t ceiled_parent_radius = volumes_.ceilRadius(config.getCollisionRadius(*parent), parent->use_min_xy_dist); const coord_t projected_radius_increased = config.getRadius(parent->effective_radius_height + 1, parent->buildplate_radius_increases); const coord_t projected_radius_delta = projected_radius_increased - config.getCollisionRadius(*parent); - // When z distance is more than one layer up and down the Collision used to calculate the wall restriction will always include the wall (and not just the xy_min_distance) of the layer above and below like this (d = blocked area because of z distance): + // When z distance is more than one layer up and down the Collision used to calculate the wall restriction will always include the wall (and not just the + // xy_min_distance) of the layer above and below like this (d = blocked area because of z distance): /* * layer z+1:dddddiiiiiioooo * layer z+0:xxxxxdddddddddd * layer z-1:dddddxxxxxxxxxx * For more detailed visualisation see calculateWallRestrictions */ - const coord_t safe_movement_distance = - (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + - (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); - if (ceiled_parent_radius == volumes_.ceilRadius(projected_radius_increased, parent->use_min_xy_dist) || projected_radius_increased < config.increase_radius_until_radius) + const coord_t safe_movement_distance = (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); + if (ceiled_parent_radius == volumes_.ceilRadius(projected_radius_increased, parent->use_min_xy_dist) + || projected_radius_increased < config.increase_radius_until_radius) { - // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of the slower moving wall + // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of + // the slower moving wall extra_speed += projected_radius_delta; } else @@ -940,16 +945,20 @@ void TreeSupport::increaseAreas extra_slow_speed += std::min(projected_radius_delta, (config.maximum_move_distance + extra_speed) - (config.maximum_move_distance_slow + extra_slow_speed)); } - if (config.layer_start_bp_radius > layer_idx && config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.buildplate_radius_increases)) + if (config.layer_start_bp_radius > layer_idx + && config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.buildplate_radius_increases)) { // Can guarantee elephant foot radius increase. - if (ceiled_parent_radius == volumes_.ceilRadius(config.getRadius(parent->effective_radius_height + 1, parent->buildplate_radius_increases + 1), parent->use_min_xy_dist)) + if (ceiled_parent_radius + == volumes_.ceilRadius(config.getRadius(parent->effective_radius_height + 1, parent->buildplate_radius_increases + 1), parent->use_min_xy_dist)) { extra_speed += config.branch_radius * config.diameter_scale_bp_radius; } else { - extra_slow_speed += std::min(coord_t(config.branch_radius * config.diameter_scale_bp_radius), config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed)); + extra_slow_speed += std::min( + coord_t(config.branch_radius * config.diameter_scale_bp_radius), + config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed)); } } @@ -970,60 +979,70 @@ void TreeSupport::increaseAreas // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found std::deque order; - std::function insertSetting = - [&](const AreaIncreaseSettings& settings, bool back) + std::function insertSetting = [&](const AreaIncreaseSettings& settings, bool back) + { + if (std::find(order.begin(), order.end(), settings) == order.end()) { - if (std::find(order.begin(), order.end(), settings) == order.end()) + if (back) { - if (back) - { - order.emplace_back(settings); - } - else - { - order.emplace_front(settings); - } + order.emplace_back(settings); } - }; + else + { + order.emplace_front(settings); + } + } + }; const bool parent_moved_slow = elem.last_area_increase.increase_speed < config.maximum_move_distance; const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::SLOW; - if - ( - elem.last_area_increase.move && - elem.last_area_increase.no_error && - elem.can_use_safe_radius && - ! mergelayer && - ! avoidance_speed_mismatch && - (elem.distance_to_top >= config.tip_layers || parent_moved_slow) - ) + if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && ! mergelayer && ! avoidance_speed_mismatch + && (elem.distance_to_top >= config.tip_layers || parent_moved_slow)) { // Assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster. const auto slow_or_fast = elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed; - insertSetting(AreaIncreaseSettings(elem.last_area_increase.type, slow_or_fast, increase_radius, elem.last_area_increase.no_error, ! use_min_radius, elem.last_area_increase.move), true); - insertSetting(AreaIncreaseSettings(elem.last_area_increase.type, slow_or_fast, ! increase_radius, elem.last_area_increase.no_error, ! use_min_radius, elem.last_area_increase.move), true); + insertSetting( + AreaIncreaseSettings( + elem.last_area_increase.type, + slow_or_fast, + increase_radius, + elem.last_area_increase.no_error, + ! use_min_radius, + elem.last_area_increase.move), + true); + insertSetting( + AreaIncreaseSettings( + elem.last_area_increase.type, + slow_or_fast, + ! increase_radius, + elem.last_area_increase.no_error, + ! use_min_radius, + elem.last_area_increase.move), + true); } // Branch may still go though a hole, so a check has to be done whether the hole was already passed, and the regular avoidance can be used. if (! elem.can_use_safe_radius) { // If the radius until which it is always increased can not be guaranteed, move fast. This is to avoid holes smaller than the real branch radius. // This does not guarantee the avoidance of such holes, but ensures they are avoided if possible. - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, !move), true); // Did we go through the hole. + insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, ! move), true); // Did we go through the hole. // In many cases the definition of hole is overly restrictive, so to avoid unnecessary fast movement in the tip, it is ignored there for a bit. // This CAN cause a branch to go though a hole it otherwise may have avoided. if (elem.distance_to_top < round_up_divide(config.tip_layers, 2)) { - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, slow_speed, increase_radius, no_error, ! use_min_radius, !move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, slow_speed, increase_radius, no_error, ! use_min_radius, ! move), true); } - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, !move), true); // Did we manage to avoid the hole, - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, !increase_radius, no_error, ! use_min_radius, move), true); - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, !increase_radius, no_error, ! use_min_radius, move), true); + insertSetting( + AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, ! move), + true); // Did we manage to avoid the hole, + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); } else { insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, move), true); - // While moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, which looks similar to a layer shift and can reduce stability. - // As such idx have chosen to only use the user setting for radius increases as a friendly recommendation. + // While moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, which looks similar to a + // layer shift and can reduce stability. As such idx have chosen to only use the user setting for radius increases as a friendly recommendation. insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, ! increase_radius, no_error, ! use_min_radius, move), true); // a (See above.) if (elem.distance_to_top < config.tip_layers) { @@ -1046,43 +1065,48 @@ void TreeSupport::increaseAreas order = new_order; } - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move), true); //simplifying is very important for performance, but before an error is compensated by moving faster it makes sense to check to see if the simplifying has caused issues + insertSetting( + AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, ! no_error, elem.use_min_xy_dist, move), + true); // simplifying is very important for performance, but before an error is compensated by moving faster it makes sense to check to see if the simplifying has + // caused issues // The getAccumulatedPlaceable0 intersection is just a quick and dirty check to see that at least a part of the branch would correctly rest on the model. - // Proper way would be to offset getAccumulatedPlaceable0 by -radius first, but the small benefit to maybe detect an error, that should not be happening anyway is not worth the performance impact in the expected case when a branch rests on the model. - if (elem.to_buildplate || (elem.to_model_gracious && (parent->area->intersection(volumes_.getPlaceableAreas(radius, layer_idx)).empty())) || (!elem.to_model_gracious && (parent->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx)).empty())) ) // Error case. + // Proper way would be to offset getAccumulatedPlaceable0 by -radius first, but the small benefit to maybe detect an error, that should not be happening anyway is not + // worth the performance impact in the expected case when a branch rests on the model. + if (elem.to_buildplate || (elem.to_model_gracious && (parent->area->intersection(volumes_.getPlaceableAreas(radius, layer_idx)).empty())) + || (! elem.to_model_gracious && (parent->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx)).empty()))) // Error case. { // It is normal that we won't be able to find a new area at some point in time if we won't be able to reach layer 0 aka have to connect with the model. - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, !increase_radius, !no_error, elem.use_min_xy_dist, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, ! increase_radius, ! no_error, elem.use_min_xy_dist, move), true); } if (elem.distance_to_top < elem.dont_move_until && elem.can_use_safe_radius) // Only do not move when holes would be avoided in every case. { - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, 0, increase_radius, no_error, !use_min_radius, !move), false); // Only do not move when already in a no hole avoidance with the regular xy distance. + insertSetting( + AreaIncreaseSettings(AvoidanceType::SLOW, 0, increase_radius, no_error, ! use_min_radius, ! move), + false); // Only do not move when already in a no hole avoidance with the regular xy distance. } Polygons inc_wo_collision; - // Check whether it is faster to calculate the area increased with the fast speed independently from the slow area, or time could be saved by reusing the slow area to calculate the fast one. - // Calculated by comparing the steps saved when calculating independently with the saved steps when not. - const bool offset_independent_faster = - (radius / safe_movement_distance - (((config.maximum_move_distance + extra_speed) < (radius + safe_movement_distance)) ? 1 : 0)) > - (round_up_divide((extra_speed + extra_slow_speed + config.maximum_move_distance_slow), safe_movement_distance)); + // Check whether it is faster to calculate the area increased with the fast speed independently from the slow area, or time could be saved by reusing the slow area to + // calculate the fast one. Calculated by comparing the steps saved when calculating independently with the saved steps when not. + const bool offset_independent_faster = (radius / safe_movement_distance - (((config.maximum_move_distance + extra_speed) < (radius + safe_movement_distance)) ? 1 : 0)) + > (round_up_divide((extra_speed + extra_slow_speed + config.maximum_move_distance_slow), safe_movement_distance)); for (AreaIncreaseSettings settings : order) { if (settings.move) { if (offset_slow.empty() && (settings.increase_speed == slow_speed || ! offset_independent_faster)) { - offset_slow = - TreeSupportUtils::safeOffsetInc - ( - *parent->area, - extra_speed + extra_slow_speed + config.maximum_move_distance_slow, - wall_restriction, - safe_movement_distance, offset_independent_faster ? safe_movement_distance + radius : 0, - 2, // Offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. - config.support_line_distance / 2, - &config.simplifier - ).unionPolygons(); + offset_slow = TreeSupportUtils::safeOffsetInc( + *parent->area, + extra_speed + extra_slow_speed + config.maximum_move_distance_slow, + wall_restriction, + safe_movement_distance, + offset_independent_faster ? safe_movement_distance + radius : 0, + 2, // Offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. + config.support_line_distance / 2, + &config.simplifier) + .unionPolygons(); // At this point one can see that the Polygons class was never made for precision in the single digit micron range. } @@ -1090,22 +1114,30 @@ void TreeSupport::increaseAreas { if (offset_independent_faster) { - offset_fast = - TreeSupportUtils::safeOffsetInc - ( - *parent->area, - extra_speed + config.maximum_move_distance, - wall_restriction, - safe_movement_distance, offset_independent_faster ? safe_movement_distance + radius : 0, - 1, - config.support_line_distance / 2, - &config.simplifier - ).unionPolygons(); + offset_fast = TreeSupportUtils::safeOffsetInc( + *parent->area, + extra_speed + config.maximum_move_distance, + wall_restriction, + safe_movement_distance, + offset_independent_faster ? safe_movement_distance + radius : 0, + 1, + config.support_line_distance / 2, + &config.simplifier) + .unionPolygons(); } else { const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed); - offset_fast = TreeSupportUtils::safeOffsetInc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independent_faster ? 2 : 1, config.support_line_distance / 2, &config.simplifier).unionPolygons(); + offset_fast = TreeSupportUtils::safeOffsetInc( + offset_slow, + delta_slow_fast, + wall_restriction, + safe_movement_distance, + safe_movement_distance + radius, + offset_independent_faster ? 2 : 1, + config.support_line_distance / 2, + &config.simplifier) + .unionPolygons(); } } } @@ -1114,20 +1146,22 @@ void TreeSupport::increaseAreas // Check for errors! if (! settings.no_error) { - - // If the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased + // If the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, + // it still actually has an area that can be increased Polygons lines_offset = TreeSupportUtils::toPolylines(*parent->area).offsetPolyLine(EPSILON); Polygons base_error_area = parent->area->unionPolygons(lines_offset); result = increaseSingleArea(settings, layer_idx, parent, base_error_area, to_bp_data, to_model_data, inc_wo_collision, settings.increase_speed, mergelayer); - if(fast_speed < settings.increase_speed) + if (fast_speed < settings.increase_speed) { - spdlog::warn - ( + spdlog::warn( "Influence area could not be increased! Data about the Influence area: " - "Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} safe {} until move {} \n " - "Parent {}: Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} safe {} until move {}", - radius, layer_idx - 1, + "Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} safe {} until " + "move {} \n " + "Parent {}: Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} " + "safe {} until move {}", + radius, + layer_idx - 1, elem.next_height, elem.distance_to_top, elem.buildplate_radius_increases, @@ -1138,20 +1172,29 @@ void TreeSupport::increaseAreas elem.dont_move_until, fmt::ptr(parent), config.getCollisionRadius(*parent), - layer_idx, parent->next_height, + layer_idx, + parent->next_height, parent->distance_to_top, parent->buildplate_radius_increases, parent->use_min_xy_dist, parent->to_buildplate, parent->to_model_gracious, parent->can_use_safe_radius, - parent->dont_move_until - ); + parent->dont_move_until); } } else { - result = increaseSingleArea(settings, layer_idx, parent, settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, std::max(settings.increase_speed-fast_speed,coord_t(0)), mergelayer); + result = increaseSingleArea( + settings, + layer_idx, + parent, + settings.increase_speed == slow_speed ? offset_slow : offset_fast, + to_bp_data, + to_model_data, + inc_wo_collision, + std::max(settings.increase_speed - fast_speed, coord_t(0)), + mergelayer); } if (result) @@ -1160,7 +1203,10 @@ void TreeSupport::increaseAreas radius = config.getCollisionRadius(elem); elem.last_area_increase = settings; add = true; - bypass_merge = ! settings.move || (settings.use_min_distance && elem.distance_to_top < config.tip_layers); // Do not merge if the branch should not move or the priority has to be to get farther away from the model. + bypass_merge + = ! settings.move + || (settings.use_min_distance + && elem.distance_to_top < config.tip_layers); // Do not merge if the branch should not move or the priority has to be to get farther away from the model. if (settings.move) { elem.dont_move_until = 0; @@ -1190,7 +1236,9 @@ void TreeSupport::increaseAreas if (add) { - Polygons max_influence_area = TreeSupportUtils::safeUnion(inc_wo_collision.difference(volumes_.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), TreeSupportUtils::safeUnion(to_bp_data, to_model_data)); + Polygons max_influence_area = TreeSupportUtils::safeUnion( + inc_wo_collision.difference(volumes_.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), + TreeSupportUtils::safeUnion(to_bp_data, to_model_data)); // ^^^ Note: union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be { @@ -1222,8 +1270,7 @@ void TreeSupport::increaseAreas // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent->result_on_layer = Point(-1, -1); } - } - ); + }); } void TreeSupport::createLayerPathing(std::vector>& move_bounds) @@ -1241,7 +1288,9 @@ void TreeSupport::createLayerPathing(std::vector>& bool new_element = false; // Ensure at least one merge operation per 3mm height, 50 layers, 1 mm movement of slow speed or 5mm movement of fast speed (whatever is lowest). Values were guessed. - size_t max_merge_every_x_layers = std::min(std::min(5000 / (std::max(config.maximum_move_distance, static_cast(100))), 1000 / std::max(config.maximum_move_distance_slow, static_cast(20))), 3000 / config.layer_height); + size_t max_merge_every_x_layers = std::min( + std::min(5000 / (std::max(config.maximum_move_distance, static_cast(100))), 1000 / std::max(config.maximum_move_distance_slow, static_cast(20))), + 3000 / config.layer_height); size_t merge_every_x_layers = 1; // Calculate the influence areas for each layer below (Top down) @@ -1259,7 +1308,8 @@ void TreeSupport::createLayerPathing(std::vector>& PropertyAreas influence_areas; // Over this map will be iterated when merging, as such it has to be ordered to ensure deterministic results. PropertyAreas to_model_areas; // The area of these SupportElement is not set, to avoid to much allocation and deallocation on the heap. PropertyAreasUnordered to_bp_areas; // Same. - std::vector bypass_merge_areas; // Different to the other maps of SupportElements as these here have the area already set, as they are already to be inserted into move_bounds. + std::vector + bypass_merge_areas; // Different to the other maps of SupportElements as these here have the area already set, as they are already to be inserted into move_bounds. const auto time_a = std::chrono::high_resolution_clock::now(); @@ -1279,7 +1329,7 @@ void TreeSupport::createLayerPathing(std::vector>& last_merge = layer_idx; reduced_by_merging = count_before_merge > influence_areas.size(); - if (! reduced_by_merging && !new_element) + if (! reduced_by_merging && ! new_element) { merge_every_x_layers = std::min(max_merge_every_x_layers, merge_every_x_layers + 1); } @@ -1334,16 +1384,21 @@ void TreeSupport::setPointsOnAreas(const TreeSupportElement* elem) for (TreeSupportElement* next_elem : elem->parents) { - if (next_elem->result_on_layer != Point(-1, -1)) // If the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof. + if (next_elem->result_on_layer + != Point(-1, -1)) // If the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof. { continue; } Point from = elem->result_on_layer; - if (!(next_elem->area->inside(from, true))) + if (! (next_elem->area->inside(from, true))) { - PolygonUtils::moveInside(*next_elem->area, from, 0); // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1 - // It is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. While this seems like a problem it may for example occur after merges. + PolygonUtils::moveInside( + *next_elem->area, + from, + 0); // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1 + // It is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. While this seems like a problem it may for example + // occur after merges. } next_elem->result_on_layer = from; // Do not call recursive because then amount of layers would be restricted by the stack size. @@ -1367,7 +1422,8 @@ bool TreeSupport::setToModelContact(std::vector>& Polygons valid_place_area; - // Check for every layer upwards, up to the point where this influence area was created (either by initial insert or merge) if the branch could be placed on it, and highest up layer index. + // Check for every layer upwards, up to the point where this influence area was created (either by initial insert or merge) if the branch could be placed on it, and highest + // up layer index. for (LayerIndex layer_check = layer_idx; check->next_height >= layer_check; layer_check++) { Polygons check_valid_place_area = check->area->intersection(volumes_.getPlaceableAreas(config.getCollisionRadius(*check), layer_check)); @@ -1411,7 +1467,8 @@ bool TreeSupport::setToModelContact(std::vector>& } } - for (LayerIndex layer = layer_idx + 1; layer < last_successfull_layer - 1; ++layer) // NOTE: Use of 'itoa' will make this crash in the loop, even though the operation should be equivalent. + for (LayerIndex layer = layer_idx + 1; layer < last_successfull_layer - 1; + ++layer) // NOTE: Use of 'itoa' will make this crash in the loop, even though the operation should be equivalent. { move_bounds[layer].erase(checked[layer - layer_idx]); delete checked[layer - layer_idx]->area; @@ -1440,29 +1497,36 @@ bool TreeSupport::setToModelContact(std::vector>& else // can not add graceful => just place it here and hope for the best { Point best = first_elem->next_position; - Polygons valid_place_area = first_elem->area->difference(volumes_.getAvoidance(config.getCollisionRadius(first_elem), layer_idx, AvoidanceType::COLLISION, first_elem->use_min_xy_dist)); + Polygons valid_place_area + = first_elem->area->difference(volumes_.getAvoidance(config.getCollisionRadius(first_elem), layer_idx, AvoidanceType::COLLISION, first_elem->use_min_xy_dist)); - if (!valid_place_area.inside(best, true)) + if (! valid_place_area.inside(best, true)) { - if (!valid_place_area.empty()) + if (! valid_place_area.empty()) { PolygonUtils::moveInside(valid_place_area, best); } else { bool found_partial_placement; - for (coord_t radius_offset : { -config.getCollisionRadius(first_elem), -config.getCollisionRadius(first_elem) / 2, coord_t(0) }) // Interestingly the first radius is working most of the time, even though it seems like it shouldn't. + for (coord_t radius_offset : { -config.getCollisionRadius(first_elem), + -config.getCollisionRadius(first_elem) / 2, + coord_t(0) }) // Interestingly the first radius is working most of the time, even though it seems like it shouldn't. { valid_place_area = first_elem->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx).offset(radius_offset)); - if (!valid_place_area.empty()) + if (! valid_place_area.empty()) { PolygonUtils::moveInside(valid_place_area, best); - spdlog::warn("Not able to place branch fully on non support blocker at layer {} using offset {} for radius {}", layer_idx, radius_offset, config.getCollisionRadius(first_elem)); + spdlog::warn( + "Not able to place branch fully on non support blocker at layer {} using offset {} for radius {}", + layer_idx, + radius_offset, + config.getCollisionRadius(first_elem)); found_partial_placement = true; break; } } - if (!found_partial_placement) + if (! found_partial_placement) { PolygonUtils::moveInside(*first_elem->area, best); spdlog::warn("Not able to place branch on non support blocker at layer {}", layer_idx); @@ -1478,7 +1542,8 @@ bool TreeSupport::setToModelContact(std::vector>& void TreeSupport::createNodesFromArea(std::vector>& move_bounds) { - // Initialize points on layer 0, with a "random" point in the influence area. Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result. + // Initialize points on layer 0, with a "random" point in the influence area. Point is chosen based on an inaccurate estimate where the branches will split into two, but every + // point inside the influence area would produce a valid result. std::unordered_set remove; for (TreeSupportElement* init : move_bounds[0]) { @@ -1499,7 +1564,8 @@ void TreeSupport::createNodesFromArea(std::vector> } else { - // If the support_rest_preference is GRACEFUL the collision radius is increased, but the radius will only be increased if the element is to_buildplate, so if the branch rests on the buildplate, the element will have to be updated to include this information. + // If the support_rest_preference is GRACEFUL the collision radius is increased, but the radius will only be increased if the element is to_buildplate, so if the + // branch rests on the buildplate, the element will have to be updated to include this information. init->setToBuildplateForAllParents(true); } } @@ -1520,17 +1586,23 @@ void TreeSupport::createNodesFromArea(std::vector> bool removed = false; if (elem->result_on_layer == Point(-1, -1)) // Check if the resulting center point is not yet set. { - if (elem->to_buildplate || (!elem->to_buildplate && elem->distance_to_top < config.min_dtt_to_model && !elem->supports_roof)) + if (elem->to_buildplate || (! elem->to_buildplate && elem->distance_to_top < config.min_dtt_to_model && ! elem->supports_roof)) { if (elem->to_buildplate) { - spdlog::error("Uninitialized Influence area targeting ({},{}) at target_height: {} layer: {}", elem->target_position.X, elem->target_position.Y, elem->target_height, layer_idx); + spdlog::error( + "Uninitialized Influence area targeting ({},{}) at target_height: {} layer: {}", + elem->target_position.X, + elem->target_position.Y, + elem->target_height, + layer_idx); } remove.emplace(elem); // We dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set. removed = true; for (TreeSupportElement* parent : elem->parents) { - // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set. As this branch needs to be removed => all parents result_on_layer have to be invalidated. + // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set. As this branch needs + // to be removed => all parents result_on_layer have to be invalidated. parent->result_on_layer = Point(-1, -1); } continue; @@ -1546,7 +1618,7 @@ void TreeSupport::createNodesFromArea(std::vector> } } - if (!removed) + if (! removed) { setPointsOnAreas(elem); // Element is valid now setting points in the layer above. } @@ -1563,7 +1635,10 @@ void TreeSupport::createNodesFromArea(std::vector> } } -void TreeSupport::generateBranchAreas(std::vector>& linear_data, std::vector>& layer_tree_polygons, const std::map& inverse_tree_order) +void TreeSupport::generateBranchAreas( + std::vector>& linear_data, + std::vector>& layer_tree_polygons, + const std::map& inverse_tree_order) { double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC; constexpr int progress_report_steps = 10; @@ -1582,136 +1657,135 @@ void TreeSupport::generateBranchAreas(std::vector - ( - 0, - linear_data.size(), - [&](const size_t idx) - { - TreeSupportElement* elem = linear_data[idx].second; - coord_t radius = config.getRadius(*elem); - bool parent_uses_min = false; - TreeSupportElement* child_elem = inverse_tree_order.count(elem) ? inverse_tree_order.at(elem) : nullptr; + cura::parallel_for( + 0, + linear_data.size(), + [&](const size_t idx) + { + TreeSupportElement* elem = linear_data[idx].second; + coord_t radius = config.getRadius(*elem); + bool parent_uses_min = false; + TreeSupportElement* child_elem = inverse_tree_order.count(elem) ? inverse_tree_order.at(elem) : nullptr; - // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area. - std::vector> movement_directions{ std::pair(Point(0, 0), radius) }; - if (! elem->skip_ovalisation) + // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area. + std::vector> movement_directions{ std::pair(Point(0, 0), radius) }; + if (! elem->skip_ovalisation) + { + if (child_elem != nullptr) { - if (child_elem != nullptr) - { - Point movement = (child_elem->result_on_layer - elem->result_on_layer); - movement_directions.emplace_back(movement, radius); - } - for (TreeSupportElement* parent : elem->parents) - { - Point movement = (parent->result_on_layer - elem->result_on_layer); - movement_directions.emplace_back(movement, std::max(config.getRadius(parent), config.support_line_width)); - parent_uses_min |= parent->use_min_xy_dist; - } - - for (Point target: elem->additional_ovalization_targets) - { - Point movement = (target - elem->result_on_layer); - movement_directions.emplace_back(movement, std::max(radius, config.support_line_width)); - } - + Point movement = (child_elem->result_on_layer - elem->result_on_layer); + movement_directions.emplace_back(movement, radius); } - - coord_t max_speed_sqd = 0; - std::function generateArea = - [&](coord_t offset) + for (TreeSupportElement* parent : elem->parents) { - Polygons poly; + Point movement = (parent->result_on_layer - elem->result_on_layer); + movement_directions.emplace_back(movement, std::max(config.getRadius(parent), config.support_line_width)); + parent_uses_min |= parent->use_min_xy_dist; + } - for (std::pair movement : movement_directions) - { - max_speed_sqd = std::max(max_speed_sqd, vSize2(movement.first)); + for (Point target : elem->additional_ovalization_targets) + { + Point movement = (target - elem->result_on_layer); + movement_directions.emplace_back(movement, std::max(radius, config.support_line_width)); + } + } - // Visualization: https://jsfiddle.net/0zvcq39L/2/ - // Ovalizes the circle to an ellipse, that contains both old center and new target position. - double used_scale = (movement.second + offset) / (1.0 * config.branch_radius); - Point center_position = elem->result_on_layer + movement.first / 2; - const double moveX = movement.first.X / (used_scale * config.branch_radius); - const double moveY = movement.first.Y / (used_scale * config.branch_radius); - const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); + coord_t max_speed_sqd = 0; + std::function generateArea = [&](coord_t offset) + { + Polygons poly; - std::array matrix = - { - used_scale * (1 + moveX * moveX * vsize_inv), - used_scale * (0 + moveX * moveY * vsize_inv), - used_scale * (0 + moveX * moveY * vsize_inv), - used_scale * (1 + moveY * moveY * vsize_inv), - }; - Polygon circle; - for (Point vertex : branch_circle) - { - vertex = Point(matrix[0] * vertex.X + matrix[1] * vertex.Y, matrix[2] * vertex.X + matrix[3] * vertex.Y); - circle.add(center_position + vertex); - } - poly.add(circle.offset(0)); + for (std::pair movement : movement_directions) + { + max_speed_sqd = std::max(max_speed_sqd, vSize2(movement.first)); + + // Visualization: https://jsfiddle.net/0zvcq39L/2/ + // Ovalizes the circle to an ellipse, that contains both old center and new target position. + double used_scale = (movement.second + offset) / (1.0 * config.branch_radius); + Point center_position = elem->result_on_layer + movement.first / 2; + const double moveX = movement.first.X / (used_scale * config.branch_radius); + const double moveY = movement.first.Y / (used_scale * config.branch_radius); + const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); + + std::array matrix = { + used_scale * (1 + moveX * moveX * vsize_inv), + used_scale * (0 + moveX * moveY * vsize_inv), + used_scale * (0 + moveX * moveY * vsize_inv), + used_scale * (1 + moveY * moveY * vsize_inv), + }; + Polygon circle; + for (Point vertex : branch_circle) + { + vertex = Point(matrix[0] * vertex.X + matrix[1] * vertex.Y, matrix[2] * vertex.X + matrix[3] * vertex.Y); + circle.add(center_position + vertex); } + poly.add(circle.offset(0)); + } - poly = poly.unionPolygons().offset(std::min(static_cast(FUDGE_LENGTH), config.support_line_width / 4)).difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); - // ^^^ There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. - // This fixes it, and for every other part, 0.05mm will not be noticed. - return poly; - }; + poly = poly.unionPolygons() + .offset(std::min(static_cast(FUDGE_LENGTH), config.support_line_width / 4)) + .difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); + // ^^^ There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly + // further away front the overhang (x/y wise) than optimal. + // This fixes it, and for every other part, 0.05mm will not be noticed. + return poly; + }; - constexpr auto three_quarters_sqd = 0.75 * 0.75; - const bool fast_relative_movement = max_speed_sqd > (radius * radius * three_quarters_sqd); + constexpr auto three_quarters_sqd = 0.75 * 0.75; + const bool fast_relative_movement = max_speed_sqd > (radius * radius * three_quarters_sqd); - // Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius. - linear_inserts[idx] = generateArea(0); + // Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius. + linear_inserts[idx] = generateArea(0); - if (fast_relative_movement || config.getRadius(*elem) - config.getCollisionRadius(*elem) > config.support_line_width) + if (fast_relative_movement || config.getRadius(*elem) - config.getCollisionRadius(*elem) > config.support_line_width) + { + // Simulate the path the nozzle will take on the outermost wall. + // If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air. + Polygons nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); + if (nozzle_path.splitIntoParts(false).size() > 1) { - // Simulate the path the nozzle will take on the outermost wall. - // If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air. - Polygons nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); + // Just try to make the area a tiny bit larger. + linear_inserts[idx] = generateArea(config.support_line_width / 2); + nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); + + // if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best if (nozzle_path.splitIntoParts(false).size() > 1) { - // Just try to make the area a tiny bit larger. - linear_inserts[idx] = generateArea(config.support_line_width / 2); - nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); - - // if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best - if (nozzle_path.splitIntoParts(false).size() > 1) + Polygons polygons_with_correct_center; + for (PolygonsPart part : nozzle_path.splitIntoParts(false)) { - Polygons polygons_with_correct_center; - for (PolygonsPart part : nozzle_path.splitIntoParts(false)) + if (part.inside(elem->result_on_layer, true)) { - if (part.inside(elem->result_on_layer, true)) + polygons_with_correct_center = polygons_with_correct_center.unionPolygons(part); + } + else + { + // Try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... + Point from = elem->result_on_layer; + PolygonUtils::moveInside(part, from, 0); + if (vSize2(elem->result_on_layer - from) < (FUDGE_LENGTH * FUDGE_LENGTH) / 4) { polygons_with_correct_center = polygons_with_correct_center.unionPolygons(part); } - else - { - // Try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... - Point from = elem->result_on_layer; - PolygonUtils::moveInside(part, from, 0); - if (vSize2(elem->result_on_layer - from) < (FUDGE_LENGTH * FUDGE_LENGTH) / 4) - { - polygons_with_correct_center = polygons_with_correct_center.unionPolygons(part); - } - } } - // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. - linear_inserts[idx] = polygons_with_correct_center.offset(config.support_line_width / 2).unionPolygons(); - linear_inserts[idx] = linear_inserts[idx].difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)).unionPolygons(); } + // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. + linear_inserts[idx] = polygons_with_correct_center.offset(config.support_line_width / 2).unionPolygons(); + linear_inserts[idx] + = linear_inserts[idx].difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)).unionPolygons(); } } + } - if (idx % progress_inserts_check_interval == 0) + if (idx % progress_inserts_check_interval == 0) + { { - { - std::lock_guard critical_section_progress(critical_sections); - progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps; - Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * progress_multiplier + progress_offset, TREE_PROGRESS_TOTAL); - } + std::lock_guard critical_section_progress(critical_sections); + progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps; + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * progress_multiplier + progress_offset, TREE_PROGRESS_TOTAL); } } - ); + }); // Single threaded combining all elements to the right layers. Only copies data! for (const coord_t i : ranges::views::iota(0UL, linear_data.size())) @@ -1731,8 +1805,7 @@ void TreeSupport::smoothBranchAreas(std::vector> processing; processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end()); std::vector>> update_next(processing.size()); // With this a lock can be avoided. - cura::parallel_for - ( + cura::parallel_for( 0, processing.size(), [&](const size_t processing_idx) @@ -1746,10 +1819,13 @@ void TreeSupport::smoothBranchAreas(std::vectorresult_on_layer - parent->result_on_layer) - (config.getRadius(*data_pair.first) - config.getRadius(*parent))); + max_outer_wall_distance = std::max( + max_outer_wall_distance, + vSize(data_pair.first->result_on_layer - parent->result_on_layer) - (config.getRadius(*data_pair.first) - config.getRadius(*parent))); } } - max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers. + max_outer_wall_distance + += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers. if (do_something) { Polygons max_allowed_area = data_pair.second.offset(max_outer_wall_distance); @@ -1757,12 +1833,12 @@ void TreeSupport::smoothBranchAreas(std::vector(parent, layer_tree_polygons[layer_idx + 1][parent].intersection(max_allowed_area))); + update_next[processing_idx].emplace_back( + std::pair(parent, layer_tree_polygons[layer_idx + 1][parent].intersection(max_allowed_area))); } } } - } - ); + }); for (std::vector> data_vector : update_next) { @@ -1784,10 +1860,11 @@ void TreeSupport::smoothBranchAreas(std::vector> processing; processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end()); - std::vector> update_next(processing.size(), std::pair(nullptr, Polygons())); // With this a lock can be avoided. + std::vector> update_next( + processing.size(), + std::pair(nullptr, Polygons())); // With this a lock can be avoided. - cura::parallel_for - ( + cura::parallel_for( 0, processing.size(), [&](const size_t processing_idx) @@ -1821,8 +1898,7 @@ void TreeSupport::smoothBranchAreas(std::vector(data_pair.first, result); } } - } - ); + }); updated_last_iteration.clear(); for (std::pair data_pair : update_next) @@ -1839,22 +1915,21 @@ void TreeSupport::smoothBranchAreas(std::vector>& layer_tree_polygons, const std::vector>& linear_data, std::vector>>& dropped_down_areas, - const std::map& inverse_tree_order -) + const std::map& inverse_tree_order) { - cura::parallel_for - ( + cura::parallel_for( 0, linear_data.size(), [&](const size_t idx) { TreeSupportElement* elem = linear_data[idx].second; - bool non_gracious_model_contact = ! elem->to_model_gracious && ! inverse_tree_order.count(elem); // If an element has no child, it connects to whatever is below as no support further down for it will exist. + bool non_gracious_model_contact + = ! elem->to_model_gracious + && ! inverse_tree_order.count(elem); // If an element has no child, it connects to whatever is below as no support further down for it will exist. if (non_gracious_model_contact) { Polygons rest_support = layer_tree_polygons[linear_data[idx].first][elem].intersection(volumes_.getAccumulatedPlaceable0(linear_data[idx].first)); @@ -1864,8 +1939,7 @@ void TreeSupport::dropNonGraciousAreas dropped_down_areas[idx].emplace_back(linear_data[idx].first - counter, rest_support); } } - } - ); + }); } @@ -1873,8 +1947,8 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora { const auto t_start = std::chrono::high_resolution_clock::now(); - const coord_t closing_dist=config.support_line_width*config.support_wall_count; - const coord_t open_close_distance = config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5; // based on calculation in WallToolPath + const coord_t closing_dist = config.support_line_width * config.support_wall_count; + const coord_t open_close_distance = config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5; // based on calculation in WallToolPath const double small_area_length = INT2MM(static_cast(config.support_line_width) / 2); std::function reversePolygon = [&](Polygons& poly) @@ -1886,107 +1960,103 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora }; - std::vector support_holes(support_layer_storage.size(),Polygons()); - //Extract all holes as polygon objects - cura::parallel_for - ( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { - - - support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + std::vector support_holes(support_layer_storage.size(), Polygons()); + // Extract all holes as polygon objects + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))) + .offset(-open_close_distance) + .offset(open_close_distance * 2) + .offset(-open_close_distance); + support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - std::vector parts = support_layer_storage[layer_idx].sortByNesting(); + std::vector parts = support_layer_storage[layer_idx].sortByNesting(); - if (parts.size() <= 1) - { - return; - } + if (parts.size() <= 1) + { + return; + } - Polygons holes_original; - for (const size_t idx : ranges::views::iota(1UL, parts.size())) - { - Polygons area = parts[idx]; - reversePolygon(area); - holes_original.add(area); - } - support_holes[layer_idx] = holes_original; + Polygons holes_original; + for (const size_t idx : ranges::views::iota(1UL, parts.size())) + { + Polygons area = parts[idx]; + reversePolygon(area); + holes_original.add(area); } - ); + support_holes[layer_idx] = holes_original; + }); const auto t_union = std::chrono::high_resolution_clock::now(); std::vector> holeparts(support_layer_storage.size()); - //Split all holes into parts - cura::parallel_for - ( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) + // Split all holes into parts + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + for (Polygons hole : support_holes[layer_idx].splitIntoParts()) { - for (Polygons hole:support_holes[layer_idx].splitIntoParts()) - { - holeparts[layer_idx].emplace_back(hole); - } + holeparts[layer_idx].emplace_back(hole); } - ); - std::vector>> hole_rest_map (holeparts.size()); - std::vector> holes_resting_outside (holeparts.size()); + }); + std::vector>> hole_rest_map(holeparts.size()); + std::vector> holes_resting_outside(holeparts.size()); - //Figure out which hole rests on which other hole - cura::parallel_for - ( - 1, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) + // Figure out which hole rests on which other hole + cura::parallel_for( + 1, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + if (holeparts[layer_idx].empty()) { - if (holeparts[layer_idx].empty()) - { - return; - } + return; + } - Polygons outer_walls = - TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(closing_dist,0);//.unionPolygons(volumes_.getCollision(0, layer_idx - 1, true).offset(-(config.support_line_width+config.xy_min_distance))); + Polygons outer_walls + = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()) + .tubeShape(closing_dist, 0); //.unionPolygons(volumes_.getCollision(0, layer_idx - 1, true).offset(-(config.support_line_width+config.xy_min_distance))); - Polygons holes_below; + Polygons holes_below; - for (auto poly: holeparts[layer_idx - 1]) + for (auto poly : holeparts[layer_idx - 1]) + { + holes_below.add(poly); + } + + for (auto [idx, hole] : holeparts[layer_idx] | ranges::views::enumerate) + { + AABB hole_aabb = AABB(hole); + hole_aabb.expand(EPSILON); + if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls, hole_aabb)).empty()) { - holes_below.add(poly); + holes_resting_outside[layer_idx].emplace(idx); } - - for (auto [idx, hole] : holeparts[layer_idx] | ranges::views::enumerate) + else { - AABB hole_aabb = AABB(hole); - hole_aabb.expand(EPSILON); - if (!hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls,hole_aabb)).empty()) - { - holes_resting_outside[layer_idx].emplace(idx); - } - else + for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) { - for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) + if (hole_aabb.hit(AABB(hole2)) + && ! hole.intersection(hole2).empty()) // TODO should technically be outline: Check if this is fine either way as it would save an offset { - - if (hole_aabb.hit(AABB(hole2)) && ! hole.intersection(hole2).empty() ) // TODO should technically be outline: Check if this is fine either way as it would save an offset - { - hole_rest_map[layer_idx][idx].emplace_back(idx2); - } + hole_rest_map[layer_idx][idx].emplace_back(idx2); } } } } - ); + }); const auto t_hole_rest_ordering = std::chrono::high_resolution_clock::now(); std::unordered_set removed_holes_by_idx; std::vector valid_holes(support_holes.size(), Polygons()); - //Check which holes have to be removed as they do not rest on anything. Only keep holes that have to be removed + // Check which holes have to be removed as they do not rest on anything. Only keep holes that have to be removed for (const size_t layer_idx : ranges::views::iota(1UL, support_holes.size())) { std::unordered_set next_removed_holes_by_idx; @@ -2000,7 +2070,8 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } else { - if(hole_rest_map[layer_idx].contains(idx)){ + if (hole_rest_map[layer_idx].contains(idx)) + { for (size_t resting_idx : hole_rest_map[layer_idx][idx]) { if (! removed_holes_by_idx.contains(resting_idx)) @@ -2025,24 +2096,22 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } const auto t_hole_removal_tagging = std::chrono::high_resolution_clock::now(); - //Check if holes are so close to each other that two lines will be printed directly next to each other, which is assumed stable (as otherwise the simulated support pattern will not work correctly) and remove all remaining, invalid holes - cura::parallel_for - ( - 1, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) + // Check if holes are so close to each other that two lines will be printed directly next to each other, which is assumed stable (as otherwise the simulated support pattern + // will not work correctly) and remove all remaining, invalid holes + cura::parallel_for( + 1, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + if (holeparts[layer_idx].empty()) { - if (holeparts[layer_idx].empty()) - { - return; - } - - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons(); - reversePolygon(valid_holes[layer_idx]); - support_layer_storage[layer_idx].add(valid_holes[layer_idx]); + return; } - ); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons(); + reversePolygon(valid_holes[layer_idx]); + support_layer_storage[layer_idx].add(valid_holes[layer_idx]); + }); const auto t_end = std::chrono::high_resolution_clock::now(); @@ -2052,19 +2121,24 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora const auto dur_hole_removal_tagging = 0.001 * std::chrono::duration_cast(t_hole_removal_tagging - t_hole_rest_ordering).count(); const auto dur_hole_removal = 0.001 * std::chrono::duration_cast(t_end - t_hole_removal_tagging).count(); - spdlog::debug("Time to union areas: {} ms Time to evaluate which hole rest on which other hole: {} ms Time to see which holes are not resting on anything valid: {} ms remove all holes that are invalid and not close enough to a valid hole: {} ms", dur_union,dur_hole_rest_ordering,dur_hole_removal_tagging, dur_hole_removal); - + spdlog::debug( + "Time to union areas: {} ms Time to evaluate which hole rest on which other hole: {} ms Time to see which holes are not resting on anything valid: {} ms remove all holes " + "that are invalid and not close enough to a valid hole: {} ms", + dur_union, + dur_hole_rest_ordering, + dur_hole_removal_tagging, + dur_hole_removal); } void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_roof_storage, SliceDataStorage& storage) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; - double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; + double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; // Iterate over the generated circles in parallel and clean them up. Also add support floor. std::mutex critical_sections; - cura::parallel_for - ( + cura::parallel_for( 0, support_layer_storage.size(), [&](const LayerIndex layer_idx) @@ -2073,43 +2147,53 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor // Subtract support lines of the branches from the roof storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(support_roof_storage[layer_idx]); - if (!storage.support.supportLayers[layer_idx].support_roof.empty() && support_layer_storage[layer_idx].intersection(storage.support.supportLayers[layer_idx].support_roof).area() > 1) + if (! storage.support.supportLayers[layer_idx].support_roof.empty() + && support_layer_storage[layer_idx].intersection(storage.support.supportLayers[layer_idx].support_roof).area() > 1) { switch (interface_pref) { - case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); - break; - - case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]); - break; - - case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: - { - Polygons interface_lines = - TreeSupportUtils::generateSupportInfillLines(storage.support.supportLayers[layer_idx].support_roof, config, true, layer_idx, config.support_roof_line_distance, storage.support.cross_fill_provider, true) - .offsetPolyLine(config.support_roof_line_width / 2); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); - } + case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); break; - case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: - { - Polygons tree_lines; - tree_lines = - tree_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(support_layer_storage[layer_idx], config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, true) - .offsetPolyLine(config.support_line_width / 2) - ); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(tree_lines); - // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. - } + case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]); break; - case InterfacePreference::NOTHING: - break; + case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: + { + Polygons interface_lines = TreeSupportUtils::generateSupportInfillLines( + storage.support.supportLayers[layer_idx].support_roof, + config, + true, + layer_idx, + config.support_roof_line_distance, + storage.support.cross_fill_provider, + true) + .offsetPolyLine(config.support_roof_line_width / 2); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); + } + break; + + case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: + { + Polygons tree_lines; + tree_lines = tree_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( + support_layer_storage[layer_idx], + config, + false, + layer_idx, + config.support_line_distance, + storage.support.cross_fill_provider, + true) + .offsetPolyLine(config.support_line_width / 2)); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(tree_lines); + // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. + } + break; + + case InterfacePreference::NOTHING: + break; } } @@ -2121,8 +2205,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor size_t layers_below = 0; while (layers_below <= config.support_bottom_layers) { - // One sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each other. - const size_t sample_layer = static_cast(std::max(0, (static_cast(layer_idx) - static_cast(layers_below)) - static_cast(config.z_distance_bottom_layers))); + // One sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each + // other. + const size_t sample_layer + = static_cast(std::max(0, (static_cast(layer_idx) - static_cast(layers_below)) - static_cast(config.z_distance_bottom_layers))); constexpr bool no_support = false; constexpr bool no_prime_tower = false; floor_layer.add(layer_outset.intersection(storage.getLayerOutlines(sample_layer, no_support, no_prime_tower))); @@ -2153,32 +2239,31 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor { std::lock_guard critical_section_storage(critical_sections); - if (!storage.support.supportLayers[layer_idx].support_infill_parts.empty() || !storage.support.supportLayers[layer_idx].support_roof.empty()) + if (! storage.support.supportLayers[layer_idx].support_infill_parts.empty() || ! storage.support.supportLayers[layer_idx].support_roof.empty()) { storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast(layer_idx)); } } - } - ); + }); } void TreeSupport::drawAreas(std::vector>& move_bounds, SliceDataStorage& storage) { std::vector support_layer_storage(move_bounds.size()); std::vector support_roof_storage(move_bounds.size()); - std::map inverse_tree_order; // In the tree structure only the parents can be accessed. Inverse this to be able to access the children. - std::vector> linear_data; // All SupportElements are put into a layer independent storage to improve parallelization. Was added at a point in time where this function had performance issues. - // These were fixed by creating less initial points, but i do not see a good reason to remove a working performance optimization. + std::map + inverse_tree_order; // In the tree structure only the parents can be accessed. Inverse this to be able to access the children. + std::vector> + linear_data; // All SupportElements are put into a layer independent storage to improve parallelization. Was added at a point in time where this function had performance + // issues. These were fixed by creating less initial points, but i do not see a good reason to remove a working performance optimization. for (const auto layer_idx : ranges::views::iota(0UL, move_bounds.size())) { for (TreeSupportElement* elem : move_bounds[layer_idx]) { // (Check if) We either come from nowhere at the final layer or we had invalid parents 2. should never happen but just to be sure: - if - ( - (layer_idx > 0 && ((!inverse_tree_order.count(elem) && elem->target_height == layer_idx && config.min_dtt_to_model > 0 && !elem->to_buildplate) || - (inverse_tree_order.count(elem) && inverse_tree_order[elem]->result_on_layer == Point(-1, -1)))) - ) + if ((layer_idx > 0 + && ((! inverse_tree_order.count(elem) && elem->target_height == layer_idx && config.min_dtt_to_model > 0 && ! elem->to_buildplate) + || (inverse_tree_order.count(elem) && inverse_tree_order[elem]->result_on_layer == Point(-1, -1))))) { continue; } @@ -2204,7 +2289,8 @@ void TreeSupport::drawAreas(std::vector>& move_bou generateBranchAreas(linear_data, layer_tree_polygons, inverse_tree_order); const auto t_generate = std::chrono::high_resolution_clock::now(); - // In some edge-cases a branch may go through a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out. + // In some edge-cases a branch may go through a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be + // caught and smoothed out. smoothBranchAreas(layer_tree_polygons); const auto t_smooth = std::chrono::high_resolution_clock::now(); @@ -2223,19 +2309,15 @@ void TreeSupport::drawAreas(std::vector>& move_bou } // ensure all branch areas added as roof actually cause a roofline to generate. Else disable turning the branch to roof going down - cura::parallel_for - ( + cura::parallel_for( 0, layer_tree_polygons.size(), [&](const size_t layer_idx) { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - if - ( - data_pair.first->missing_roof_layers > data_pair.first->distance_to_top && - TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, true).empty() - ) + if (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top + && TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, true).empty()) { std::vector to_disable_roofs; to_disable_roofs.emplace_back(data_pair.first); @@ -2254,8 +2336,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } } - } - ); + }); // Single threaded combining all support areas to the right layers. // Only copies data! @@ -2263,15 +2344,13 @@ void TreeSupport::drawAreas(std::vector>& move_bou { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - ( - (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top) ? support_roof_storage : support_layer_storage - )[layer_idx].add(data_pair.second); + ((data_pair.first->missing_roof_layers > data_pair.first->distance_to_top) ? support_roof_storage : support_layer_storage)[layer_idx].add(data_pair.second); } } for (const auto layer_idx : ranges::views::iota(0UL, additional_required_support_area.size())) { - if(support_layer_storage.size() > layer_idx) + if (support_layer_storage.size() > layer_idx) { support_layer_storage[layer_idx].add(additional_required_support_area[layer_idx]); } @@ -2289,7 +2368,14 @@ void TreeSupport::drawAreas(std::vector>& move_bou const auto dur_drop = 0.001 * std::chrono::duration_cast(t_drop - t_smooth).count(); const auto dur_filter = 0.001 * std::chrono::duration_cast(t_filter - t_drop).count(); const auto dur_finalize = 0.001 * std::chrono::duration_cast(t_end - t_filter).count(); - spdlog::info("Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms filterFloatingLines: {} ms finalizeInterfaceAndSupportAreas {} ms", dur_gen_tips, dur_smooth, dur_drop, dur_filter, dur_finalize); + spdlog::info( + "Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms filterFloatingLines: {} ms " + "finalizeInterfaceAndSupportAreas {} ms", + dur_gen_tips, + dur_smooth, + dur_drop, + dur_filter, + dur_finalize); } } // namespace cura diff --git a/src/bridge.cpp b/src/bridge.cpp index a395f7bd79..06ddbb6080 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -1,22 +1,30 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "bridge.h" -#include "sliceDataStorage.h" + #include "settings/types/Ratio.h" +#include "sliceDataStorage.h" #include "utils/AABB.h" #include "utils/polygon.h" namespace cura { -int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const SliceDataStorage& storage, const unsigned layer_nr, const unsigned bridge_layer, const SupportLayer* support_layer, Polygons& supported_regions) +int bridgeAngle( + const Settings& settings, + const Polygons& skin_outline, + const SliceDataStorage& storage, + const unsigned layer_nr, + const unsigned bridge_layer, + const SupportLayer* support_layer, + Polygons& supported_regions) { assert(! skin_outline.empty()); AABB boundary_box(skin_outline); - //To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer. - // This gives us the islands that the layer rests on. + // To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer. + // This gives us the islands that the layer rests on. Polygons islands; Polygons prev_layer_outline; // we also want the complete outline of the previous layer @@ -42,7 +50,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl } prev_layer_outline.add(solid_below); // not intersected with skin - if (!boundary_box.hit(prev_layer_part.boundaryBox)) + if (! boundary_box.hit(prev_layer_part.boundaryBox)) continue; islands.add(skin_outline.intersection(solid_below)); @@ -59,7 +67,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl // the model on one side but the remainder of the skin is above support would look like // a bridge because it would have two islands) - FIXME more work required here? - if (!support_layer->support_roof.empty()) + if (! support_layer->support_roof.empty()) { AABB support_roof_bb(support_layer->support_roof); if (boundary_box.hit(support_roof_bb)) @@ -67,7 +75,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl prev_layer_outline.add(support_layer->support_roof); // not intersected with skin Polygons supported_skin(skin_outline.intersection(support_layer->support_roof)); - if (!supported_skin.empty()) + if (! supported_skin.empty()) { supported_regions.add(supported_skin); } @@ -83,7 +91,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl prev_layer_outline.add(support_part.getInfillArea()); // not intersected with skin Polygons supported_skin(skin_outline.intersection(support_part.getInfillArea())); - if (!supported_skin.empty()) + if (! supported_skin.empty()) { supported_regions.add(supported_skin); } @@ -115,7 +123,8 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl Polygons skin_perimeter_lines; for (ConstPolygonRef poly : skin_outline) { - if (poly.empty()) continue; + if (poly.empty()) + continue; skin_perimeter_lines.add(poly); skin_perimeter_lines.back().emplace_back(poly.front()); } @@ -157,15 +166,15 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl return -1; } - //Next find the 2 largest islands that we rest on. + // Next find the 2 largest islands that we rest on. double area1 = 0; double area2 = 0; int idx1 = -1; int idx2 = -1; - for(unsigned int n=0; n area1) @@ -184,15 +193,14 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl idx2 = n; } } - + if (idx1 < 0 || idx2 < 0) return -1; - + Point center1 = islands[idx1].centerOfMass(); Point center2 = islands[idx2].centerOfMass(); return angle(center2 - center1); } -}//namespace cura - +} // namespace cura From b3fb6626654d2b384fd1b425a6d2cc5e7e348c56 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 15 Sep 2023 12:06:19 +0200 Subject: [PATCH 520/656] Plugins specify a verion-range, slots just a version. Not the other way around as we have now. This also adheres closer to the way we handle front-end plugins. Ideally you'd maybe want both of them to be ranges, but that's for the future. part of CURA-11035 Co-authored-by: Jelle Spijker --- conanfile.py | 2 +- include/plugins/exception.h | 6 +++--- include/plugins/metadata.h | 4 ++-- include/plugins/pluginproxy.h | 8 ++++---- include/plugins/slotproxy.h | 14 +++++++------- include/plugins/slots.h | 10 +++++----- include/plugins/validator.h | 4 ++-- src/plugins/converters.cpp | 4 ++-- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/conanfile.py b/conanfile.py index 7d4d5dc601..69154f284d 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/cura_11035") # FIXME!: Put back to .../testing after merge! self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") diff --git a/include/plugins/exception.h b/include/plugins/exception.h index a1816c7754..f593c2d69b 100644 --- a/include/plugins/exception.h +++ b/include/plugins/exception.h @@ -28,13 +28,13 @@ class ValidatorException : public std::exception ValidatorException(const auto& validator, const slot_metadata& slot_info, const plugin_metadata& plugin_info) noexcept : msg_(fmt::format( - "Failed to validate plugin '{}-{}' running at [{}] for slot '{}', slot range '{}' incompatible with plugin slot version '{}'", + "Failed to validate plugin '{}-{}' running at [{}] for slot '{}', version '{}' incompatible with plugin specified slot-version-range '{}'.", plugin_info.plugin_name, plugin_info.plugin_version, plugin_info.peer, slot_info.slot_id, - slot_info.version_range, - plugin_info.slot_version)) + slot_info.version, + plugin_info.slot_version_range)) { } diff --git a/include/plugins/metadata.h b/include/plugins/metadata.h index fadc807d77..af3e210342 100644 --- a/include/plugins/metadata.h +++ b/include/plugins/metadata.h @@ -18,7 +18,7 @@ namespace cura::plugins struct plugin_metadata { - std::string slot_version; + std::string slot_version_range; std::string plugin_name; std::string plugin_version; std::string peer; @@ -28,7 +28,7 @@ struct plugin_metadata struct slot_metadata { plugins::v0::SlotID slot_id; - std::string_view version_range; + std::string_view version; std::string_view engine_uuid; }; diff --git a/include/plugins/pluginproxy.h b/include/plugins/pluginproxy.h index 102e76833f..2ff7b5c0c3 100644 --- a/include/plugins/pluginproxy.h +++ b/include/plugins/pluginproxy.h @@ -50,7 +50,7 @@ namespace cura::plugins * * Template arguments are: * SlotID - plugin slot ID - * SlotVersionRng - plugin version range + * SlotVersion - slot version used -- will be the only version available in the engine for that slot, since there is just the latest version of the slot a.t.m. * Stub - process stub type * ValidatorTp - validator type * RequestTp - gRPC convertible request type, or dummy -- if stub is a proper invoke-stub, this is enforced by the specialization of the invoke component @@ -58,7 +58,7 @@ namespace cura::plugins * * Class provides methods for validating the plugin, making requests and processing responses. */ -template +template class PluginProxy { // type aliases for easy use @@ -131,7 +131,7 @@ class PluginProxy spdlog::error(status.error_message()); throw exceptions::RemoteException(slot_info_, status.error_message()); } - if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version.empty()) + if (! plugin_info.plugin_name.empty() && ! plugin_info.slot_version_range.empty()) { plugin_info_.emplace(plugin_info); } @@ -340,7 +340,7 @@ class PluginProxy req_converter_type req_{}; ///< The Invoke request converter object. rsp_converter_type rsp_{}; ///< The Invoke response converter object. slot_metadata slot_info_{ .slot_id = SlotID, - .version_range = SlotVersionRng.value, + .version = SlotVersion.value, .engine_uuid = Application::getInstance().instance_uuid }; ///< Holds information about the plugin slot. std::optional plugin_info_{ std::optional(std::nullopt) }; ///< Optional object that holds the plugin metadata, set after handshake }; diff --git a/include/plugins/slotproxy.h b/include/plugins/slotproxy.h index 84ba7846c3..037db10a19 100644 --- a/include/plugins/slotproxy.h +++ b/include/plugins/slotproxy.h @@ -28,19 +28,19 @@ namespace cura::plugins * for communication with plugins assigned to the slot. It delegates plugin requests to the * corresponding PluginProxy object and provides a default behavior when no plugin is available. * - * @tparam Slot The plugin slot ID. - * @tparam Validator The type used for validating the plugin. + * @tparam SlotID The plugin slot ID. + * @tparam SlotVersion The version of the indicated slot. * @tparam Stub The process stub type. - * @tparam Prepare The prepare type. - * @tparam Request The gRPC convertible request type. - * @tparam Response The gRPC convertible response type. + * @tparam ValidatorTp The type used for validating the plugin. + * @tparam RequestTp The gRPC convertible request type. + * @tparam ResponseTp The gRPC convertible response type. * @tparam Default The default behavior when no plugin is available. */ -template +template class SlotProxy { Default default_process{}; - using value_type = PluginProxy; + using value_type = PluginProxy; std::optional plugin_{ std::nullopt }; public: diff --git a/include/plugins/slots.h b/include/plugins/slots.h index 5b99ab52b0..ed7c91a4e6 100644 --- a/include/plugins/slots.h +++ b/include/plugins/slots.h @@ -69,12 +69,12 @@ struct infill_generate_default */ template using slot_simplify_ - = SlotProxy; + = SlotProxy; template using slot_infill_generate_ = SlotProxy< v0::SlotID::INFILL_GENERATE, - "<=1.0.0", + "0.1.0-alpha", slots::infill::v0::generate::InfillGenerateService::Stub, Validator, infill_generate_request, @@ -91,7 +91,7 @@ using slot_infill_generate_ = SlotProxy< template using slot_postprocess_ = SlotProxy< v0::SlotID::POSTPROCESS_MODIFY, - "<=1.0.0", + "0.1.0-alpha", slots::postprocess::v0::modify::PostprocessModifyService::Stub, Validator, postprocess_request, @@ -100,12 +100,12 @@ using slot_postprocess_ = SlotProxy< template using slot_settings_broadcast_ - = SlotProxy; + = SlotProxy; template using slot_gcode_paths_modify_ = SlotProxy< v0::SlotID::GCODE_PATHS_MODIFY, - "<=1.0.0", + "0.1.0-alpha", slots::gcode_paths::v0::modify::GCodePathsModifyService::Stub, Validator, gcode_paths_modify_request, diff --git a/include/plugins/validator.h b/include/plugins/validator.h index 0d18c074a3..ffe9cbba8e 100644 --- a/include/plugins/validator.h +++ b/include/plugins/validator.h @@ -33,8 +33,8 @@ class Validator */ Validator(const slot_metadata& slot_info, const plugin_metadata& plugin_info) { - auto slot_range = semver::range::detail::range(slot_info.version_range); - auto slot_version = semver::from_string(plugin_info.slot_version); + auto slot_range = semver::range::detail::range(plugin_info.slot_version_range); + auto slot_version = semver::from_string(slot_info.version); valid_ = slot_range.satisfies(slot_version, include_prerelease_); }; diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 33e7d4882e..2ba03fe8c6 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -70,7 +70,7 @@ handshake_request::value_type handshake_request::operator()(const std::string& n { value_type message{}; message.set_slot_id(slot_info.slot_id); - message.set_version_range(slot_info.version_range.data()); + message.set_version(slot_info.version.data()); message.set_plugin_name(name); message.set_plugin_version(version); return message; @@ -78,7 +78,7 @@ handshake_request::value_type handshake_request::operator()(const std::string& n handshake_response::native_value_type handshake_response::operator()(const handshake_response::value_type& message, std::string_view peer) const { - return { .slot_version = message.slot_version(), + return { .slot_version_range = message.slot_version_range(), .plugin_name = message.plugin_name(), .plugin_version = message.plugin_version(), .peer = std::string{ peer }, From 12e9152dae56f7658319e4a144f19781d4bb7913 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 18 Sep 2023 16:45:05 +0200 Subject: [PATCH 521/656] Apply nozzle temperature when set in gcode header CURA-8889 --- src/gcodeExport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index a9bf55f7c1..caff200811 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -763,7 +763,7 @@ void GCodeExport::processInitialLayerTemperature(const SliceDataStorage& storage processInitialLayerBedTemperature(); - if (scene.current_mesh_group->settings.get("material_print_temp_prepend")) + if (scene.current_mesh_group->settings.get("material_print_temp_prepend") || (scene.current_mesh_group != scene.mesh_groups.begin())) { for (unsigned extruder_nr = 0; extruder_nr < num_extruders; extruder_nr++) { From 74456d083079cc4025c7d7d68507d6a0152e97f6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 19 Sep 2023 12:36:39 +0200 Subject: [PATCH 522/656] Don't use auto when the type is not 'near'. Done as part of CURA-11019 --- src/FffGcodeWriter.cpp | 16 ++++++++-------- src/FffPolygonGenerator.cpp | 10 +++++----- src/LayerPlan.cpp | 4 ++-- src/TreeSupport.cpp | 4 ++-- src/bridge.cpp | 2 +- src/settings/PathConfigStorage.cpp | 2 +- src/sliceDataStorage.cpp | 8 ++++---- src/support.cpp | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 0d6ea404fd..d5cbd83648 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -106,7 +106,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep } size_t total_layers = 0; - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; size_t mesh_layer_num = mesh.layers.size(); @@ -270,7 +270,7 @@ void FffGcodeWriter::findLayerSeamsForSpiralize(SliceDataStorage& storage, size_ const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (unsigned int mesh_idx : mesh_order) { - auto& mesh = *storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; // if this mesh has layer data for this layer process it if (! done_this_layer && mesh.layers.size() > layer_nr) { @@ -372,7 +372,7 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for (auto& mesh : storage.meshes) + for (std::shared_ptr& mesh : storage.meshes) { retractionAndWipeConfigFromSettings(mesh->settings, &mesh->retraction_wipe_config); } @@ -916,7 +916,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn { z = storage.meshes[0]->layers[layer_nr].printZ; // stub default // find printZ of first actual printed mesh - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") @@ -953,7 +953,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } coord_t max_inner_wall_width = 0; - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; coord_t mesh_inner_wall_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); @@ -1025,7 +1025,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (size_t mesh_idx : mesh_order) { - const auto& mesh = storage.meshes[mesh_idx]; + const std::shared_ptr& mesh = storage.meshes[mesh_idx]; const MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; if (mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE && extruder_nr @@ -1385,7 +1385,7 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s std::vector::iterator mesh_group = Application::getInstance().current_slice->scene.current_mesh_group; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const auto& mesh = *storage.meshes[mesh_idx]; + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.getExtruderIsUsed(extruder_nr)) { const Mesh& mesh_data = mesh_group->meshes[mesh_idx]; @@ -2263,7 +2263,7 @@ bool FffGcodeWriter::processInsets( Polygons outlines_below; AABB boundaryBox(part.outline); - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& m = *mesh_ptr; if (m.isPrinted()) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index edd5e48d6e..daff0196eb 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -276,7 +276,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe // always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes storage.meshes.push_back(std::make_shared(&meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh - auto& meshStorage = *storage.meshes.back(); + SliceMeshStorage& meshStorage = *storage.meshes.back(); // only create layer parts for normal meshes const bool is_support_modifier = AreaSupport::handleSupportModifierMesh(storage, mesh.settings, slicer); @@ -347,7 +347,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // compute layer count and remove first empty layers // there is no separate progress stage for removeEmptyFisrtLayer (TODO) unsigned int slice_layer_count = 0; - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (! mesh.settings.get("infill_mesh") && ! mesh.settings.get("anti_overhang_mesh")) @@ -433,7 +433,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& spdlog::debug("Meshes post-processing"); // meshes post processing - for (auto& mesh : storage.meshes) + for (std::shared_ptr& mesh : storage.meshes) { processDerivedWallsSkinInfill(*mesh); } @@ -742,7 +742,7 @@ bool FffPolygonGenerator::isEmptyLayer(SliceDataStorage& storage, const LayerInd return false; } } - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (layer_idx >= mesh.layers.size()) @@ -855,7 +855,7 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage max_print_height_per_extruder.resize(extruder_count, -(raft_layers + 1)); // Initialize all as -1 (or lower in case of raft). { // compute max_object_height_per_extruder // Height of the meshes themselves. - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh")) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index b7d24b1b11..530ba6b63b 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -159,7 +159,7 @@ Polygons LayerPlan::computeCombBoundary(const CombBoundary boundary_type) } else { - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; const SliceLayer& layer = mesh.layers[static_cast(layer_nr)]; @@ -1188,7 +1188,7 @@ void LayerPlan::addLinesByOptimizer( if (layer_nr >= 0) { // determine how much the skin/infill lines overlap the combing boundary - for (const auto& mesh : storage.meshes) + for (const std::shared_ptr& mesh : storage.meshes) { const coord_t overlap = std::max(mesh->settings.get("skin_overlap_mm"), mesh->settings.get("infill_overlap_mm")); if (overlap > dist) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 9cc4c7d404..c226588ebe 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -39,7 +39,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) { size_t largest_printed_mesh_idx = 0; - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; TreeSupportSettings::some_model_contains_thick_roof |= mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); @@ -52,7 +52,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) // Only one setting object is needed per group, as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes. for (auto [mesh_idx, mesh_ptr] : storage.meshes | ranges::views::enumerate) { - auto& mesh = *mesh_ptr; + SliceMeshStorage& mesh = *mesh_ptr; const bool non_supportable_mesh = mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh"); if (mesh.settings.get("support_structure") != ESupportStructure::TREE || ! mesh.settings.get("support_enable") || non_supportable_mesh) { diff --git a/src/bridge.cpp b/src/bridge.cpp index 06ddbb6080..75e42889e2 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -32,7 +32,7 @@ int bridgeAngle( const Ratio sparse_infill_max_density = settings.get("bridge_sparse_infill_max_density"); // include parts from all meshes - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; if (mesh.isPrinted()) diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 467b130f5c..f6e6d1b703 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -124,7 +124,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } mesh_configs.reserve(storage.meshes.size()); - for (const auto& mesh_storage : storage.meshes) + for (const std::shared_ptr& mesh_storage : storage.meshes) { mesh_configs.emplace_back(*mesh_storage, layer_thickness, layer_nr, line_width_factor_per_extruder); } diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index c890783f8c..4533fbb8be 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -300,7 +300,7 @@ Polygons Polygons total; if (layer_nr >= 0) { - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { if (mesh->settings.get("infill_mesh") || mesh->settings.get("anti_overhang_mesh") || (extruder_nr != -1 && extruder_nr != int(mesh->settings.get("wall_0_extruder_nr").extruder_nr))) @@ -381,7 +381,7 @@ std::vector SliceDataStorage::getExtrudersUsed() const // support // support is presupposed to be present... - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { if (mesh->settings.get("support_enable") || mesh->settings.get("support_mesh")) { @@ -399,7 +399,7 @@ std::vector SliceDataStorage::getExtrudersUsed() const } // all meshes are presupposed to actually have content - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { @@ -508,7 +508,7 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) if (include_models) { - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { diff --git a/src/support.cpp b/src/support.cpp index a55d21b53f..188f2b486d 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -599,7 +599,7 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp void AreaSupport::generateOverhangAreas(SliceDataStorage& storage) { - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) From 7f01c3a075d280907b08c5e6b73daf6ea9e107bd Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 19 Sep 2023 14:36:32 +0200 Subject: [PATCH 523/656] Fix min layer time for gradual flow Change ordering of back pressure compensation, gradual flow and minimum layer time gcode modification steps Ordering before commit: 1: back pressure compensation 2: minimum layer time 3: gradual flow Ordering after commit: 1: gradual flow 2: back pressure compensation 3: minimum layer time CURA-11020 --- include/LayerPlan.h | 5 +++ src/FffGcodeWriter.cpp | 1 + src/LayerPlan.cpp | 85 ++++++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 3eda752178..e9e7bde3f1 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -704,6 +704,11 @@ class LayerPlan : public NoCopy */ void moveInsideCombBoundary(const coord_t distance, const std::optional& part = std::nullopt); + /*! + * If enabled, apply the modify plugin to the layer-plan. + */ + void applyModifyPlugin(); + /*! * Apply back-pressure compensation to this layer-plan. * Since the total (filament) pressure in a feeder-system is not only dependent on the pressure that exists between the nozzle and the diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 648e9082ce..c66e6a7659 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1046,6 +1046,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } } + gcode_layer.applyModifyPlugin(); gcode_layer.applyBackPressureCompensation(); return gcode_layer; } diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 600869daac..639a47f76a 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1852,46 +1852,6 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx]; - scripta::log( - "extruder_plan_0", - extruder_plan.paths, - SectionType::NA, - layer_nr, - scripta::CellVDI{ "flow", &GCodePath::flow }, - scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, - scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, - scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, - scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, - scripta::CellVDI{ "retract", &GCodePath::retract }, - scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, - scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, - scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, - scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, - scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, - scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); - - extruder_plan.paths = slots::instance().modify(extruder_plan.paths, extruder_plan.extruder_nr, layer_nr); - - // Since the time/material estimates _may_ have changed during the plugin modify step we recalculate it - extruder_plan.computeNaiveTimeEstimates(gcode.getPositionXY()); - scripta::log( - "extruder_plan_1", - extruder_plan.paths, - SectionType::NA, - layer_nr, - scripta::CellVDI{ "flow", &GCodePath::flow }, - scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, - scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, - scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, - scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, - scripta::CellVDI{ "retract", &GCodePath::retract }, - scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, - scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, - scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, - scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, - scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, - scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); - const RetractionAndWipeConfig* retraction_config = current_mesh ? ¤t_mesh->retraction_wipe_config : &storage.retraction_wipe_config_per_extruder[extruder_plan.extruder_nr]; coord_t z_hop_height = retraction_config->retraction_config.zHop; @@ -2402,6 +2362,51 @@ bool LayerPlan::writePathWithCoasting( return true; } +void LayerPlan::applyModifyPlugin() +{ + for (auto& extruder_plan : extruder_plans) + { + scripta::log( + "extruder_plan_0", + extruder_plan.paths, + SectionType::NA, + layer_nr, + scripta::CellVDI{ "flow", &GCodePath::flow }, + scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, + scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, + scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, + scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, + scripta::CellVDI{ "retract", &GCodePath::retract }, + scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, + scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, + scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, + scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, + scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); + + extruder_plan.paths = slots::instance().modify(extruder_plan.paths, extruder_plan.extruder_nr, layer_nr); + + scripta::log( + "extruder_plan_1", + extruder_plan.paths, + SectionType::NA, + layer_nr, + scripta::CellVDI{ "flow", &GCodePath::flow }, + scripta::CellVDI{ "width_factor", &GCodePath::width_factor }, + scripta::CellVDI{ "spiralize", &GCodePath::spiralize }, + scripta::CellVDI{ "speed_factor", &GCodePath::speed_factor }, + scripta::CellVDI{ "speed_back_pressure_factor", &GCodePath::speed_back_pressure_factor }, + scripta::CellVDI{ "retract", &GCodePath::retract }, + scripta::CellVDI{ "unretract_before_last_travel_move", &GCodePath::unretract_before_last_travel_move }, + scripta::CellVDI{ "perform_z_hop", &GCodePath::perform_z_hop }, + scripta::CellVDI{ "perform_prime", &GCodePath::perform_prime }, + scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, + scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, + scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); + + } +} + void LayerPlan::applyBackPressureCompensation() { for (auto& extruder_plan : extruder_plans) From 8157ad44bf2ef62a970fe1b212a6a9ee3acca01f Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 19 Sep 2023 12:37:17 +0000 Subject: [PATCH 524/656] Applied clang-format. --- src/LayerPlan.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 639a47f76a..b2ff1d6273 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2403,7 +2403,6 @@ void LayerPlan::applyModifyPlugin() scripta::CellVDI{ "fan_speed", &GCodePath::getFanSpeed }, scripta::CellVDI{ "is_travel_path", &GCodePath::isTravelPath }, scripta::CellVDI{ "extrusion_mm3_per_mm", &GCodePath::getExtrusionMM3perMM }); - } } From cf0c484079e10b5cbe3b6fe6531a365c3dbe2ed9 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 20 Sep 2023 07:34:45 +0200 Subject: [PATCH 525/656] Use latest grpc definitions CURA-11035 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 69154f284d..7d4d5dc601 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/cura_11035") # FIXME!: Put back to .../testing after merge! + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From d5a099f84bc500b3e37676de60d201ffe1e44d14 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 20 Sep 2023 13:47:09 +0200 Subject: [PATCH 526/656] Start 5.5.x release branch. --- conanfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conanfile.py b/conanfile.py index 7d4d5dc601..381b6e2366 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-alpha" + self.version = "5.5.0-beta.1" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) @@ -82,10 +82,10 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/5.3.0") + self.requires("arcus/(latest)@ultimaker/stable") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/stable") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 3a849553c4952e6f8f959729fe90d285a7ab8d23 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 20 Sep 2023 14:58:40 +0200 Subject: [PATCH 527/656] use correct versions --- conanfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 381b6e2366..dea1860300 100644 --- a/conanfile.py +++ b/conanfile.py @@ -82,10 +82,10 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/stable") + self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/stable") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From b621644d96d4334f246c0475ef65cedf499fdf74 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 21 Sep 2023 15:38:05 +0200 Subject: [PATCH 528/656] Proof-of-concept 'true' support z-offset: half-layer heights. First see if the part of the support (interface only for now) that should (perhaps) be a bit lower if the support z gap is not a multiple of the layer height, can actually be lowered at all without upending the code too much. This turns out to be possible. Next is actually setting the proper gap height (or what's left of the gap after already dropping down to the layer that needs to be a fractional height.) Proof of concept mostly in the sense that the quality of the code added may not reflect our standards, and would also be missing functionality (for instance; what if we print without support-interface). part of CURA-11041, which is the proof-of-concept for CURA-10407 --- include/GCodePathConfig.h | 1 + include/LayerPlan.h | 19 ++-- include/pathPlanning/GCodePath.h | 1 + include/sliceDataStorage.h | 2 + src/FffGcodeWriter.cpp | 169 ++++++++++++++++--------------- src/LayerPlan.cpp | 41 ++++---- src/support.cpp | 3 +- 7 files changed, 130 insertions(+), 106 deletions(-) diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 1ee2003195..1f0da38d60 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -18,6 +18,7 @@ namespace cura */ struct GCodePathConfig { + coord_t z_offset{}; PrintFeatureType type{}; //!< name of the feature type coord_t line_width{}; //!< width of the line extruded coord_t layer_thickness{}; //!< current layer height in micron diff --git a/include/LayerPlan.h b/include/LayerPlan.h index bd30920a04..24fc141b82 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -117,10 +117,11 @@ class LayerPlan : public NoCopy */ GCodePath* getLatestPathWithConfig( const GCodePathConfig& config, - SpaceFillType space_fill_type, + const coord_t z_offset, + const SpaceFillType space_fill_type, const Ratio flow = 1.0_r, const Ratio width_factor = 1.0_r, - bool spiralize = false, + const bool spiralize = false, const Ratio speed_factor = 1.0_r); public: @@ -281,7 +282,7 @@ class LayerPlan : public NoCopy * \param p The point to travel to. * \param force_retract Whether to force a retraction to occur. */ - GCodePath& addTravel(const Point p, const bool force_retract = false); + GCodePath& addTravel(const Point p, const bool force_retract = false, const coord_t z_offset = 0); /*! * Add a travel path to a certain point and retract if needed. @@ -291,7 +292,7 @@ class LayerPlan : public NoCopy * \param p The point to travel to * \param path (optional) The travel path to which to add the point \p p */ - GCodePath& addTravel_simple(Point p, GCodePath* path = nullptr); + GCodePath& addTravel_simple(const Point p, GCodePath* path = nullptr); /*! * Plan a prime blob at the current location. @@ -317,14 +318,14 @@ class LayerPlan : public NoCopy * \param fan_speed Fan speed override for this path. */ void addExtrusionMove( - Point p, + const Point p, const GCodePathConfig& config, - SpaceFillType space_fill_type, + const SpaceFillType space_fill_type, const Ratio& flow = 1.0_r, const Ratio width_factor = 1.0_r, - bool spiralize = false, - Ratio speed_factor = 1.0_r, - double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); + const bool spiralize = false, + const Ratio speed_factor = 1.0_r, + const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT); /*! * Add polygon to the gcode starting at vertex \p startIdx diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 7bbd56c709..ef59a8747d 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -29,6 +29,7 @@ namespace cura */ struct GCodePath { + coord_t z_offset{}; GCodePathConfig config{}; //!< The configuration settings of the path. std::shared_ptr mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; SpaceFillType space_fill_type{}; //!< The type of space filling of which this path is a part diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 9b0661ed36..067a0a60a3 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -212,6 +212,8 @@ class SupportLayer std::vector support_infill_parts; //!< a list of support infill parts Polygons support_bottom; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof. Polygons support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. + Polygons support_fractional_roof_top; //!< If the support distance is less than a multiple of the layer height, + // the first part of support just underneath the model needs to be printed at a fracional layer height. Polygons support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support Polygons support_mesh; //!< Areas from support meshes which should NOT be supported by more support Polygons anti_overhang; //!< Areas where no overhang should be detected. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..359ea4fdf9 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3327,89 +3327,100 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay support_roof_line_distance *= roof_extruder.settings.get("initial_layer_line_width_factor"); } - Polygons infill_outline = support_layer.support_roof; - Polygons wall; - // make sure there is a wall if this is on the first layer - if (gcode_layer.getLayerNr() == 0) - { - wall = support_layer.support_roof.offset(-support_roof_line_width / 2); - infill_outline = wall.offset(-support_roof_line_width / 2); - } + const auto half_layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height") / 2; - Infill roof_computation( - pattern, - zig_zaggify_infill, - connect_polygons, - infill_outline, - gcode_layer.configs_storage.support_roof_config.getLineWidth(), - support_roof_line_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - Polygons roof_polygons; - std::vector roof_paths; - Polygons roof_lines; - roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); - if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) - { - return false; // We didn't create any support roof. - } - gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support - if (gcode_layer.getLayerNr() == 0) - { - gcode_layer.addPolygonsByOptimizer(wall, gcode_layer.configs_storage.support_roof_config); - } - if (! roof_polygons.empty()) - { - constexpr bool force_comb_retract = false; - gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(roof_polygons, gcode_layer.configs_storage.support_roof_config); - } - if (! roof_paths.empty()) + auto infill_outlines = { support_layer.support_roof.difference(support_layer.support_fractional_roof_top), support_layer.support_fractional_roof_top }; + auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! + bool generated_something = false; + for (auto infill_outline : infill_outlines) { - const GCodePathConfig& config = gcode_layer.configs_storage.support_roof_config; - constexpr bool retract_before_outer_wall = false; - constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + Polygons wall; + // make sure there is a wall if this is on the first layer + if (gcode_layer.getLayerNr() == 0) + { + wall = support_layer.support_roof.offset(-support_roof_line_width / 2); + infill_outline = wall.offset(-support_roof_line_width / 2); + } - InsetOrderOptimizer wall_orderer( - *this, - storage, - gcode_layer, - roof_extruder.settings, - roof_extruder_nr, - config, - config, - config, - config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - roof_extruder_nr, - roof_extruder_nr, - z_seam_config, - roof_paths); - wall_orderer.addToLayer(); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_outline, + current_roof_config.getLineWidth(), + support_roof_line_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z + current_roof_config.z_offset, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + Polygons roof_polygons; + std::vector roof_paths; + Polygons roof_lines; + roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); + if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) + { + continue; // We didn't create any support roof. + } + generated_something = true; // We _did_ create at least some support roof. + gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support + if (gcode_layer.getLayerNr() == 0) + { + gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); + } + if (!roof_polygons.empty()) + { + constexpr bool force_comb_retract = false; + gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); + gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); + } + if (!roof_paths.empty()) + { + const GCodePathConfig& config = current_roof_config; + constexpr bool retract_before_outer_wall = false; + constexpr coord_t wipe_dist = 0; + const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + roof_extruder.settings, + roof_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + roof_extruder_nr, + roof_extruder_nr, + z_seam_config, + roof_paths); + wall_orderer.addToLayer(); + } + gcode_layer.addLinesByOptimizer( + roof_lines, + gcode_layer.configs_storage.support_roof_config, + (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + + current_roof_config.z_offset = -half_layer_height; + current_roof_config.flow /= 2.0; } - gcode_layer.addLinesByOptimizer( - roof_lines, - gcode_layer.configs_storage.support_roof_config, - (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); - return true; + return generated_something; } bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, LayerPlan& gcode_layer) const diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 4a283c147e..4642c8bfd1 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -38,19 +38,21 @@ constexpr int MINIMUM_SQUARED_LINE_LENGTH = MINIMUM_LINE_LENGTH * MINIMUM_LINE_L GCodePath* LayerPlan::getLatestPathWithConfig( const GCodePathConfig& config, - SpaceFillType space_fill_type, + const coord_t z_offset, + const SpaceFillType space_fill_type, const Ratio flow, const Ratio width_factor, - bool spiralize, + const bool spiralize, const Ratio speed_factor) { std::vector& paths = extruder_plans.back().paths; if (paths.size() > 0 && paths.back().config == config && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor - && paths.back().speed_factor == speed_factor && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between + && paths.back().speed_factor == speed_factor && paths.back().z_offset == z_offset && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } - paths.emplace_back(GCodePath{ .config = config, + paths.emplace_back(GCodePath{ .z_offset = z_offset, + .config = config, .mesh = current_mesh, .space_fill_type = space_fill_type, .flow = flow, @@ -326,14 +328,14 @@ std::optional> LayerPlan::getFirstTravelDestinationState( return ret; } -GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) +GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract, const coord_t z_offset) { const GCodePathConfig& travel_config = configs_storage.travel_config_per_extruder[getExtruder()]; const RetractionConfig& retraction_config = current_mesh ? current_mesh->retraction_wipe_config.retraction_config : storage.retraction_wipe_config_per_extruder[getExtruder()].retraction_config; - GCodePath* path = getLatestPathWithConfig(travel_config, SpaceFillType::None); + GCodePath* path = getLatestPathWithConfig(travel_config, z_offset, SpaceFillType::None); bool combed = false; @@ -478,7 +480,7 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract) return ret; } -GCodePath& LayerPlan::addTravel_simple(Point p, GCodePath* path) +GCodePath& LayerPlan::addTravel_simple(const Point p, GCodePath* path) { bool is_first_travel_of_layer = ! static_cast(last_planned_position); if (is_first_travel_of_layer) @@ -488,7 +490,7 @@ GCodePath& LayerPlan::addTravel_simple(Point p, GCodePath* path) } if (path == nullptr) { - path = getLatestPathWithConfig(configs_storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None); + path = getLatestPathWithConfig(configs_storage.travel_config_per_extruder[getExtruder()], 0, SpaceFillType::None); } path->points.push_back(p); last_planned_position = p; @@ -506,16 +508,16 @@ void LayerPlan::planPrime(const float& prime_blob_wipe_length) } void LayerPlan::addExtrusionMove( - Point p, + const Point p, const GCodePathConfig& config, - SpaceFillType space_fill_type, + const SpaceFillType space_fill_type, const Ratio& flow, const Ratio width_factor, - bool spiralize, - Ratio speed_factor, - double fan_speed) + const bool spiralize, + const Ratio speed_factor, + const double fan_speed) { - GCodePath* path = getLatestPathWithConfig(config, space_fill_type, flow, width_factor, spiralize, speed_factor); + GCodePath* path = getLatestPathWithConfig(config, config.z_offset, space_fill_type, flow, width_factor, spiralize, speed_factor); path->points.push_back(p); path->setFanSpeed(fan_speed); if (! static_cast(first_extrusion_acc_jerk)) @@ -537,7 +539,7 @@ void LayerPlan::addPolygon( { constexpr Ratio width_ratio = 1.0_r; // Not printed with variable line width. Point p0 = polygon[start_idx]; - addTravel(p0, always_retract); + addTravel(p0, always_retract, config.z_offset); const int direction = backwards ? -1 : 1; for (size_t point_idx = 1; point_idx < polygon.size(); point_idx++) { @@ -1250,7 +1252,7 @@ void LayerPlan::addLinesInGivenOrder( } else { - addTravel(start); + addTravel(start, false, config.z_offset); } Point p0 = start; @@ -1947,7 +1949,12 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeRetraction(retraction_config->retraction_config); } - if (! path.retract && path.config.isTravelPath() && path.points.size() == 1 && path.points[0] == gcode.getPositionXY() && z == gcode.getPositionZ()) + if (z > 0) + { + gcode.setZ(z + path.z_offset); + } + + if (! path.retract && path.config.isTravelPath() && path.points.size() == 1 && path.points[0] == gcode.getPositionXY() && (z + path.z_offset) == gcode.getPositionZ()) { // ignore travel moves to the current location to avoid needless change of acceleration/jerk continue; diff --git a/src/support.cpp b/src/support.cpp index 188f2b486d..b8de3c5247 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1805,7 +1805,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh const double minimum_roof_area = mesh.settings.get("minimum_roof_area"); std::vector& support_layers = storage.support.supportLayers; - for (LayerIndex layer_idx = 0; layer_idx < static_cast(support_layers.size() - z_distance_top); layer_idx++) + for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top) - 1; layer_idx >= 0; --layer_idx) { const LayerIndex top_layer_idx_above{ std::min(LayerIndex{ support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) @@ -1818,6 +1818,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh Polygons roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); support_layers[layer_idx].support_roof.add(roofs); + support_layers[layer_idx].support_fractional_roof_top = roofs.difference(support_layers[layer_idx + 1].support_roof); scripta::log("support_interface_roofs", roofs, SectionType::SUPPORT, layer_idx); } From a07fb927de6ab53893db31c2af7c196d1c5ed8ae Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 21 Sep 2023 16:07:07 +0200 Subject: [PATCH 529/656] Proof-of-concept: fractional support z-distance. Instead of half-height layers in each area that could potentially be affected by non-layer-height modulo support z distance, actually calculate the proper z-offset that is needed for the fractional layer. Proof of concept mostly in the sense that the quality of the code added may not reflect our standards, and would also be missing functionality (for instance; what if we print without support-interface, or tree-support at all). part of CURA-11041, which is the proof-of-concept for CURA-10407 --- src/FffGcodeWriter.cpp | 12 +++++++----- src/support.cpp | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 359ea4fdf9..1af4378832 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2287,7 +2287,7 @@ bool FffGcodeWriter::processInsets( if (mesh_group_settings.get("support_enable")) { const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + 1; + const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Previously '... +1', but now there is an extra fractional layer on top. const int support_layer_nr = gcode_layer.getLayerNr() - z_distance_top_layers; if (support_layer_nr > 0) @@ -2596,7 +2596,7 @@ void FffGcodeWriter::processTopBottom( { const coord_t layer_height = mesh_config.inset0_config.getLayerThickness(); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + 1; + const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Previously '... +1', but now there is an extra fractional layer on top. support_layer_nr = layer_nr - z_distance_top_layers; } @@ -3327,7 +3327,9 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay support_roof_line_distance *= roof_extruder.settings.get("initial_layer_line_width_factor"); } - const auto half_layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height") / 2; + const auto layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); + const auto support_top_distance = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_top_distance"); + const coord_t leftover_support_distance = support_top_distance % layer_height; auto infill_outlines = { support_layer.support_roof.difference(support_layer.support_fractional_roof_top), support_layer.support_fractional_roof_top }; auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! @@ -3417,8 +3419,8 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay gcode_layer.configs_storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); - current_roof_config.z_offset = -half_layer_height; - current_roof_config.flow /= 2.0; + current_roof_config.z_offset = -leftover_support_distance; + current_roof_config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); } return generated_something; } diff --git a/src/support.cpp b/src/support.cpp index b8de3c5247..ba1dc02a6f 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -794,7 +794,7 @@ void AreaSupport::generateOverhangAreasForMesh(SliceDataStorage& storage, SliceM // Don't generate overhang areas if the Z distance is higher than the objects we're generating support for. const coord_t layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + 1; // Support must always be 1 layer below overhang. + const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Previously '... +1', but now there is an extra fractional layer on top. if (z_distance_top_layers + 1 > storage.print_layer_count) { return; @@ -1059,7 +1059,7 @@ void AreaSupport::generateSupportAreasForMesh( // early out const coord_t layer_thickness = mesh_group_settings.get("layer_height"); const coord_t z_distance_top = ((mesh.settings.get("support_roof_enable")) ? roof_settings : infill_settings).get("support_top_distance"); - const size_t layer_z_distance_top = round_up_divide(z_distance_top, layer_thickness) + 1; // support must always be 1 layer below overhang + const size_t layer_z_distance_top = round_up_divide(z_distance_top, layer_thickness); // Previously '... +1', but now there is an extra fractional layer on top. if (layer_z_distance_top + 1 > layer_count) { return; @@ -1805,7 +1805,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh const double minimum_roof_area = mesh.settings.get("minimum_roof_area"); std::vector& support_layers = storage.support.supportLayers; - for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top) - 1; layer_idx >= 0; --layer_idx) + for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top); layer_idx >= 0; --layer_idx) { const LayerIndex top_layer_idx_above{ std::min(LayerIndex{ support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) From 7bdbe9acc8137cbb23245768737d5da6e3d62f4a Mon Sep 17 00:00:00 2001 From: rburema Date: Thu, 21 Sep 2023 14:08:19 +0000 Subject: [PATCH 530/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 6 +++--- src/LayerPlan.cpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1af4378832..866ab2ee8f 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3332,7 +3332,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay const coord_t leftover_support_distance = support_top_distance % layer_height; auto infill_outlines = { support_layer.support_roof.difference(support_layer.support_fractional_roof_top), support_layer.support_fractional_roof_top }; - auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! + auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! bool generated_something = false; for (auto infill_outline : infill_outlines) { @@ -3382,13 +3382,13 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay { gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); } - if (!roof_polygons.empty()) + if (! roof_polygons.empty()) { constexpr bool force_comb_retract = false; gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); } - if (!roof_paths.empty()) + if (! roof_paths.empty()) { const GCodePathConfig& config = current_roof_config; constexpr bool retract_before_outer_wall = false; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 4642c8bfd1..70a6152eb7 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -47,7 +47,8 @@ GCodePath* LayerPlan::getLatestPathWithConfig( { std::vector& paths = extruder_plans.back().paths; if (paths.size() > 0 && paths.back().config == config && ! paths.back().done && paths.back().flow == flow && paths.back().width_factor == width_factor - && paths.back().speed_factor == speed_factor && paths.back().z_offset == z_offset && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between + && paths.back().speed_factor == speed_factor && paths.back().z_offset == z_offset + && paths.back().mesh == current_mesh) // spiralize can only change when a travel path is in between { return &paths.back(); } From c6f5b9ae30c43f5b9faf59b1df427a275da77678 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 21 Sep 2023 16:51:29 +0200 Subject: [PATCH 531/656] Basically working prime tower without raft CURA-10993 --- src/FffGcodeWriter.cpp | 4 +- src/PrimeTower.cpp | 107 +++++++++++++++++++++-------------------- src/raft.cpp | 50 ++++++++++--------- 3 files changed, 86 insertions(+), 75 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..0bc8b7a809 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -161,7 +161,9 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep } run_multiple_producers_ordered_consumer( - process_layer_starting_layer_nr, + // process_layer_starting_layer_nr, + -Raft::getTotalExtraLayers(), + // total_layers + Raft::getFillerLayerCount() - 1, total_layers, [&storage, total_layers, this](int layer_nr) { diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 3a4846c69c..b731011398 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -1,65 +1,69 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. -#include -#include +#include "PrimeTower.h" #include "Application.h" //To get settings. #include "ExtruderTrain.h" -#include "gcodeExport.h" -#include "infill.h" #include "LayerPlan.h" -#include "PrimeTower.h" #include "PrintFeature.h" -#include "raft.h" #include "Scene.h" #include "Slice.h" +#include "gcodeExport.h" +#include "infill.h" +#include "raft.h" #include "sliceDataStorage.h" -#define CIRCLE_RESOLUTION 32 //The number of vertices in each circle. +#include +#include + +#define CIRCLE_RESOLUTION 32 // The number of vertices in each circle. -namespace cura +namespace cura { PrimeTower::PrimeTower() -: wipe_from_middle(false) + : wipe_from_middle(false) { const Scene& scene = Application::getInstance().current_slice->scene; { EPlatformAdhesion adhesion_type = scene.current_mesh_group->settings.get("adhesion_type"); - //When we have multiple extruders sharing the same heater/nozzle, we expect that all the extruders have been + // When we have multiple extruders sharing the same heater/nozzle, we expect that all the extruders have been //'primed' by the print-start gcode script, but we don't know which one has been left at the tip of the nozzle - //and whether it needs 'purging' (before extruding a pure material) or not, so we need to prime (actually purge) - //each extruder before it is used for the model. This can done by the (per-extruder) brim lines or (per-extruder) - //skirt lines when they are used, but we need to do that inside the first prime-tower layer when they are not - //used (sacrifying for this purpose the usual single-extruder first layer, that would be better for prime-tower - //adhesion). - - multiple_extruders_on_first_layer = scene.current_mesh_group->settings.get("machine_extruders_share_nozzle") && ((adhesion_type != EPlatformAdhesion::SKIRT) && (adhesion_type != EPlatformAdhesion::BRIM)); + // and whether it needs 'purging' (before extruding a pure material) or not, so we need to prime (actually purge) + // each extruder before it is used for the model. This can done by the (per-extruder) brim lines or (per-extruder) + // skirt lines when they are used, but we need to do that inside the first prime-tower layer when they are not + // used (sacrifying for this purpose the usual single-extruder first layer, that would be better for prime-tower + // adhesion). + + 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 + 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; - would_have_actual_tower = enabled; // Assume so for now. + would_have_actual_tower = enabled; // Assume so for now. extruder_count = scene.extruders.size(); extruder_order.resize(extruder_count); for (unsigned int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { - extruder_order[extruder_nr] = extruder_nr; //Start with default order, then sort. + extruder_order[extruder_nr] = extruder_nr; // Start with default order, then sort. } - //Sort from high adhesion to low adhesion. - const Scene* scene_pointer = &scene; //Communicate to lambda via pointer to prevent copy. - std::stable_sort(extruder_order.begin(), extruder_order.end(), [scene_pointer](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool - { - const Ratio adhesion_a = scene_pointer->extruders[extruder_nr_a].settings.get("material_adhesion_tendency"); - const Ratio adhesion_b = scene_pointer->extruders[extruder_nr_b].settings.get("material_adhesion_tendency"); - return adhesion_a < adhesion_b; - }); + // Sort from high adhesion to low adhesion. + const Scene* scene_pointer = &scene; // Communicate to lambda via pointer to prevent copy. + std::stable_sort( + extruder_order.begin(), + extruder_order.end(), + [scene_pointer](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool + { + const Ratio adhesion_a = scene_pointer->extruders[extruder_nr_a].settings.get("material_adhesion_tendency"); + const Ratio adhesion_b = scene_pointer->extruders[extruder_nr_b].settings.get("material_adhesion_tendency"); + return adhesion_a < adhesion_b; + }); } void PrimeTower::checkUsed(const SliceDataStorage& storage) @@ -78,7 +82,7 @@ void PrimeTower::checkUsed(const SliceDataStorage& storage) void PrimeTower::generateGroundpoly() { - if (!enabled) + if (! enabled) { return; } @@ -97,7 +101,8 @@ void PrimeTower::generateGroundpoly() void PrimeTower::generatePaths(const SliceDataStorage& storage) { - would_have_actual_tower = storage.max_print_height_second_to_last_extruder >= 0; //Maybe it turns out that we don't need a prime tower after all because there are no layer switches. + would_have_actual_tower + = storage.max_print_height_second_to_last_extruder >= 0; // Maybe it turns out that we don't need a prime tower after all because there are no layer switches. if (would_have_actual_tower && enabled) { generatePaths_denseInfill(); @@ -113,7 +118,7 @@ void PrimeTower::generatePaths_denseInfill() pattern_per_extruder.resize(extruder_count); pattern_per_extruder_layer0.resize(extruder_count); - coord_t cumulative_inset = 0; //Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. + coord_t cumulative_inset = 0; // Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. for (size_t extruder_nr : extruder_order) { const coord_t line_width = scene.extruders[extruder_nr].settings.get("prime_tower_line_width"); @@ -122,28 +127,28 @@ void PrimeTower::generatePaths_denseInfill() coord_t current_volume = 0; ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; - //Create the walls of the prime tower. + // Create the walls of the prime tower. unsigned int wall_nr = 0; for (; current_volume < required_volume; wall_nr++) { - //Create a new polygon with an offset from the outer polygon. + // Create a new polygon with an offset from the outer polygon. Polygons polygons = outer_poly.offset(-cumulative_inset - wall_nr * line_width - line_width / 2); pattern.polygons.add(polygons); current_volume += polygons.polygonLength() * line_width * layer_height * flow; - if (polygons.empty()) //Don't continue. We won't ever reach the required volume because it doesn't fit. + if (polygons.empty()) // Don't continue. We won't ever reach the required volume because it doesn't fit. { break; } } - //Only the most inside extruder needs to fill the inside of the prime tower - if (extruder_nr != extruder_order.back()) + // Only the most inside extruder needs to fill the inside of the prime tower + if (false /*extruder_nr != extruder_order.back()*/) { pattern_per_extruder_layer0 = pattern_per_extruder; } else { - //Generate the pattern for the first layer. + // Generate the pattern for the first layer. coord_t line_width_layer0 = line_width * scene.extruders[extruder_nr].settings.get("initial_layer_line_width_factor"); ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0[extruder_nr]; @@ -151,7 +156,7 @@ void PrimeTower::generatePaths_denseInfill() // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the // first layer of the prime tower to not stick well. Polygons inset = outer_poly.offset(-cumulative_inset - line_width_layer0 / 2); - while (!inset.empty()) + while (! inset.empty()) { pattern_layer0.polygons.add(inset); inset = inset.offset(-line_width_layer0); @@ -183,7 +188,7 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la } const LayerIndex layer_nr = gcode_layer.getLayerNr(); - if (layer_nr < 0 || layer_nr > storage.max_print_height_second_to_last_extruder + 1) + if (/*layer_nr < 0 ||*/ layer_nr > storage.max_print_height_second_to_last_extruder + 1) { return; } @@ -207,7 +212,7 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la // post-wipe: if (post_wipe) { - //Make sure we wipe the old extruder on the prime tower. + // Make sure we wipe the old extruder on the prime tower. const Settings& previous_settings = Application::getInstance().current_slice->scene.extruders[prev_extruder].settings; const Point previous_nozzle_offset = Point(previous_settings.get("machine_nozzle_offset_x"), previous_settings.get("machine_nozzle_offset_y")); const Settings& new_settings = Application::getInstance().current_slice->scene.extruders[new_extruder].settings; @@ -220,9 +225,9 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const { - const ExtrusionMoves& pattern = (gcode_layer.getLayerNr() == -static_cast(Raft::getFillerLayerCount())) - ? pattern_per_extruder_layer0[extruder_nr] - : pattern_per_extruder[extruder_nr]; + const ExtrusionMoves& pattern + //= (gcode_layer.getLayerNr() == -static_cast(Raft::getFillerLayerCount())) ? pattern_per_extruder_layer0[extruder_nr] : pattern_per_extruder[extruder_nr]; + = (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) ? pattern_per_extruder_layer0[extruder_nr] : pattern_per_extruder[extruder_nr]; const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; @@ -234,7 +239,7 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { const Polygons outside_polygon = outer_poly.getOutsidePolygons(); AABB outside_polygon_boundary_box(outside_polygon); - for(size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) + for (size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) { SupportLayer& support_layer = storage.support.supportLayers[layer]; // take the differences of the support infill parts and the prime tower area @@ -244,13 +249,13 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const int extruder_nr) const { - int current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations) - + number_of_prime_tower_start_locations) % number_of_prime_tower_start_locations; + int current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations) + number_of_prime_tower_start_locations) + % number_of_prime_tower_start_locations; const ClosestPolygonPoint wipe_location = prime_tower_start_locations[current_start_location_idx]; const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; - const coord_t inward_dist = train.settings.get("machine_nozzle_size") * 3 / 2 ; + const coord_t inward_dist = train.settings.get("machine_nozzle_size") * 3 / 2; const coord_t start_dist = train.settings.get("machine_nozzle_size") * 2; const Point prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist); const Point outward_dir = wipe_location.location - prime_end; @@ -259,4 +264,4 @@ void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const int extruder_nr gcode_layer.addTravel(prime_start); } -}//namespace cura +} // namespace cura diff --git a/src/raft.cpp b/src/raft.cpp index 96c81be90b..16134f55bc 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -1,17 +1,18 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. -#include +#include "raft.h" #include "Application.h" //To get settings. #include "ExtruderTrain.h" -#include "raft.h" #include "Slice.h" +#include "settings/EnumSettings.h" //For EPlatformAdhesion. #include "sliceDataStorage.h" #include "support.h" -#include "settings/EnumSettings.h" //For EPlatformAdhesion. #include "utils/math.h" +#include + namespace cura { @@ -21,24 +22,27 @@ void Raft::generate(SliceDataStorage& storage) const Settings& settings = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("raft_base_extruder_nr").settings; const coord_t distance = settings.get("raft_margin"); constexpr bool include_support = true; - constexpr bool dont_include_prime_tower = false; // Prime tower raft will be handled separately in 'storage.primeRaftOutline'; see below. + constexpr bool dont_include_prime_tower = false; // Prime tower raft will be handled separately in 'storage.primeRaftOutline'; see below. storage.raftOutline = storage.getLayerOutlines(0, include_support, dont_include_prime_tower).offset(distance, ClipperLib::jtRound); const coord_t shield_line_width_layer0 = settings.get("skirt_brim_line_width"); if (storage.draft_protection_shield.size() > 0) { - Polygons draft_shield_raft = storage.draft_protection_shield.offset(shield_line_width_layer0) // start half a line width outside shield - .difference(storage.draft_protection_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield + Polygons draft_shield_raft + = storage.draft_protection_shield + .offset(shield_line_width_layer0) // start half a line width outside shield + .difference(storage.draft_protection_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield storage.raftOutline = storage.raftOutline.unionPolygons(draft_shield_raft); } if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0) { const Polygons& ooze_shield = storage.oozeShield[0]; - Polygons ooze_shield_raft = ooze_shield.offset(shield_line_width_layer0) // start half a line width outside shield + Polygons ooze_shield_raft = ooze_shield + .offset(shield_line_width_layer0) // start half a line width outside shield .difference(ooze_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield storage.raftOutline = storage.raftOutline.unionPolygons(ooze_shield_raft); } - if(settings.get("raft_remove_inside_corners")) + if (settings.get("raft_remove_inside_corners")) { storage.raftOutline.makeConvex(); } @@ -62,25 +66,25 @@ void Raft::generate(SliceDataStorage& storage) } } - storage.primeRaftOutline = storage.primeTower.outer_poly.offset(distance, ClipperLib::jtRound); - // NOTE: the raft doesn't take the prime tower brim into account, because it's (currently) not being printed when printing a raft - if (settings.get("raft_remove_inside_corners")) - { - storage.primeRaftOutline = storage.primeRaftOutline.unionPolygons(storage.raftOutline); - storage.primeRaftOutline.makeConvex(); - } - storage.primeRaftOutline = storage.primeRaftOutline.difference(storage.raftOutline); // In case of overlaps. + // storage.primeRaftOutline = storage.primeTower.outer_poly.offset(distance, ClipperLib::jtRound); + // NOTE: the raft doesn't take the prime tower brim into account, because it's (currently) not being printed when printing a raft + // if (settings.get("raft_remove_inside_corners")) + //{ + // storage.primeRaftOutline = storage.primeRaftOutline.unionPolygons(storage.raftOutline); + // storage.primeRaftOutline.makeConvex(); + // } + // storage.primeRaftOutline = storage.primeRaftOutline.difference(storage.raftOutline); // In case of overlaps. } coord_t Raft::getTotalThickness() { - const Settings& mesh_group_settings =Application::getInstance().current_slice->scene.current_mesh_group->settings; + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const ExtruderTrain& base_train = mesh_group_settings.get("raft_base_extruder_nr"); const ExtruderTrain& interface_train = mesh_group_settings.get("raft_interface_extruder_nr"); const ExtruderTrain& surface_train = mesh_group_settings.get("raft_surface_extruder_nr"); return base_train.settings.get("raft_base_thickness") - + interface_train.settings.get("raft_interface_layers") * interface_train.settings.get("raft_interface_thickness") - + surface_train.settings.get("raft_surface_layers") * surface_train.settings.get("raft_surface_thickness"); + + interface_train.settings.get("raft_interface_layers") * interface_train.settings.get("raft_interface_thickness") + + surface_train.settings.get("raft_surface_layers") * surface_train.settings.get("raft_surface_thickness"); } coord_t Raft::getZdiffBetweenRaftAndLayer0() @@ -115,7 +119,7 @@ coord_t Raft::getFillerLayerHeight() size_t Raft::getTotalExtraLayers() { - const Settings& mesh_group_settings =Application::getInstance().current_slice->scene.current_mesh_group->settings; + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const ExtruderTrain& base_train = mesh_group_settings.get("raft_base_extruder_nr"); const ExtruderTrain& interface_train = mesh_group_settings.get("raft_interface_extruder_nr"); const ExtruderTrain& surface_train = mesh_group_settings.get("raft_surface_extruder_nr"); @@ -127,4 +131,4 @@ size_t Raft::getTotalExtraLayers() } -}//namespace cura +} // namespace cura From f0e4ababa60dc5c78303c382460d1e56a0a04f7a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 21 Sep 2023 17:00:40 +0200 Subject: [PATCH 532/656] Add brim on prime tower without raft CURA-10993 --- src/PrimeTower.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index b731011398..d6ff7a3fcd 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -155,11 +155,16 @@ void PrimeTower::generatePaths_denseInfill() // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the // first layer of the prime tower to not stick well. - Polygons inset = outer_poly.offset(-cumulative_inset - line_width_layer0 / 2); - while (! inset.empty()) + Polygons inset = outer_poly.offset(-(-cumulative_inset - line_width_layer0 / 2)); + /*while (! inset.empty()) { pattern_layer0.polygons.add(inset); inset = inset.offset(-line_width_layer0); + }*/ + for (int i = 0; i < 15; ++i) + { + pattern_layer0.polygons.add(inset); + inset = inset.offset(line_width_layer0); } } cumulative_inset += wall_nr * line_width; @@ -225,14 +230,18 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const { - const ExtrusionMoves& pattern - //= (gcode_layer.getLayerNr() == -static_cast(Raft::getFillerLayerCount())) ? pattern_per_extruder_layer0[extruder_nr] : pattern_per_extruder[extruder_nr]; - = (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) ? pattern_per_extruder_layer0[extruder_nr] : pattern_per_extruder[extruder_nr]; - const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + + if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) + { + const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); + gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); + } } void PrimeTower::subtractFromSupport(SliceDataStorage& storage) From 22824f9d44fdea70471b0f64c4a3b95dd06b5d83 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 22 Sep 2023 09:23:23 +0200 Subject: [PATCH 533/656] Fixed missing prime tower hull on some layers CURA-10993 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 0bc8b7a809..520a8b42d2 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1346,7 +1346,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayerExcludingStartingExtr std::vector extruder_is_used_on_this_layer = storage.getExtrudersUsed(layer_nr); // The outermost prime tower extruder is always used if there is a prime tower, apart on layers with negative index (e.g. for the raft) - if (mesh_group_settings.get("prime_tower_enable") && layer_nr >= 0 && layer_nr <= storage.max_print_height_second_to_last_extruder) + if (mesh_group_settings.get("prime_tower_enable") && /*layer_nr >= 0 &&*/ layer_nr <= storage.max_print_height_second_to_last_extruder) { extruder_is_used_on_this_layer[storage.primeTower.extruder_order[0]] = true; } From cc01931e836a7d00ae2c6032d5d2e904af35fa8b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 22 Sep 2023 10:28:53 +0200 Subject: [PATCH 534/656] Higher brim for primer tower to make it stronger CURA-10993 --- src/PrimeTower.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index d6ff7a3fcd..316be75b3a 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -236,7 +236,8 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); - if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) + // if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) + if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) { const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); From 16d1a84567072bb7f39b18b2171d7cfea9d0e1c9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 22 Sep 2023 14:51:52 +0200 Subject: [PATCH 535/656] Fix crash in proof-of-concept code. part of CURA-11041 --- src/support.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support.cpp b/src/support.cpp index ba1dc02a6f..a4cf4a711e 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1805,7 +1805,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh const double minimum_roof_area = mesh.settings.get("minimum_roof_area"); std::vector& support_layers = storage.support.supportLayers; - for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top); layer_idx >= 0; --layer_idx) + for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top) - 1; layer_idx >= 0; --layer_idx) { const LayerIndex top_layer_idx_above{ std::min(LayerIndex{ support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) From 3820d3b8ad8eb67195ef93c446324dcf7388aa0a Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 22 Sep 2023 15:05:46 +0200 Subject: [PATCH 536/656] Proof-of-concept. Attempt to fix newly introduced small extrusions. done as part of CURA-11041 --- src/FffGcodeWriter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 866ab2ee8f..3746a9ada2 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3331,10 +3331,14 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay const auto support_top_distance = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_top_distance"); const coord_t leftover_support_distance = support_top_distance % layer_height; - auto infill_outlines = { support_layer.support_roof.difference(support_layer.support_fractional_roof_top), support_layer.support_fractional_roof_top }; + std::vector infill_outlines = + { + Simplify(roof_extruder.settings).polygon(support_layer.support_roof.difference(support_layer.support_fractional_roof_top)), + Simplify(roof_extruder.settings).polygon(support_layer.support_fractional_roof_top) + }; auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! bool generated_something = false; - for (auto infill_outline : infill_outlines) + for (auto& infill_outline : infill_outlines) { Polygons wall; // make sure there is a wall if this is on the first layer From 94d6007a7f7b6e4a11e2e8d77acad0e60a645c4a Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 22 Sep 2023 13:07:21 +0000 Subject: [PATCH 537/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 3746a9ada2..9361f1ff10 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3331,11 +3331,8 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay const auto support_top_distance = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_top_distance"); const coord_t leftover_support_distance = support_top_distance % layer_height; - std::vector infill_outlines = - { - Simplify(roof_extruder.settings).polygon(support_layer.support_roof.difference(support_layer.support_fractional_roof_top)), - Simplify(roof_extruder.settings).polygon(support_layer.support_fractional_roof_top) - }; + std::vector infill_outlines = { Simplify(roof_extruder.settings).polygon(support_layer.support_roof.difference(support_layer.support_fractional_roof_top)), + Simplify(roof_extruder.settings).polygon(support_layer.support_fractional_roof_top) }; auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! bool generated_something = false; for (auto& infill_outline : infill_outlines) From 48e83717de4465e7110003e48697f9d49bf3afc8 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 22 Sep 2023 16:42:38 +0200 Subject: [PATCH 538/656] Print prime tower during raft print CURA-10993 --- src/FffGcodeWriter.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 520a8b42d2..5554355ba3 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -143,21 +143,21 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep findLayerSeamsForSpiralize(storage, total_layers); } - int process_layer_starting_layer_nr = 0; + // int process_layer_starting_layer_nr = 0; const bool has_raft = scene.current_mesh_group->settings.get("adhesion_type") == EPlatformAdhesion::RAFT; if (has_raft) { processRaft(storage); // process filler layers to fill the airgap with helper object (support etc) so that they stick better to the raft. // only process the filler layers if there is anything to print in them. - for (bool extruder_is_used_in_filler_layers : storage.getExtrudersUsed(-1)) + /*for (bool extruder_is_used_in_filler_layers : storage.getExtrudersUsed(-1)) { if (extruder_is_used_in_filler_layers) { process_layer_starting_layer_nr = -Raft::getFillerLayerCount(); break; } - } + }*/ } run_multiple_producers_ordered_consumer( @@ -687,6 +687,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); + + setExtruder_addPrime(storage, gcode_layer, 0); } const coord_t interface_layer_height = interface_settings.get("raft_interface_thickness"); @@ -790,6 +792,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); + + setExtruder_addPrime(storage, gcode_layer, 0); } const coord_t surface_layer_height = surface_settings.get("raft_surface_thickness"); @@ -894,6 +898,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } layer_plan_buffer.handle(gcode_layer, gcode); + + setExtruder_addPrime(storage, gcode_layer, 0); } } @@ -1014,7 +1020,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn // later in the print a prime tower is needed. // - prime tower is already printed this layer (only applicable for more than 2 extruders). // The setExtruder_addPrime takes care of this. - if (extruder_nr != extruder_order.front() || extruder_order.size() == 1) + if (extruder_nr != extruder_order.front() || (extruder_order.size() == 1 && layer_nr >= 0) || extruder_nr == 0) { setExtruder_addPrime(storage, gcode_layer, extruder_nr); } @@ -1045,7 +1051,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn // Always print a prime tower before switching extruder. Unless: // - The prime tower is already printed this layer (setExtruder_addPrime takes care of this). // - this is the last extruder of the layer, since the next layer will start with the same extruder. - if (extruder_nr != extruder_order.back()) + if (extruder_nr != extruder_order.back() && layer_nr >= 0) { setExtruder_addPrime(storage, gcode_layer, extruder_nr); } From e48316eba6edb6f07022bb5b0d69e04b1ac0dee5 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 25 Sep 2023 10:34:52 +0200 Subject: [PATCH 539/656] Better prime tower raft CURA-10993 --- src/FffGcodeWriter.cpp | 14 +++++++------- src/PrimeTower.cpp | 30 ++++++++++++++++++++---------- src/gcodeExport.cpp | 2 +- src/raft.cpp | 10 +++++++++- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 5554355ba3..8f7d993811 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -143,28 +143,28 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep findLayerSeamsForSpiralize(storage, total_layers); } - // int process_layer_starting_layer_nr = 0; + int process_layer_starting_layer_nr = 0; const bool has_raft = scene.current_mesh_group->settings.get("adhesion_type") == EPlatformAdhesion::RAFT; if (has_raft) { processRaft(storage); // process filler layers to fill the airgap with helper object (support etc) so that they stick better to the raft. // only process the filler layers if there is anything to print in them. - /*for (bool extruder_is_used_in_filler_layers : storage.getExtrudersUsed(-1)) + for (bool extruder_is_used_in_filler_layers : storage.getExtrudersUsed(-1)) { if (extruder_is_used_in_filler_layers) { process_layer_starting_layer_nr = -Raft::getFillerLayerCount(); break; } - }*/ + } } run_multiple_producers_ordered_consumer( - // process_layer_starting_layer_nr, - -Raft::getTotalExtraLayers(), - // total_layers + Raft::getFillerLayerCount() - 1, - total_layers, + process_layer_starting_layer_nr, + //-Raft::getTotalExtraLayers(), + total_layers + Raft::getFillerLayerCount() - 1, + // total_layers, [&storage, total_layers, this](int layer_nr) { return &processLayer(storage, layer_nr, total_layers); diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 316be75b3a..9845eff054 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -149,22 +149,24 @@ void PrimeTower::generatePaths_denseInfill() else { // Generate the pattern for the first layer. - coord_t line_width_layer0 = line_width * scene.extruders[extruder_nr].settings.get("initial_layer_line_width_factor"); + const coord_t line_width_layer0 = scene.extruders[extruder_nr].settings.get("raft_base_line_width"); ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0[extruder_nr]; // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the // first layer of the prime tower to not stick well. - Polygons inset = outer_poly.offset(-(-cumulative_inset - line_width_layer0 / 2)); - /*while (! inset.empty()) + Polygons inset = outer_poly.offset(-cumulative_inset - line_width_layer0 / 2); + while (! inset.empty()) { pattern_layer0.polygons.add(inset); inset = inset.offset(-line_width_layer0); - }*/ + } + + Polygons outset = outer_poly.offset(cumulative_inset + line_width_layer0 / 2); for (int i = 0; i < 15; ++i) { - pattern_layer0.polygons.add(inset); - inset = inset.offset(line_width_layer0); + pattern_layer0.polygons.add(outset); + outset = outset.offset(line_width_layer0); } } cumulative_inset += wall_nr * line_width; @@ -230,19 +232,27 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const { - const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + const GCodePathConfig& config + = gcode_layer.getLayerNr() < 0 ? gcode_layer.configs_storage.raft_base_config : gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); - gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); // if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) { + const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; + const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); } + else + { + const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + + const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); + gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + } } void PrimeTower::subtractFromSupport(SliceDataStorage& storage) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index caff200811..799ac84e0c 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1498,7 +1498,7 @@ void GCodeExport::writeTemperatureCommand(const size_t extruder, const Temperatu *output_stream << " T" << extruder; } #ifdef ASSERT_INSANE_OUTPUT - assert(temperature >= 0); + // assert(temperature >= 0); #endif // ASSERT_INSANE_OUTPUT *output_stream << " S" << PrecisionedDouble{ 1, temperature } << new_line; if (extruder != current_extruder && always_write_active_tool) diff --git a/src/raft.cpp b/src/raft.cpp index 16134f55bc..384958afc4 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -113,7 +113,15 @@ coord_t Raft::getFillerLayerHeight() const coord_t normal_layer_height = mesh_group_settings.get("layer_height"); return normal_layer_height; } - return round_divide(getZdiffBetweenRaftAndLayer0(), getFillerLayerCount()); + + if (getFillerLayerCount() != 0) + { + return round_divide(getZdiffBetweenRaftAndLayer0(), getFillerLayerCount()); + } + else + { + return mesh_group_settings.get("layer_height"); + } } From 9bcfe21624ebe5ac0f3c4f6e9460bade96a0ba69 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 25 Sep 2023 13:49:16 +0200 Subject: [PATCH 540/656] Better prime tower raft and fixed supports into raft CURA-10993 --- include/PrimeTower.h | 1 + src/FffGcodeWriter.cpp | 4 +-- src/PrimeTower.cpp | 79 +++++++++++++++++++++++++++++++++++++----- src/gcodeExport.cpp | 2 +- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index c0e50685bf..c2909295d8 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -41,6 +41,7 @@ class PrimeTower std::vector pattern_per_extruder; //!< For each extruder the pattern to print on all layers of the prime tower. std::vector pattern_per_extruder_layer0; //!< For each extruder the pattern to print on the first layer + std::vector pattern_per_extruder_layer_raft; //!< For each extruder the pattern to print on the raft layers public: bool enabled; //!< Whether the prime tower is enabled. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 8f7d993811..bd29bcef24 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -163,8 +163,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep run_multiple_producers_ordered_consumer( process_layer_starting_layer_nr, //-Raft::getTotalExtraLayers(), - total_layers + Raft::getFillerLayerCount() - 1, - // total_layers, + // total_layers + Raft::getFillerLayerCount() - 1, + total_layers, [&storage, total_layers, this](int layer_nr) { return &processLayer(storage, layer_nr, total_layers); diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 9845eff054..b0bf9e853e 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -117,6 +117,7 @@ void PrimeTower::generatePaths_denseInfill() const coord_t layer_height = mesh_group_settings.get("layer_height"); pattern_per_extruder.resize(extruder_count); pattern_per_extruder_layer0.resize(extruder_count); + pattern_per_extruder_layer_raft.resize(extruder_count); coord_t cumulative_inset = 0; // Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. for (size_t extruder_nr : extruder_order) @@ -150,7 +151,7 @@ void PrimeTower::generatePaths_denseInfill() { // Generate the pattern for the first layer. const coord_t line_width_layer0 = scene.extruders[extruder_nr].settings.get("raft_base_line_width"); - ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0[extruder_nr]; + ExtrusionMoves& pattern_layer_raft = pattern_per_extruder_layer_raft[extruder_nr]; // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the @@ -158,16 +159,29 @@ void PrimeTower::generatePaths_denseInfill() Polygons inset = outer_poly.offset(-cumulative_inset - line_width_layer0 / 2); while (! inset.empty()) { - pattern_layer0.polygons.add(inset); + pattern_layer_raft.polygons.add(inset); inset = inset.offset(-line_width_layer0); } Polygons outset = outer_poly.offset(cumulative_inset + line_width_layer0 / 2); for (int i = 0; i < 15; ++i) { - pattern_layer0.polygons.add(outset); + pattern_layer_raft.polygons.add(outset); outset = outset.offset(line_width_layer0); } + + // Generate the pattern for the raft layers + ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0[extruder_nr]; + + // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using + // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the + // first layer of the prime tower to not stick well. + outset = outer_poly.offset(cumulative_inset + line_width / 2); + for (int i = 0; i < 15; ++i) + { + pattern_layer0.polygons.add(outset); + outset = outset.offset(line_width); + } } cumulative_inset += wall_nr * line_width; } @@ -235,24 +249,71 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext const GCodePathConfig& config = gcode_layer.getLayerNr() < 0 ? gcode_layer.configs_storage.raft_base_config : gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - - // if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) - if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) + if (gcode_layer.getLayerNr() == -Raft::getTotalExtraLayers() && extruder_nr == 0) { + // Specific case for first layer => very high adhesion const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; - const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); - gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); + const ExtrusionMoves& pattern_raft = pattern_per_extruder_layer_raft[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern_raft.polygons, config); + gcode_layer.addLinesByOptimizer(pattern_raft.lines, config, SpaceFillType::Lines); } else { const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + // Actual prime pattern + const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); + gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + + if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) + { + const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); + gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); + } + } + + /* + if (gcode_layer.getLayerNr() > -static_cast(Raft::getTotalExtraLayers())) + { + const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + + // Actual prime pattern const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + + if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) + { + const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); + gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); + } } +*/ + + // if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) + /*if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) + { + if (gcode_layer.getLayerNr() == -Raft::getTotalExtraLayers()) + { + const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; + + const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); + gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); + } + else + { + const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + + const ExtrusionMoves& pattern_raft = pattern_per_extruder_layer_raft[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern_raft.polygons, config); + gcode_layer.addLinesByOptimizer(pattern_raft.lines, config, SpaceFillType::Lines); + } + }*/ } void PrimeTower::subtractFromSupport(SliceDataStorage& storage) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 799ac84e0c..caff200811 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1498,7 +1498,7 @@ void GCodeExport::writeTemperatureCommand(const size_t extruder, const Temperatu *output_stream << " T" << extruder; } #ifdef ASSERT_INSANE_OUTPUT - // assert(temperature >= 0); + assert(temperature >= 0); #endif // ASSERT_INSANE_OUTPUT *output_stream << " S" << PrecisionedDouble{ 1, temperature } << new_line; if (extruder != current_extruder && always_write_active_tool) From 1038eda887fa9b5b3b0158bdd104b61b53e944d7 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Mon, 25 Sep 2023 11:49:57 +0000 Subject: [PATCH 541/656] Applied clang-format. --- include/PrimeTower.h | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index c2909295d8..4bca473177 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -1,15 +1,15 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef PRIME_TOWER_H #define PRIME_TOWER_H -#include - #include "utils/polygon.h" // Polygons #include "utils/polygonUtils.h" -namespace cura +#include + +namespace cura { class SliceDataStorage; @@ -73,7 +73,7 @@ class PrimeTower /*! * Generate the prime tower area to be used on each layer - * + * * Fills \ref PrimeTower::inner_poly and sets \ref PrimeTower::middle */ void generateGroundpoly(); @@ -85,7 +85,7 @@ class PrimeTower /*! * Add path plans for the prime tower to the \p gcode_layer - * + * * \param storage where to get settings from; where to get the maximum height of the prime tower from * \param[in,out] gcode_layer Where to get the current extruder from; where to store the generated layer paths * \param prev_extruder The previous extruder with which paths were planned; from which extruder a switch was made @@ -102,10 +102,9 @@ class PrimeTower void subtractFromSupport(SliceDataStorage& storage); private: - /*! * \see WipeTower::generatePaths - * + * * Generate the extrude paths for each extruder on even and odd layers * Fill the ground poly with dense infill. */ @@ -139,8 +138,6 @@ class PrimeTower }; - - -}//namespace cura +} // namespace cura #endif // PRIME_TOWER_H From 43955a199c60077b74012d9a47297c694e5c2d91 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 25 Sep 2023 13:53:00 +0200 Subject: [PATCH 542/656] Support brim printed only at layer 0 CURA-10968 --- src/FffGcodeWriter.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..0721bd7628 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,24 +1232,28 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer + //support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + if (layer_nr == 0) { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {}); + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + { + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); + } } } From 26c7522af035fc5e629f72b398be78f8541f0a1f Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Mon, 25 Sep 2023 11:53:42 +0000 Subject: [PATCH 543/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 0721bd7628..ef5d748533 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,7 +1232,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer - //support brim is only added in layer 0 + // support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. if (layer_nr == 0) { From e2203b07fd02668c1c7010c596ef84f6ff766b69 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 25 Sep 2023 13:55:28 +0200 Subject: [PATCH 544/656] Comment fix CURA-10968 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index ef5d748533..68591de295 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,7 +1232,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer - // support brim is only added in layer 0 + // Support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. if (layer_nr == 0) { From 321d564cda3865c4be12f8c29ccaeac9219a535f Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 25 Sep 2023 14:53:50 +0200 Subject: [PATCH 545/656] Group outer wall order For the gradual flow we do want to group the outer walls in order to limit the number of flow changes. CURA-11082 --- src/InsetOrderOptimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 006296fc68..86305d27b5 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -93,7 +93,7 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool detect_loops = false; constexpr Polygons* combing_boundary = nullptr; - constexpr bool group_outer_walls = false; + const auto group_outer_walls = false; // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. PathOrderOptimizer From ec8bcfc3ad346fa21dde94b188caa84e18fcae06 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 25 Sep 2023 15:05:24 +0200 Subject: [PATCH 546/656] Add setting to group outer walls CURA-11082 --- src/InsetOrderOptimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 86305d27b5..05b1fa3f3f 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -93,7 +93,7 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool detect_loops = false; constexpr Polygons* combing_boundary = nullptr; - const auto group_outer_walls = false; + const auto group_outer_walls = settings.get("group_outer_walls"); // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. PathOrderOptimizer From ec98763e4218ba9fd991d8396e1d4565d6116913 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 25 Sep 2023 16:45:49 +0200 Subject: [PATCH 547/656] Fix bad temperature crash CURA-10993 --- src/FffGcodeWriter.cpp | 12 ++++++------ src/PrimeTower.cpp | 40 ---------------------------------------- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index bd29bcef24..7d96e81eb6 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -685,10 +685,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raftLines.clear(); } + setExtruder_addPrime(storage, gcode_layer, 0); + layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); - - setExtruder_addPrime(storage, gcode_layer, 0); } const coord_t interface_layer_height = interface_settings.get("raft_interface_thickness"); @@ -790,10 +790,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_lines.clear(); } + setExtruder_addPrime(storage, gcode_layer, 0); + layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); - - setExtruder_addPrime(storage, gcode_layer, 0); } const coord_t surface_layer_height = surface_settings.get("raft_surface_thickness"); @@ -897,9 +897,9 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_lines.clear(); } - layer_plan_buffer.handle(gcode_layer, gcode); - setExtruder_addPrime(storage, gcode_layer, 0); + + layer_plan_buffer.handle(gcode_layer, gcode); } } diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index b0bf9e853e..3818d8e3f8 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -274,46 +274,6 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); } } - - /* - if (gcode_layer.getLayerNr() > -static_cast(Raft::getTotalExtraLayers())) - { - const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - - // Actual prime pattern - const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); - gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); - - if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) - { - const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); - gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); - } - } -*/ - - // if (gcode_layer.getLayerNr() == -static_cast(Raft::getTotalExtraLayers())) - /*if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) - { - if (gcode_layer.getLayerNr() == -Raft::getTotalExtraLayers()) - { - const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; - - const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); - gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); - } - else - { - const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - - const ExtrusionMoves& pattern_raft = pattern_per_extruder_layer_raft[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern_raft.polygons, config); - gcode_layer.addLinesByOptimizer(pattern_raft.lines, config, SpaceFillType::Lines); - } - }*/ } void PrimeTower::subtractFromSupport(SliceDataStorage& storage) From d78bb6de2d5e85d054b7b46051661a98e75cd602 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 26 Sep 2023 19:59:27 +0200 Subject: [PATCH 548/656] Add missing 'perform_prime' member-variable. Note that this needs the updated GRPC definitions as well! -- This caused that variable to always be (re)set to false, even when no plugin was present, due to the 'identity' conversion taking place. (Maybe we should stop that in general, since that implies that we copy the data even when no plugin is present.) In any case, even if it wouldn't have caused this particular bug, it's an oversight that should be fixed anyway. should fix CURA-11084 --- src/plugins/converters.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 6e93f34afd..72a8166de7 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -345,6 +345,7 @@ gcode_paths_modify_request::value_type gcode_path->set_retract(path.retract); gcode_path->set_unretract_before_last_travel_move(path.unretract_before_last_travel_move); gcode_path->set_perform_z_hop(path.perform_z_hop); + gcode_path->set_perform_prime(path.perform_prime); gcode_path->set_skip_agressive_merge_hint(path.skip_agressive_merge_hint); gcode_path->set_done(path.done); gcode_path->set_fan_speed(path.getFanSpeed()); @@ -458,6 +459,7 @@ gcode_paths_modify_response::native_value_type .retract = gcode_path_msg.retract(), .unretract_before_last_travel_move = gcode_path_msg.unretract_before_last_travel_move(), .perform_z_hop = gcode_path_msg.perform_z_hop(), + .perform_prime = gcode_path_msg.perform_prime(), .skip_agressive_merge_hint = gcode_path_msg.skip_agressive_merge_hint(), .done = gcode_path_msg.done(), .fan_speed = gcode_path_msg.fan_speed(), From b996cd013d083fb6ca3b19a8ad5ea0952e2fe0be Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 26 Sep 2023 21:15:38 +0200 Subject: [PATCH 549/656] Proof-of-concept. Fix partial-layer-height difference for (support) line-fill. Part of the partial layer-height support distance proof of concept: CURA-11041 --- src/FffGcodeWriter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9361f1ff10..1fab3e3005 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3375,6 +3375,9 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) { + current_roof_config.z_offset = -leftover_support_distance; + current_roof_config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); + continue; // We didn't create any support roof. } generated_something = true; // We _did_ create at least some support roof. @@ -3417,7 +3420,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay } gcode_layer.addLinesByOptimizer( roof_lines, - gcode_layer.configs_storage.support_roof_config, + current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); current_roof_config.z_offset = -leftover_support_distance; From 185a2211d1ad1a95a28c046f7634bb23e9e2284d Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 26 Sep 2023 19:16:28 +0000 Subject: [PATCH 550/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1fab3e3005..e497b08a1f 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3418,10 +3418,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay roof_paths); wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer( - roof_lines, - current_roof_config, - (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); current_roof_config.z_offset = -leftover_support_distance; current_roof_config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); From e9f015de3a91ddcdeefe9dacfd49495724134356 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 27 Sep 2023 15:36:29 +0200 Subject: [PATCH 551/656] Added a foot to prime tower to make it stronger CURA-10993 --- include/PrimeTower.h | 5 +- src/FffGcodeWriter.cpp | 6 +-- src/PrimeTower.cpp | 109 +++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 59 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 4bca473177..5bb8192a82 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -40,8 +40,7 @@ class PrimeTower const unsigned int number_of_prime_tower_start_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations std::vector pattern_per_extruder; //!< For each extruder the pattern to print on all layers of the prime tower. - std::vector pattern_per_extruder_layer0; //!< For each extruder the pattern to print on the first layer - std::vector pattern_per_extruder_layer_raft; //!< For each extruder the pattern to print on the raft layers + std::vector pattern_extra_brim_per_layer; //!< For each layer with an extra brim, the pattern to be added public: bool enabled; //!< Whether the prime tower is enabled. @@ -102,6 +101,8 @@ class PrimeTower void subtractFromSupport(SliceDataStorage& storage); private: + ExtrusionMoves generatePaths_extraBrim(const Polygons &outer_poly, coord_t extra_radius, coord_t line_width, bool add_inset); + /*! * \see WipeTower::generatePaths * diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 7d96e81eb6..7585677793 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -685,7 +685,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raftLines.clear(); } - setExtruder_addPrime(storage, gcode_layer, 0); + setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); @@ -790,7 +790,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_lines.clear(); } - setExtruder_addPrime(storage, gcode_layer, 0); + setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); @@ -897,7 +897,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_lines.clear(); } - setExtruder_addPrime(storage, gcode_layer, 0); + setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); layer_plan_buffer.handle(gcode_layer, gcode); } diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 3818d8e3f8..472edc4054 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -110,14 +110,45 @@ void PrimeTower::generatePaths(const SliceDataStorage& storage) } } +PrimeTower::ExtrusionMoves PrimeTower::generatePaths_extraBrim(const Polygons& outer_poly, coord_t extra_radius, coord_t line_width, bool add_inset) +{ + const Scene& scene = Application::getInstance().current_slice->scene; + const Settings& mesh_group_settings = scene.current_mesh_group->settings; + const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); + + ExtrusionMoves pattern; + + if (add_inset) + { + Polygons inset = outer_poly.offset(-line_width / 2); + while (! inset.empty()) + { + pattern.polygons.add(inset); + inset = inset.offset(-line_width); + } + } + + int circles = 0; + Polygons outset = outer_poly.offset(line_width / 2); + while (outset.max() - outset.min() < (tower_size + extra_radius * 2)) + { + pattern.polygons.add(outset); + outset = outset.offset(line_width); + circles++; + } + + return pattern; +} + void PrimeTower::generatePaths_denseInfill() { + constexpr coord_t brim_extra_radius = 20000; + constexpr coord_t brim_extra_height = 20000; + const Scene& scene = Application::getInstance().current_slice->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); pattern_per_extruder.resize(extruder_count); - pattern_per_extruder_layer0.resize(extruder_count); - pattern_per_extruder_layer_raft.resize(extruder_count); coord_t cumulative_inset = 0; // Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. for (size_t extruder_nr : extruder_order) @@ -143,44 +174,19 @@ void PrimeTower::generatePaths_denseInfill() } // Only the most inside extruder needs to fill the inside of the prime tower - if (false /*extruder_nr != extruder_order.back()*/) - { - pattern_per_extruder_layer0 = pattern_per_extruder; - } - else + if (extruder_nr == extruder_order.front()) { // Generate the pattern for the first layer. const coord_t line_width_layer0 = scene.extruders[extruder_nr].settings.get("raft_base_line_width"); - ExtrusionMoves& pattern_layer_raft = pattern_per_extruder_layer_raft[extruder_nr]; - - // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using - // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the - // first layer of the prime tower to not stick well. - Polygons inset = outer_poly.offset(-cumulative_inset - line_width_layer0 / 2); - while (! inset.empty()) - { - pattern_layer_raft.polygons.add(inset); - inset = inset.offset(-line_width_layer0); - } - - Polygons outset = outer_poly.offset(cumulative_inset + line_width_layer0 / 2); - for (int i = 0; i < 15; ++i) - { - pattern_layer_raft.polygons.add(outset); - outset = outset.offset(line_width_layer0); - } + ExtrusionMoves pattern_layer0 = generatePaths_extraBrim(outer_poly, brim_extra_radius, line_width_layer0, true); + pattern_extra_brim_per_layer.push_back(pattern_layer0); - // Generate the pattern for the raft layers - ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0[extruder_nr]; - - // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using - // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the - // first layer of the prime tower to not stick well. - outset = outer_poly.offset(cumulative_inset + line_width / 2); - for (int i = 0; i < 15; ++i) + for (coord_t z = layer_height; z < brim_extra_height; z += layer_height) { - pattern_layer0.polygons.add(outset); - outset = outset.offset(line_width); + double brim_radius_factor = std::pow((1.0 - static_cast(z) / brim_extra_height), 4); + coord_t extra_radius = brim_extra_radius * brim_radius_factor; + ExtrusionMoves pattern = generatePaths_extraBrim(outer_poly, extra_radius, line_width, false); + pattern_extra_brim_per_layer.push_back(pattern); } } cumulative_inset += wall_nr * line_width; @@ -246,33 +252,28 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const { - const GCodePathConfig& config - = gcode_layer.getLayerNr() < 0 ? gcode_layer.configs_storage.raft_base_config : gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + Raft::getTotalExtraLayers(); + assert(absolute_layer_number >= 0); - if (gcode_layer.getLayerNr() == -Raft::getTotalExtraLayers() && extruder_nr == 0) - { - // Specific case for first layer => very high adhesion - const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; - - const ExtrusionMoves& pattern_raft = pattern_per_extruder_layer_raft[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern_raft.polygons, config); - gcode_layer.addLinesByOptimizer(pattern_raft.lines, config, SpaceFillType::Lines); - } - else + if (absolute_layer_number > 0) { + // Actual prime pattern const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - // Actual prime pattern const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + } - if (gcode_layer.getLayerNr() < 0 && extruder_nr == 0) - { - const ExtrusionMoves& pattern0 = pattern_per_extruder_layer0[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern0.polygons, config); - gcode_layer.addLinesByOptimizer(pattern0.lines, config, SpaceFillType::Lines); - } + if (absolute_layer_number < pattern_extra_brim_per_layer.size() && extruder_nr == extruder_order.front()) + { + // Specific case for first layer => very high adhesion + const GCodePathConfig& config + = absolute_layer_number == 0 ? gcode_layer.configs_storage.raft_base_config : gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + + const ExtrusionMoves& pattern = pattern_extra_brim_per_layer[absolute_layer_number]; + gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); + gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); } } From f832ec814da6d7d0e817460f65b4bfa74d13f5bb Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 29 Sep 2023 11:32:03 +0200 Subject: [PATCH 552/656] Comment fix CURA-10968 --- src/FffGcodeWriter.cpp | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 68591de295..9a404fa292 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1234,26 +1234,23 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer // Support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. - if (layer_nr == 0) + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if ((layer_nr == 0) && (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr)) { - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) - { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {}); - } + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); } } From f063b5ab7df6747b7c3092beec8ea90d042aa05b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 3 Oct 2023 12:24:18 +0200 Subject: [PATCH 553/656] Reverted the changes done in CURA-9521 CURA-11109 --- include/FffGcodeWriter.h | 3 +-- include/infill.h | 7 ++----- src/FffGcodeWriter.cpp | 9 +++------ src/infill.cpp | 10 ++++------ 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 0e3dea645c..96f504c1e3 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -569,8 +569,7 @@ class FffGcodeWriter : public NoCopy const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, - const bool is_bridge_skin = false) const; + double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to diff --git a/include/infill.h b/include/infill.h index 40ca7fbd59..289a0850bb 100644 --- a/include/infill.h +++ b/include/infill.h @@ -206,8 +206,7 @@ class Infill const std::shared_ptr& cross_fill_provider = nullptr, const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr, - const Polygons& prevent_small_exposed_to_air = Polygons(), - const bool is_bridge_skin = false); + const Polygons& prevent_small_exposed_to_air = Polygons()); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -219,7 +218,6 @@ class Infill * \param line_width [in] The optimum wall line width of the walls * \param infill_overlap [in] The overlap of the infill * \param settings [in] A settings storage to use for generating variable-width walls. - * \param is_bridge_skin [in] Setting to filter out the extra skin walls while bridging * \return The inner contour of the wall toolpaths */ static Polygons generateWallToolPaths( @@ -230,8 +228,7 @@ class Infill const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type, - const bool is_bridge_skin = false); + SectionType section_type); private: /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..2af62a6824 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2727,8 +2727,7 @@ void FffGcodeWriter::processTopBottom( skin_density, monotonic, added_something, - fan_speed, - is_bridge_skin); + fan_speed); } void FffGcodeWriter::processSkinPrintFeature( @@ -2745,8 +2744,7 @@ void FffGcodeWriter::processSkinPrintFeature( const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed, - const bool is_bridge_skin) const + double fan_speed) const { Polygons skin_polygons; Polygons skin_lines; @@ -2806,8 +2804,7 @@ void FffGcodeWriter::processSkinPrintFeature( nullptr, nullptr, nullptr, - small_areas_on_surface ? Polygons() : exposed_to_air, - is_bridge_skin); + small_areas_on_surface ? Polygons() : exposed_to_air); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) diff --git a/src/infill.cpp b/src/infill.cpp index 9137aa66ca..6da3c25c4f 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -60,14 +60,13 @@ Polygons Infill::generateWallToolPaths( const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type, - const bool is_bridge_skin) + SectionType section_type) { outer_contour = outer_contour.offset(infill_overlap); scripta::log("infill_outer_contour", outer_contour, section_type, layer_idx, scripta::CellVDI{ "infill_overlap", infill_overlap }); Polygons inner_contour; - if ((wall_line_count > 0) && (! is_bridge_skin)) + if (wall_line_count > 0) { constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. WallToolPaths wall_toolpaths(outer_contour, line_width, wall_line_count, wall_0_inset, settings, layer_idx, section_type); @@ -91,15 +90,14 @@ void Infill::generate( const std::shared_ptr& cross_fill_provider, const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh, - const Polygons& prevent_small_exposed_to_air, - const bool is_bridge_skin) + const Polygons& prevent_small_exposed_to_air) { if (outer_contour.empty()) { return; } - inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type, is_bridge_skin); + inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type); scripta::log("infill_inner_contour_0", inner_contour, section_type, layer_idx); // It does not make sense to print a pattern in a small region. So the infill region From 873800b9a36cb736a5c21acfbbc1e436c9bbab87 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 3 Oct 2023 19:39:58 +0200 Subject: [PATCH 554/656] Use correct path config CURA-11119 CURA-11121 --- src/FffGcodeWriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..fd80cd7d04 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2833,10 +2833,10 @@ void FffGcodeWriter::processSkinPrintFeature( gcode_layer, mesh.settings, extruder_nr, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, + config, + config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, From a70793c1a98825020a66d3d72176ad5412cf5868 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Oct 2023 09:54:12 +0200 Subject: [PATCH 555/656] Change seam position scoring to make it more consistent CURA-11100 --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 93198fcd19..464810cefa 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -646,7 +646,7 @@ class PathOrderOptimizer // so the user has some control over where the seam will lie. // the divisor here may need adjusting to obtain the best results (TBD) - corner_shift = score_distance / 10; + corner_shift = score_distance / 50; } float score = score_distance; From 3bce9f8c2eb62b581cd18aa9c9a273616a3af439 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 5 Oct 2023 11:48:49 +0200 Subject: [PATCH 556/656] Remove unused variable boyscouting CURA-11110 --- src/skin.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/skin.cpp b/src/skin.cpp index 227591a8c4..28fc60c238 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -346,8 +346,6 @@ void SkinInfillAreaComputation::generateRoofingFillAndSkinFill(SliceLayerPart& p */ Polygons SkinInfillAreaComputation::generateFilledAreaAbove(SliceLayerPart& part, size_t roofing_layer_count) { - const size_t wall_idx = std::min(size_t(2), mesh.settings.get("wall_line_count")); - Polygons filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); if (! no_small_gaps_heuristic) { From 3787b2c73369052182ea2b359f372f38f5dc5fe5 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 5 Oct 2023 12:45:17 +0200 Subject: [PATCH 557/656] Use different config for roofing inner/outer walls Implementation is not fully desired since it has some downsides 1. When a layer-part is partially a roof the whole outer/inner wall uses the inner/outer wall roofing print-configuration 2. Some logic is duplicated, namely the function that calculates the `filled_area_above`. This function previously lived in `SkinInfillAreaComputation`. It's not logical to create a `SkinInfillAreaComputation` instance just to re-use this utility. Proposal to fix this is to move the logic of calculating the `filled_area_above` to a more central location, this will be done in a seperate ticket. 3. The `inset0_roofing_config` and `insetX_roofing_config` contain `line_width` properties. Changing the line-widths here doesn't actually change the line width. The line widths can only be changed through the `insetX_config` and `inset0_config` configs CURA-11110 --- include/settings/MeshPathConfigs.h | 2 + src/FffGcodeWriter.cpp | 96 +++++++++++++++++++++++++++++- src/settings/MeshPathConfigs.cpp | 26 ++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/include/settings/MeshPathConfigs.h b/include/settings/MeshPathConfigs.h index 43457fbc8c..61c993b9a8 100644 --- a/include/settings/MeshPathConfigs.h +++ b/include/settings/MeshPathConfigs.h @@ -15,6 +15,8 @@ struct MeshPathConfigs { GCodePathConfig inset0_config{}; GCodePathConfig insetX_config{}; + GCodePathConfig inset0_roofing_config{}; + GCodePathConfig insetX_roofing_config{}; GCodePathConfig bridge_inset0_config{}; GCodePathConfig bridge_insetX_config{}; GCodePathConfig skin_config{}; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe5ebfcc08..5cde325a69 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -21,6 +21,8 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include +#include #include #include @@ -2392,6 +2394,96 @@ bool FffGcodeWriter::processInsets( } else { + // for layers that (partially) do not have any layers above we apply the roofing configuration + auto use_roofing_config = [&part, &mesh, &gcode_layer](){ + const auto getOutlineOnLayer = [mesh](const SliceLayerPart& part_here, const LayerIndex layer2_nr) -> Polygons + { + Polygons result; + if (layer2_nr >= static_cast(mesh.layers.size())) + { + return result; + } + const SliceLayer& layer2 = mesh.layers[layer2_nr]; + for (const SliceLayerPart& part2 : layer2.parts) + { + if (part_here.boundaryBox.hit(part2.boundaryBox)) + { + result.add(part2.outline); + } + } + return result; + }; + + const auto filled_area_above = [&getOutlineOnLayer, &part, &mesh, &gcode_layer]() -> Polygons + { + const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); + const bool no_small_gaps_heuristic = mesh.settings.get("skin_no_small_gaps_heuristic"); + const int layer_nr = gcode_layer.getLayerNr(); + auto filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); + if (! no_small_gaps_heuristic) + { + for (int layer_nr_above = layer_nr + 1; layer_nr_above < layer_nr + roofing_layer_count; layer_nr_above++) + { + Polygons outlines_above = getOutlineOnLayer(part, layer_nr_above); + filled_area_above = filled_area_above.intersection(outlines_above); + } + } + if (layer_nr > 0) + { + // if the skin has air below it then cutting it into regions could cause a region + // to be wholely or partly above air and it may not be printable so restrict + // the regions that have air above (the visible regions) to not include any area that + // has air below (fixes https://github.com/Ultimaker/Cura/issues/2656) + + // set air_below to the skin area for the current layer that has air below it + Polygons air_below = getOutlineOnLayer(part, layer_nr).difference(getOutlineOnLayer(part, layer_nr - 1)); + + if (! air_below.empty()) + { + // add the polygons that have air below to the no air above polygons + filled_area_above = filled_area_above.unionPolygons(air_below); + } + } + + return filled_area_above; + }(); + + if (filled_area_above.empty()) + { + return true; + } + + const auto point_view = ranges::views::transform([](auto extrusion_junction) { return extrusion_junction.p; }); + + for (const auto& path: part.wall_toolpaths) + { + for (const auto& wall: path) + { + for (const auto& p : wall | point_view) + { + if (!filled_area_above.inside(p)) + { + return true; + } + } + + for (const auto& window : wall | point_view | ranges::views::sliding(2)) + { + auto p0 = window[0]; + auto p1 = window[1]; + if (PolygonUtils::polygonCollidesWithLineSegment(filled_area_above, p0, p1)) + { + return true; + } + } + } + } + return false; + }(); + + const GCodePathConfig& inset0_config = use_roofing_config ? mesh_config.inset0_roofing_config : mesh_config.inset0_config; + const GCodePathConfig& insetX_config = use_roofing_config ? mesh_config.insetX_roofing_config : mesh_config.insetX_config; + // Main case: Optimize the insets with the InsetOrderOptimizer. const coord_t wall_x_wipe_dist = 0; const ZSeamConfig z_seam_config( @@ -2405,8 +2497,8 @@ bool FffGcodeWriter::processInsets( gcode_layer, mesh.settings, extruder_nr, - mesh_config.inset0_config, - mesh_config.insetX_config, + inset0_config, + insetX_config, mesh_config.bridge_inset0_config, mesh_config.bridge_insetX_config, mesh.settings.get("travel_retract_before_outer_wall"), diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index 942084a231..a89b444bd3 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -28,6 +28,32 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } + , inset0_roofing_config + { + .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { + .speed = mesh.settings.get("speed_wall_0_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), + .jerk = mesh.settings.get("jerk_wall_0_roofing") + } + } + , insetX_roofing_config + { + .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { + .speed = mesh.settings.get("speed_wall_x_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), + .jerk = mesh.settings.get("jerk_wall_x_roofing") + } + } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") From 08dd09abf6ef354c5581fc68be8c2943ce3e06bd Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 5 Oct 2023 10:47:34 +0000 Subject: [PATCH 558/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 15 +++++++---- src/settings/MeshPathConfigs.cpp | 46 ++++++++++++++------------------ 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 5cde325a69..1c592da8ad 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2395,7 +2395,8 @@ bool FffGcodeWriter::processInsets( else { // for layers that (partially) do not have any layers above we apply the roofing configuration - auto use_roofing_config = [&part, &mesh, &gcode_layer](){ + auto use_roofing_config = [&part, &mesh, &gcode_layer]() + { const auto getOutlineOnLayer = [mesh](const SliceLayerPart& part_here, const LayerIndex layer2_nr) -> Polygons { Polygons result; @@ -2453,15 +2454,19 @@ bool FffGcodeWriter::processInsets( return true; } - const auto point_view = ranges::views::transform([](auto extrusion_junction) { return extrusion_junction.p; }); + const auto point_view = ranges::views::transform( + [](auto extrusion_junction) + { + return extrusion_junction.p; + }); - for (const auto& path: part.wall_toolpaths) + for (const auto& path : part.wall_toolpaths) { - for (const auto& wall: path) + for (const auto& wall : path) { for (const auto& p : wall | point_view) { - if (!filled_area_above.inside(p)) + if (! filled_area_above.inside(p)) { return true; } diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index a89b444bd3..aa71d89e07 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -28,32 +28,26 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } - , inset0_roofing_config - { - .type = PrintFeatureType::OuterWall, - .line_width = static_cast( - mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), - .speed_derivatives = { - .speed = mesh.settings.get("speed_wall_0_roofing"), - .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), - .jerk = mesh.settings.get("jerk_wall_0_roofing") - } - } - , insetX_roofing_config - { - .type = PrintFeatureType::OuterWall, - .line_width = static_cast( - mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), - .speed_derivatives = { - .speed = mesh.settings.get("speed_wall_x_roofing"), - .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), - .jerk = mesh.settings.get("jerk_wall_x_roofing") - } - } + , inset0_roofing_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") + * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), + .jerk = mesh.settings.get("jerk_wall_0_roofing") } } + , insetX_roofing_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") + * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), + .jerk = mesh.settings.get("jerk_wall_x_roofing") } } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") From d24323d14ba1ed4f8d90e17cf35956ca8a575b48 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 5 Oct 2023 15:17:34 +0200 Subject: [PATCH 559/656] the length of brim is correctly calculated in case of no adhesion CURA-11097 --- src/FffGcodeWriter.cpp | 1 - src/SkirtBrim.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe5ebfcc08..300ea26147 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3050,7 +3050,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } island_order_optimizer.optimize(); - const auto support_brim_line_count = infill_extruder.settings.get("support_brim_line_count"); const auto support_connect_zigzags = infill_extruder.settings.get("support_connect_zigzags"); const auto support_structure = infill_extruder.settings.get("support_structure"); const Point infill_origin; diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 6aed96d7fe..2c1c134d3c 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); - const coord_t length = skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE)? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 65ad30f6630b5d65e4e4905dae5c96eee57ac79b Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Thu, 5 Oct 2023 13:18:20 +0000 Subject: [PATCH 560/656] Applied clang-format. --- src/SkirtBrim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 2c1c134d3c..98992c67d9 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE)? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 54389a88290b86aacc9c162ca1e3339f6a0edd1a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Oct 2023 17:18:08 +0200 Subject: [PATCH 561/656] Better version of the corner angle computation (to be optimized) CURA-11100 --- include/PathOrderOptimizer.h | 114 +++++++++++++++++------------------ src/FffGcodeWriter.cpp | 2 +- 2 files changed, 56 insertions(+), 60 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 464810cefa..b269b1e063 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -136,7 +136,7 @@ class PathOrderOptimizer * This reorders the \ref paths field and fills their starting vertices and * directions. */ - void optimize() + void optimize(bool precompute_start = true) { if(paths.empty()) { @@ -188,7 +188,7 @@ class PathOrderOptimizer //For some Z seam types the start position can be pre-computed. //This is faster since we don't need to re-compute the start position at each step then. - const bool precompute_start = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; + precompute_start &= seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; if(precompute_start) { for(auto& path : paths) @@ -656,13 +656,13 @@ class PathOrderOptimizer case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: if(corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. { - score -= (-corner_angle + 1.0) * corner_shift; + score += corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: if(corner_angle > 0) // Indeed a convex corner? { - score -= (corner_angle + 1.0) * corner_shift; + score -= corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: @@ -707,6 +707,43 @@ class PathOrderOptimizer return best_i; } + Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance) + { + assert(distance); + + const Point here_pos = (*path.converted)[here]; + int direction = distance > 0 ? 1 : -1; + distance = std::abs(distance); + + coord_t actual_distance = 0; + int actual_delta = 0; + + Point prev_pos = here_pos; + Point next_pos; + while(actual_distance < distance) + { + actual_delta += direction; + next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + actual_distance += vSize(next_pos - prev_pos); + prev_pos = next_pos; + } + + if(actual_distance > distance) // Which is veeeery likely + { + prev_pos = (*path.converted)[(here + actual_delta -direction + path.converted->size()) % path.converted->size()]; + + Point vector = next_pos - prev_pos; + coord_t vector_size = vSize(vector); + Point unit_vector = (vector * 1000) / vector_size; + Point vector_delta = unit_vector * (vector_size - (actual_distance - distance)); + return prev_pos + vector_delta / 1000; + } + else + { + return next_pos; + } + } + /*! * Some models have very sharp corners, but also have a high resolution. If a sharp corner * consists of many points each point individual might have a shallow corner, but the @@ -722,68 +759,27 @@ class PathOrderOptimizer */ float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) { - // If the edge length becomes too small we cannot accurately calculate the angle - // define a minimum edge length, so we don't get deviant values in the angle calculations - constexpr coord_t min_edge_length = 10; - constexpr coord_t min_edge_length2 = min_edge_length * min_edge_length; + static constexpr coord_t distance_step = 2000; + static constexpr coord_t max_distance = 5000; - const int offset_index = i % path.converted->size(); - Point here = (*path.converted)[offset_index]; + const Point here = (*path.converted)[i]; - const std::function find_neighbour_point = [&offset_index, &path](const int direction, const Point& here) - { - int offset_index_ = offset_index; - Point neighbour; - do - { - offset_index_ = (offset_index_ + path.converted->size() + direction) % path.converted->size(); - neighbour = (*path.converted)[offset_index_]; - } - while (vSize2(here - neighbour) < min_edge_length2 && offset_index_ != offset_index); // find previous point that is at least min_edge_length units away from here - return neighbour; - }; - - const std::function iterate_to_previous_point = [&find_neighbour_point](Point& previous_, Point& here_, Point& next_) - { - const auto dist = vSize(here_ - next_); - next_ = here_; - here_ = previous_; - previous_ = find_neighbour_point(-1, here_); - return dist; - }; - Point previous = find_neighbour_point(-1, here); + float angle = 0.0; + int computed_angles = 0; - const std::function iterate_to_next_point = [&find_neighbour_point](Point& previous_, Point& here_, Point& next_) + for(coord_t distance = distance_step ; distance <= max_distance ; distance += distance_step) { - const auto dist = vSize(here_ - previous_); - previous_ = here_; - here_ = next_; - next_ = find_neighbour_point(1, here_); - return dist; - }; - Point next = find_neighbour_point(1, here); + Point next = findNeighbourPoint(path, i, distance); + Point previous = findNeighbourPoint(path, i, -distance); - float corner_angle = LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - - for (const auto& iterate_func : {iterate_to_previous_point, iterate_to_next_point}) - { - Point next_ = next; - Point here_ = here; - Point previous_ = previous; - for - ( - coord_t distance_to_query = iterate_func(previous_, here_, next_); - distance_to_query < angle_query_distance && here_ != here; - distance_to_query += iterate_func(previous_, here_, next_) - ) - { - // angles further away from the query point are weighted less - const float angle_weight = 1.0 - pow(distance_to_query / angle_query_distance, fall_off_strength); - corner_angle += (LinearAlg2D::getAngleLeft(previous_, here_, next_) - M_PI) * angle_weight; - } + angle += LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; + computed_angles++; } - return corner_angle / M_PI; // Limit angle between -1 and 1. + angle /= computed_angles; + angle /= M_PI; + + return angle; } /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe5ebfcc08..07f961484e 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1493,7 +1493,7 @@ void FffGcodeWriter::addMeshLayerToGCode( { part_order_optimizer.addPolygon(&part); } - part_order_optimizer.optimize(); + part_order_optimizer.optimize(false); for (const PathOrdering& path : part_order_optimizer.paths) { addMeshPartToGCode(storage, mesh, extruder_nr, mesh_config, *path.vertices, gcode_layer); From 317dab031139972d9f07b2b6d11b9a2252c5409e Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Thu, 5 Oct 2023 15:18:51 +0000 Subject: [PATCH 562/656] Applied clang-format. --- include/PathOrderOptimizer.h | 302 +++++++++++++++++++---------------- 1 file changed, 160 insertions(+), 142 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index b269b1e063..939baf6b21 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -4,9 +4,6 @@ #ifndef PATHORDEROPTIMIZER_H #define PATHORDEROPTIMIZER_H -#include -#include - #include "InsetOrderOptimizer.h" // for makeOrderIncludeTransitive #include "PathOrdering.h" #include "pathPlanning/CombPath.h" //To calculate the combing distance if we want to use combing. @@ -16,12 +13,16 @@ #include "utils/linearAlg2D.h" //To find the angle of corners to hide seams. #include "utils/polygonUtils.h" #include "utils/views/dfs.h" -#include -#include + #include #include -#include #include +#include +#include +#include +#include + +#include namespace cura { @@ -99,10 +100,17 @@ class PathOrderOptimizer * it into a polygon. * \param combing_boundary Boundary to avoid when making travel moves. */ - PathOrderOptimizer(const Point start_point, const ZSeamConfig seam_config = ZSeamConfig(), const bool detect_loops = false, const Polygons* combing_boundary = nullptr, const bool reverse_direction = false, const std::unordered_multimap& order_requirements = no_order_requirements, const bool group_outer_walls = false) + PathOrderOptimizer( + const Point start_point, + const ZSeamConfig seam_config = ZSeamConfig(), + const bool detect_loops = false, + const Polygons* combing_boundary = nullptr, + const bool reverse_direction = false, + const std::unordered_multimap& order_requirements = no_order_requirements, + const bool group_outer_walls = false) : start_point(start_point) , seam_config(seam_config) - , combing_boundary((combing_boundary != nullptr && !combing_boundary->empty()) ? combing_boundary : nullptr) + , combing_boundary((combing_boundary != nullptr && ! combing_boundary->empty()) ? combing_boundary : nullptr) , detect_loops(detect_loops) , reverse_direction(reverse_direction) , order_requirements(&order_requirements) @@ -138,70 +146,70 @@ class PathOrderOptimizer */ void optimize(bool precompute_start = true) { - if(paths.empty()) + if (paths.empty()) { return; } - //Get the vertex data and store it in the paths. - for(auto& path : paths) + // Get the vertex data and store it in the paths. + for (auto& path : paths) { path.converted = path.getVertexData(); vertices_to_paths.emplace(path.vertices, &path); } - //If necessary, check polylines to see if they are actually polygons. - if(detect_loops) + // If necessary, check polylines to see if they are actually polygons. + if (detect_loops) { - for(auto& path : paths) + for (auto& path : paths) { - if(!path.is_closed) + if (! path.is_closed) { - //If we want to detect chains, first check if some of the polylines are secretly polygons. - path.is_closed = isLoopingPolyline(path); //If it is, we'll set the seam position correctly later. + // If we want to detect chains, first check if some of the polylines are secretly polygons. + path.is_closed = isLoopingPolyline(path); // If it is, we'll set the seam position correctly later. } } } - - //Add all vertices to a bucket grid so that we can find nearby endpoints quickly. + + // Add all vertices to a bucket grid so that we can find nearby endpoints quickly. const coord_t snap_radius = 10_mu; // 0.01mm grid cells. Chaining only needs to consider polylines which are next to each other. SparsePointGridInclusive line_bucket_grid(snap_radius); - for(const auto& [i, path]: paths | ranges::views::enumerate) + for (const auto& [i, path] : paths | ranges::views::enumerate) { if (path.converted->empty()) { continue; } - if(path.is_closed) + if (path.is_closed) { - for(const Point& point : *path.converted) + for (const Point& point : *path.converted) { - line_bucket_grid.insert(point, i); //Store by index so that we can also mark them down in the `picked` vector. + line_bucket_grid.insert(point, i); // Store by index so that we can also mark them down in the `picked` vector. } } - else //For polylines, only insert the endpoints. Those are the only places we can start from so the only relevant vertices to be near to. + else // For polylines, only insert the endpoints. Those are the only places we can start from so the only relevant vertices to be near to. { line_bucket_grid.insert(path.converted->front(), i); line_bucket_grid.insert(path.converted->back(), i); } } - //For some Z seam types the start position can be pre-computed. - //This is faster since we don't need to re-compute the start position at each step then. + // For some Z seam types the start position can be pre-computed. + // This is faster since we don't need to re-compute the start position at each step then. precompute_start &= seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; - if(precompute_start) + if (precompute_start) { - for(auto& path : paths) + for (auto& path : paths) { - if(!path.is_closed || path.converted->empty()) + if (! path.is_closed || path.converted->empty()) { - continue; //Can't pre-compute the seam for open polylines since they're at the endpoint nearest to the current position. + continue; // Can't pre-compute the seam for open polylines since they're at the endpoint nearest to the current position. } path.start_vertex = findStartLocation(path, seam_config.pos); } } - std::vector optimized_order; //To store our result in. At the end we'll std::swap. + std::vector optimized_order; // To store our result in. At the end we'll std::swap. if (order_requirements->empty()) { @@ -213,9 +221,9 @@ class PathOrderOptimizer } - if(reverse_direction && order_requirements->empty()) + if (reverse_direction && order_requirements->empty()) { - std::vector reversed = reverseOrderPaths(optimized_order); //Reverse-insert the optimized order, to invert the ordering. + std::vector reversed = reverseOrderPaths(optimized_order); // Reverse-insert the optimized order, to invert the ordering. std::swap(reversed, paths); } else @@ -225,6 +233,7 @@ class PathOrderOptimizer combing_grid.reset(); } + protected: /*! * If \ref detect_loops is enabled, endpoints of polylines that are closer @@ -276,18 +285,24 @@ class PathOrderOptimizer std::vector getOptimizedOrder(SparsePointGridInclusive line_bucket_grid, size_t snap_radius) { - std::vector optimized_order; //To store our result in. + std::vector optimized_order; // To store our result in. Point current_position = start_point; - std::unordered_map picked(paths.size()); //Fixed size boolean flag for whether each path is already in the optimized vector. + std::unordered_map picked(paths.size()); // Fixed size boolean flag for whether each path is already in the optimized vector. - auto isPicked = [&picked](OrderablePath* c) { return picked[c]; }; - auto notPicked = [&picked](OrderablePath* c) { return !picked[c]; }; + auto isPicked = [&picked](OrderablePath* c) + { + return picked[c]; + }; + auto notPicked = [&picked](OrderablePath* c) + { + return ! picked[c]; + }; - while(optimized_order.size() < paths.size()) + while (optimized_order.size() < paths.size()) { - //Use bucket grid to find paths within snap_radius + // Use bucket grid to find paths within snap_radius std::vector nearby_candidates; for (const auto i : line_bucket_grid.getNearbyVals(current_position, snap_radius)) { @@ -296,14 +311,14 @@ class PathOrderOptimizer std::vector available_candidates; available_candidates.reserve(nearby_candidates.size()); - for(auto candidate : nearby_candidates | ranges::views::filter(notPicked)) + for (auto candidate : nearby_candidates | ranges::views::filter(notPicked)) { available_candidates.push_back(candidate); } - if(available_candidates.empty()) // We need to broaden our search through all candidates + if (available_candidates.empty()) // We need to broaden our search through all candidates { - for(auto path : paths | ranges::views::addressof | ranges::views::filter(notPicked)) + for (auto path : paths | ranges::views::addressof | ranges::views::filter(notPicked)) { available_candidates.push_back(path); } @@ -315,15 +330,15 @@ class PathOrderOptimizer optimized_order.push_back(*best_path); picked[best_path] = true; - if(!best_path->converted->empty()) //If all paths were empty, the best path is still empty. We don't upate the current position then. + if (! best_path->converted->empty()) // If all paths were empty, the best path is still empty. We don't upate the current position then. { - if(best_path->is_closed) + if (best_path->is_closed) { - current_position = (*best_path->converted)[best_path->start_vertex]; //We end where we started. + current_position = (*best_path->converted)[best_path->start_vertex]; // We end where we started. } else { - //Pick the other end from where we started. + // Pick the other end from where we started. current_position = best_path->start_vertex == 0 ? best_path->converted->back() : best_path->converted->front(); } } @@ -332,9 +347,10 @@ class PathOrderOptimizer return optimized_order; } - std::vector getOptimizerOrderWithConstraints(SparsePointGridInclusive line_bucket_grid, size_t snap_radius, const std::unordered_multimap& order_requirements) + std::vector + getOptimizerOrderWithConstraints(SparsePointGridInclusive line_bucket_grid, size_t snap_radius, const std::unordered_multimap& order_requirements) { - std::vector optimized_order; //To store our result in. + std::vector optimized_order; // To store our result in. // initialize the roots set with all possible nodes std::unordered_set roots; @@ -358,8 +374,8 @@ class PathOrderOptimizer std::unordered_set visited; Point current_position = start_point; - std::function(const Path, const std::unordered_multimap&)> get_neighbours = - [current_position, this](const Path current_node, const std::unordered_multimap& graph) + std::function(const Path, const std::unordered_multimap&)> get_neighbours + = [current_position, this](const Path current_node, const std::unordered_multimap& graph) { std::vector order; // Output order to traverse neighbors @@ -382,13 +398,13 @@ class PathOrderOptimizer // update local_current_position auto path = vertices_to_paths[best_candidate]; - if(path->is_closed) + if (path->is_closed) { - local_current_position = (*path->converted)[path->start_vertex]; //We end where we started. + local_current_position = (*path->converted)[path->start_vertex]; // We end where we started. } else { - //Pick the other end from where we started. + // Pick the other end from where we started. local_current_position = path->start_vertex == 0 ? path->converted->back() : path->converted->front(); } } @@ -396,34 +412,33 @@ class PathOrderOptimizer return order; }; - const std::function handle_node = - [¤t_position, &optimized_order, this] - (const Path current_node, const std::nullptr_t _state) + const std::function handle_node + = [¤t_position, &optimized_order, this](const Path current_node, const std::nullptr_t _state) + { + // We should make map from node <-> path for this stuff + for (auto& path : paths) { - // We should make map from node <-> path for this stuff - for (auto& path : paths) + if (path.vertices == current_node) { - if (path.vertices == current_node) + if (path.is_closed) { - if(path.is_closed) - { - current_position = (*path.converted)[path.start_vertex]; //We end where we started. - } - else - { - //Pick the other end from where we started. - current_position = path.start_vertex == 0 ? path.converted->back() : path.converted->front(); - } - - // Add to optimized order - optimized_order.push_back(path); - - break; + current_position = (*path.converted)[path.start_vertex]; // We end where we started. } + else + { + // Pick the other end from where we started. + current_position = path.start_vertex == 0 ? path.converted->back() : path.converted->front(); + } + + // Add to optimized order + optimized_order.push_back(path); + + break; } + } - return nullptr; - }; + return nullptr; + }; if (group_outer_walls) { @@ -482,7 +497,7 @@ class PathOrderOptimizer } else { - while (!roots.empty()) + while (! roots.empty()) { Path root = findClosestPathVertices(current_position, roots); roots.erase(root); @@ -497,13 +512,13 @@ class PathOrderOptimizer std::vector reverseOrderPaths(std::vector pathsOrderPaths) { std::vector reversed; - //Don't replace with swap, assign or insert. They require functions that we can't implement for all template arguments for Path. + // Don't replace with swap, assign or insert. They require functions that we can't implement for all template arguments for Path. reversed.reserve(pathsOrderPaths.size()); - for(auto& path: pathsOrderPaths | ranges::views::reverse) + for (auto& path : pathsOrderPaths | ranges::views::reverse) { reversed.push_back(path); - reversed.back().backwards = !reversed.back().backwards; - if(!reversed.back().is_closed) + reversed.back().backwards = ! reversed.back().backwards; + if (! reversed.back().is_closed) { reversed.back().start_vertex = reversed.back().converted->size() - 1 - reversed.back().start_vertex; } @@ -530,33 +545,35 @@ class PathOrderOptimizer coord_t best_distance2 = std::numeric_limits::max(); OrderablePath* best_candidate = 0; - for(OrderablePath* path : candidate_paths) + for (OrderablePath* path : candidate_paths) { - if(path->converted->empty()) //No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. + if (path->converted->empty()) // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. { - if(best_distance2 == std::numeric_limits::max()) + if (best_distance2 == std::numeric_limits::max()) { best_candidate = path; } continue; } - const bool precompute_start = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; - if(!path->is_closed || !precompute_start) //Find the start location unless we've already precomputed it. + const bool precompute_start + = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; + if (! path->is_closed || ! precompute_start) // Find the start location unless we've already precomputed it. { path->start_vertex = findStartLocation(*path, start_position); - if(!path->is_closed) //Open polylines start at vertex 0 or vertex N-1. Indicate that they should be reversed if they start at N-1. + if (! path->is_closed) // Open polylines start at vertex 0 or vertex N-1. Indicate that they should be reversed if they start at N-1. { path->backwards = path->start_vertex > 0; } } const Point candidate_position = (*path->converted)[path->start_vertex]; coord_t distance2 = getDirectDistance(start_position, candidate_position); - if(distance2 < best_distance2 && combing_boundary) //If direct distance is longer than best combing distance, the combing distance can never be better, so only compute combing if necessary. + if (distance2 < best_distance2 + && combing_boundary) // If direct distance is longer than best combing distance, the combing distance can never be better, so only compute combing if necessary. { distance2 = getCombingDistance(start_position, candidate_position); } - if(distance2 < best_distance2) //Closer than the best candidate so far. + if (distance2 < best_distance2) // Closer than the best candidate so far. { best_candidate = path; best_distance2 = distance2; @@ -589,30 +606,31 @@ class PathOrderOptimizer * applicable. * \param is_closed Whether the polygon is closed (a polygon) or not * (a polyline). If the path is not closed, it will choose between the two - * endpoints rather than + * endpoints rather than * \return An index to a vertex in that path where printing must start. */ size_t findStartLocation(const OrderablePath& path, const Point& target_pos) { - if(!path.is_closed) + if (! path.is_closed) { - //For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. - const coord_t back_distance = (combing_boundary == nullptr) - ? getDirectDistance(path.converted->back(), target_pos) - : getCombingDistance(path.converted->back(), target_pos); - if(back_distance < getDirectDistance(path.converted->front(), target_pos) || (combing_boundary && back_distance < getCombingDistance(path.converted->front(), target_pos))) //Lazy or: Only compute combing distance if direct distance is closer. + // For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. + const coord_t back_distance + = (combing_boundary == nullptr) ? getDirectDistance(path.converted->back(), target_pos) : getCombingDistance(path.converted->back(), target_pos); + if (back_distance < getDirectDistance(path.converted->front(), target_pos) + || (combing_boundary + && back_distance < getCombingDistance(path.converted->front(), target_pos))) // Lazy or: Only compute combing distance if direct distance is closer. { - return path.converted->size() - 1; //Back end is closer. + return path.converted->size() - 1; // Back end is closer. } else { - return 0; //Front end is closer. + return 0; // Front end is closer. } } - //Rest of the function only deals with (closed) polygons. We need to be able to find the seam location of those polygons. + // Rest of the function only deals with (closed) polygons. We need to be able to find the seam location of those polygons. - if(seam_config.type == EZSeamType::RANDOM) + if (seam_config.type == EZSeamType::RANDOM) { size_t vert = getRandomPointInPolygon(*path.converted); return vert; @@ -620,14 +638,14 @@ class PathOrderOptimizer size_t best_i; float best_score = std::numeric_limits::infinity(); - for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - //For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) - ? getDirectDistance(here, target_pos) - : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + // For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) + ? MM2INT(10) + : vSize2(here - target_pos); float corner_angle = cornerAngle(path, i); // angles < 0 are concave (left turning) @@ -650,30 +668,30 @@ class PathOrderOptimizer } float score = score_distance; - switch(seam_config.corner_pref) + switch (seam_config.corner_pref) { default: case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: - if(corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. + if (corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. { score += corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: - if(corner_angle > 0) // Indeed a convex corner? + if (corner_angle > 0) // Indeed a convex corner? { score -= corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: - score -= std::abs(corner_angle) * corner_shift; //Still give sharper corners more advantage. + score -= std::abs(corner_angle) * corner_shift; // Still give sharper corners more advantage. break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE: break; - case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: //Give sharper corners some advantage, but sharper concave corners even more. + case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: // Give sharper corners some advantage, but sharper concave corners even more. { float score_corner = std::abs(corner_angle) * corner_shift; - if(corner_angle < 0) //Concave corner. + if (corner_angle < 0) // Concave corner. { score_corner *= 2; } @@ -683,7 +701,7 @@ class PathOrderOptimizer } constexpr float EPSILON = 25.0; - if(std::abs(best_score - score) <= EPSILON) + if (std::abs(best_score - score) <= EPSILON) { // add breaker for two candidate starting location with similar score // if we don't do this then we (can) get an un-even seam @@ -691,13 +709,13 @@ class PathOrderOptimizer // if x-coord for both points are equal then break ties by // favouring points with lower y-coord const Point& best_point = (*path.converted)[best_i]; - if(std::abs(here.Y - best_point.Y) <= EPSILON ? best_point.X < here.X : best_point.Y < here.Y) + if (std::abs(here.Y - best_point.Y) <= EPSILON ? best_point.X < here.X : best_point.Y < here.Y) { best_score = std::min(best_score, score); best_i = i; } } - else if(score < best_score) + else if (score < best_score) { best_i = i; best_score = score; @@ -720,7 +738,7 @@ class PathOrderOptimizer Point prev_pos = here_pos; Point next_pos; - while(actual_distance < distance) + while (actual_distance < distance) { actual_delta += direction; next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; @@ -728,9 +746,9 @@ class PathOrderOptimizer prev_pos = next_pos; } - if(actual_distance > distance) // Which is veeeery likely + if (actual_distance > distance) // Which is veeeery likely { - prev_pos = (*path.converted)[(here + actual_delta -direction + path.converted->size()) % path.converted->size()]; + prev_pos = (*path.converted)[(here + actual_delta - direction + path.converted->size()) % path.converted->size()]; Point vector = next_pos - prev_pos; coord_t vector_size = vSize(vector); @@ -745,18 +763,18 @@ class PathOrderOptimizer } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from all points within angle_query_distance of the query point. Angles closer - * to the current point are weighted more towards the total angle then points further away. - * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength - * \param path The vertex data of a path - * \param i index of the query point - * \param angle_query_distance query range (default to 0.1mm) - * \param fall_off_strength fall of strength of the angle weight - * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance - */ + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from all points within angle_query_distance of the query point. Angles closer + * to the current point are weighted more towards the total angle then points further away. + * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength + * \param path The vertex data of a path + * \param i index of the query point + * \param angle_query_distance query range (default to 0.1mm) + * \param fall_off_strength fall of strength of the angle weight + * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance + */ float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) { static constexpr coord_t distance_step = 2000; @@ -767,7 +785,7 @@ class PathOrderOptimizer float angle = 0.0; int computed_angles = 0; - for(coord_t distance = distance_step ; distance <= max_distance ; distance += distance_step) + for (coord_t distance = distance_step; distance <= max_distance; distance += distance_step) { Point next = findNeighbourPoint(path, i, distance); Point previous = findNeighbourPoint(path, i, -distance); @@ -806,11 +824,11 @@ class PathOrderOptimizer */ coord_t getCombingDistance(const Point& a, const Point& b) { - if(!PolygonUtils::polygonCollidesWithLineSegment(*combing_boundary, a, b)) + if (! PolygonUtils::polygonCollidesWithLineSegment(*combing_boundary, a, b)) { - return getDirectDistance(a, b); //No collision with any line. Just compute the direct distance then. + return getDirectDistance(a, b); // No collision with any line. Just compute the direct distance then. } - if(paths.size() > 100) + if (paths.size() > 100) { /* If we have many paths to optimize the order for, this combing calculation can become very expensive. Instead, penalize travels @@ -818,13 +836,13 @@ class PathOrderOptimizer return getDirectDistance(a, b) * 5; } - if(combing_grid == nullptr) + 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 = 2000; // 2mm grid cells. Smaller will use more memory, but reduce chance of unnecessary collision checks. combing_grid = PolygonUtils::createLocToLineGrid(*combing_boundary, grid_size); } - CombPath comb_path; //Output variable. + CombPath comb_path; // Output variable. constexpr coord_t rounding_error = -25; constexpr coord_t tiny_travel_threshold = 0; constexpr bool fail_on_unavoidable_obstacles = false; @@ -832,12 +850,12 @@ class PathOrderOptimizer coord_t sum = 0; Point last_point = a; - for(const Point& point : comb_path) + for (const Point& point : comb_path) { sum += vSize(point - last_point); last_point = point; } - return sum * sum; //Squared distance, for fair comparison with direct distance. + return sum * sum; // Squared distance, for fair comparison with direct distance. } /*! @@ -852,7 +870,7 @@ class PathOrderOptimizer bool isLoopingPolyline(const OrderablePath& path) { - if(path.converted->empty()) + if (path.converted->empty()) { return false; } @@ -863,6 +881,6 @@ class PathOrderOptimizer template const std::unordered_multimap PathOrderOptimizer::no_order_requirements; -} //namespace cura +} // namespace cura -#endif //PATHORDEROPTIMIZER_H +#endif // PATHORDEROPTIMIZER_H From 9dbcdf80ea06fbdee18769fce327e4a524170e83 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 6 Oct 2023 10:54:26 +0200 Subject: [PATCH 563/656] comment for adding if statement CURA-11097 --- src/SkirtBrim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 98992c67d9..1731714763 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -667,8 +667,8 @@ void SkirtBrim::generateSupportBrim() } storage.support_brim.add(brim_line); - - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); + // In case of adhesion::NONE length of support brim is only the length of the brims formed for the support + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 27e3c92f3e786a30db78e37e6a1480d3e4c0f743 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Fri, 6 Oct 2023 08:56:34 +0000 Subject: [PATCH 564/656] Applied clang-format. --- src/SkirtBrim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 1731714763..94761b0717 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); // In case of adhesion::NONE length of support brim is only the length of the brims formed for the support - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From a383339eae365892aa7d3cf7db3c1ddbdf537cfb Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 6 Oct 2023 13:07:14 +0200 Subject: [PATCH 565/656] Clean and optimized new corner angle computation algorithm CURA-11100 --- include/PathOrderOptimizer.h | 136 ++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 939baf6b21..108b49b61a 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -636,18 +636,35 @@ class PathOrderOptimizer return vert; } + //Precompute segments lengths because we are going to need them multiple times + std::vector segments_sizes(path.converted->size()); + coord_t total_length = 0; + for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + { + const Point &next = (*path.converted)[(i + 1) % path.converted->size()]; + coord_t segment_size = vSize(next - here); + segments_sizes[i] = segment_size; + total_length += segment_size; + } + size_t best_i; float best_score = std::numeric_limits::infinity(); for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - // For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) - ? MM2INT(10) - : vSize2(here - target_pos); + if(i == path.converted->size() - 1) + { + //The path is closed so the last point is the same as the first, don't process it twice + continue; + } - float corner_angle = cornerAngle(path, i); + //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + //For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) + ? getDirectDistance(here, target_pos) + : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + + float corner_angle = cornerAngle(path, i, segments_sizes, total_length); // angles < 0 are concave (left turning) // angles > 0 are convex (right turning) @@ -725,79 +742,82 @@ class PathOrderOptimizer return best_i; } - Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance) + /*! + * Finds a neighbour point on the path, located before or after the given reference point. The neighbour point + * is computed by travelling on the path and stopping when the distance has been reached, For example: + * |------|---------|------|--------------*---| + * H A B C N D + * In this case, H is the start point of the path and ABCD are the actual following points of the path. + * The neighbour point N is found by reaching point D then going a bit backward on the previous segment. + * This approach gets rid of the mesh actual resolution and gives a neighbour point that is on the path + * at a given physical distance. + * \param path The vertex data of a path + * \param here The starting point index + * \param distance The distance we want to travel on the path, which may be positive to go forward + * or negative to go backward + * \param segments_sizes The pre-computed sizes of the segments + * \return The position of the path a the given distance from the reference point + */ + static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector &segments_sizes) { - assert(distance); - - const Point here_pos = (*path.converted)[here]; - int direction = distance > 0 ? 1 : -1; + const int direction = distance > 0 ? 1 : -1; + const int size_delta = distance > 0 ? -1 : 0; distance = std::abs(distance); - coord_t actual_distance = 0; + // Travel on the path until we reach the distance int actual_delta = 0; - - Point prev_pos = here_pos; - Point next_pos; - while (actual_distance < distance) + coord_t travelled_distance = 0; + coord_t segment_size = 0; + while(travelled_distance < distance) { actual_delta += direction; - next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; - actual_distance += vSize(next_pos - prev_pos); - prev_pos = next_pos; + segment_size = segments_sizes[(here + actual_delta + size_delta + path.converted->size()) % path.converted->size()]; + travelled_distance += segment_size; } - if (actual_distance > distance) // Which is veeeery likely + const Point &next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + + if(travelled_distance > distance) [[likely]] { - prev_pos = (*path.converted)[(here + actual_delta - direction + path.converted->size()) % path.converted->size()]; + // We have overtaken the required distance, go backward on the last segment + int prev = (here + actual_delta -direction + path.converted->size()) % path.converted->size(); + const Point &prev_pos = (*path.converted)[prev]; - Point vector = next_pos - prev_pos; - coord_t vector_size = vSize(vector); - Point unit_vector = (vector * 1000) / vector_size; - Point vector_delta = unit_vector * (vector_size - (actual_distance - distance)); + const Point vector = next_pos - prev_pos; + const Point unit_vector = (vector * 1000) / segment_size; + const Point vector_delta = unit_vector * (segment_size - (travelled_distance - distance)); return prev_pos + vector_delta / 1000; } else { + // Luckily, the required distance stops exactly on an existing point return next_pos; } } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from all points within angle_query_distance of the query point. Angles closer - * to the current point are weighted more towards the total angle then points further away. - * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength - * \param path The vertex data of a path - * \param i index of the query point - * \param angle_query_distance query range (default to 0.1mm) - * \param fall_off_strength fall of strength of the angle weight - * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance - */ - float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from two points within angle_query_distance of the query point, no matter + * what segment this leads us to + * \param path The vertex data of a path + * \param i index of the query point + * \param segments_sizes The pre-computed sizes of the segments + * \param total_length The path total length + * \param angle_query_distance query range (default to 1mm) + * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] + */ + static float cornerAngle(const OrderablePath& path, int i, const std::vector &segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) { - static constexpr coord_t distance_step = 2000; - static constexpr coord_t max_distance = 5000; - - const Point here = (*path.converted)[i]; - - float angle = 0.0; - int computed_angles = 0; - - for (coord_t distance = distance_step; distance <= max_distance; distance += distance_step) - { - Point next = findNeighbourPoint(path, i, distance); - Point previous = findNeighbourPoint(path, i, -distance); - - angle += LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - computed_angles++; - } + const coord_t bounded_distance = std::min(angle_query_distance, total_length / 2); + const Point &here = (*path.converted)[i]; + const Point next = findNeighbourPoint(path, i, bounded_distance, segments_sizes); + const Point previous = findNeighbourPoint(path, i, -bounded_distance, segments_sizes); - angle /= computed_angles; - angle /= M_PI; + float angle = LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - return angle; + return angle / M_PI; } /*! From 6311be84cfdccd3ba9c7815b7a325c86866a65e1 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Fri, 6 Oct 2023 11:12:44 +0000 Subject: [PATCH 566/656] Applied clang-format. --- include/PathOrderOptimizer.h | 62 ++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 108b49b61a..d8d6d9ca17 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -636,12 +636,12 @@ class PathOrderOptimizer return vert; } - //Precompute segments lengths because we are going to need them multiple times + // Precompute segments lengths because we are going to need them multiple times std::vector segments_sizes(path.converted->size()); coord_t total_length = 0; - for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - const Point &next = (*path.converted)[(i + 1) % path.converted->size()]; + const Point& next = (*path.converted)[(i + 1) % path.converted->size()]; coord_t segment_size = vSize(next - here); segments_sizes[i] = segment_size; total_length += segment_size; @@ -651,18 +651,18 @@ class PathOrderOptimizer float best_score = std::numeric_limits::infinity(); for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - if(i == path.converted->size() - 1) + if (i == path.converted->size() - 1) { - //The path is closed so the last point is the same as the first, don't process it twice + // The path is closed so the last point is the same as the first, don't process it twice continue; } - //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - //For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) - ? getDirectDistance(here, target_pos) - : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + // For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) + ? MM2INT(10) + : vSize2(here - target_pos); float corner_angle = cornerAngle(path, i, segments_sizes, total_length); // angles < 0 are concave (left turning) @@ -758,7 +758,7 @@ class PathOrderOptimizer * \param segments_sizes The pre-computed sizes of the segments * \return The position of the path a the given distance from the reference point */ - static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector &segments_sizes) + static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector& segments_sizes) { const int direction = distance > 0 ? 1 : -1; const int size_delta = distance > 0 ? -1 : 0; @@ -768,20 +768,20 @@ class PathOrderOptimizer int actual_delta = 0; coord_t travelled_distance = 0; coord_t segment_size = 0; - while(travelled_distance < distance) + while (travelled_distance < distance) { actual_delta += direction; segment_size = segments_sizes[(here + actual_delta + size_delta + path.converted->size()) % path.converted->size()]; travelled_distance += segment_size; } - const Point &next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + const Point& next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; - if(travelled_distance > distance) [[likely]] + if (travelled_distance > distance) [[likely]] { // We have overtaken the required distance, go backward on the last segment - int prev = (here + actual_delta -direction + path.converted->size()) % path.converted->size(); - const Point &prev_pos = (*path.converted)[prev]; + int prev = (here + actual_delta - direction + path.converted->size()) % path.converted->size(); + const Point& prev_pos = (*path.converted)[prev]; const Point vector = next_pos - prev_pos; const Point unit_vector = (vector * 1000) / segment_size; @@ -796,22 +796,22 @@ class PathOrderOptimizer } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from two points within angle_query_distance of the query point, no matter - * what segment this leads us to - * \param path The vertex data of a path - * \param i index of the query point - * \param segments_sizes The pre-computed sizes of the segments - * \param total_length The path total length - * \param angle_query_distance query range (default to 1mm) - * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] - */ - static float cornerAngle(const OrderablePath& path, int i, const std::vector &segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from two points within angle_query_distance of the query point, no matter + * what segment this leads us to + * \param path The vertex data of a path + * \param i index of the query point + * \param segments_sizes The pre-computed sizes of the segments + * \param total_length The path total length + * \param angle_query_distance query range (default to 1mm) + * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] + */ + static float cornerAngle(const OrderablePath& path, int i, const std::vector& segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) { const coord_t bounded_distance = std::min(angle_query_distance, total_length / 2); - const Point &here = (*path.converted)[i]; + const Point& here = (*path.converted)[i]; const Point next = findNeighbourPoint(path, i, bounded_distance, segments_sizes); const Point previous = findNeighbourPoint(path, i, -bounded_distance, segments_sizes); From c562a43aab7fb36007d7cbaa15b1471cfac37576 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 10:38:33 +0200 Subject: [PATCH 567/656] Removed debug output --- src/SkeletalTrapezoidationGraph.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/SkeletalTrapezoidationGraph.cpp b/src/SkeletalTrapezoidationGraph.cpp index ed8cc1035d..4657b8ee97 100644 --- a/src/SkeletalTrapezoidationGraph.cpp +++ b/src/SkeletalTrapezoidationGraph.cpp @@ -3,17 +3,18 @@ #include "SkeletalTrapezoidationGraph.h" -#include +#include "utils/linearAlg2D.h" +#include "utils/macros.h" #include -#include "utils/linearAlg2D.h" -#include "utils/macros.h" +#include namespace cura { -STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) : HalfEdge(data) +STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) + : HalfEdge(data) { } @@ -131,7 +132,8 @@ STHalfEdge* STHalfEdge::getNextUnconnected() return result->twin; } -STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) : HalfEdgeNode(data, p) +STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) + : HalfEdgeNode(data, p) { } @@ -223,7 +225,10 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) } }; - auto should_collapse = [snap_dist](node_t* a, node_t* b) { return shorterThen(a->p - b->p, snap_dist); }; + auto should_collapse = [snap_dist](node_t* a, node_t* b) + { + return shorterThen(a->p - b->p, snap_dist); + }; for (auto edge_it = edges.begin(); edge_it != edges.end();) { @@ -253,10 +258,6 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) { edge_from_3->from = quad_mid->from; edge_from_3->twin->to = quad_mid->from; - if (count > 50) - { - std::cerr << edge_from_3->from->p << " - " << edge_from_3->to->p << '\n'; - } if (++count > 1000) { break; From 00b71215d5d80f81a9b9ae4b5531beb593e4bd96 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 10:52:13 +0200 Subject: [PATCH 568/656] Fixed some edge-cases with new angle calculation CURA-11100 --- include/PathOrderOptimizer.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index d8d6d9ca17..ab58d30651 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -689,16 +689,12 @@ class PathOrderOptimizer { default: case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: - if (corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. - { - score += corner_angle * corner_shift; - } + // Give advantage to concave corners. More advantage for sharper corners. + score += corner_angle * corner_shift; break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: - if (corner_angle > 0) // Indeed a convex corner? - { - score -= corner_angle * corner_shift; - } + // Give advantage to convex corners. More advantage for sharper corners. + score -= corner_angle * corner_shift; break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: score -= std::abs(corner_angle) * corner_shift; // Still give sharper corners more advantage. @@ -717,7 +713,7 @@ class PathOrderOptimizer } } - constexpr float EPSILON = 25.0; + constexpr float EPSILON = 5.0; if (std::abs(best_score - score) <= EPSILON) { // add breaker for two candidate starting location with similar score From edd9b398369e06a4b90a6b5de9532febcfc11954 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 15:26:34 +0200 Subject: [PATCH 569/656] Minor optimization CURA-11100 Co-authored-by: Casper Lamboo --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index ab58d30651..ce11a18b12 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -642,7 +642,7 @@ class PathOrderOptimizer for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { const Point& next = (*path.converted)[(i + 1) % path.converted->size()]; - coord_t segment_size = vSize(next - here); + const coord_t segment_size = vSize(next - here); segments_sizes[i] = segment_size; total_length += segment_size; } From 0c757cb7aabc8a4cba0948e009d3848dd4d47138 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 15:44:51 +0200 Subject: [PATCH 570/656] Applied code suggestion by @casperlamboo CURA-11100 --- include/PathOrderOptimizer.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index ce11a18b12..506e624927 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -649,14 +650,8 @@ class PathOrderOptimizer size_t best_i; float best_score = std::numeric_limits::infinity(); - for (const auto& [i, here] : **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::drop_last(1) | ranges::views::enumerate) { - if (i == path.converted->size() - 1) - { - // The path is closed so the last point is the same as the first, don't process it twice - continue; - } - // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. // For SHARPEST_CORNER, use a fixed starting score of 0. const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); From 47cc9ba3419b519ea29d48b847139b5deb8a3221 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Mon, 9 Oct 2023 13:45:37 +0000 Subject: [PATCH 571/656] Applied clang-format. --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 506e624927..ae7591db45 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -17,10 +17,10 @@ #include #include #include +#include #include #include #include -#include #include #include From b3ff78c015312b6155cddafa9fb1f62fe037ad9b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:40:09 +0200 Subject: [PATCH 572/656] pin gRPC defs to 0.1.0-beta.1 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index dea1860300..841ec2d936 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/0.1.0-beta.1") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 0503a4c7a2e554bf8f2e9cb3e0e3824df0a54fa8 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:40:52 +0200 Subject: [PATCH 573/656] set version to 5.5.0-beta.2 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 841ec2d936..2556e86c45 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-beta.1" + self.version = "5.5.0-beta.2" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) From 9cf7083257b9f9911ee45b76c7bab22b5e95eb4e Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:42:59 +0200 Subject: [PATCH 574/656] set version to 5.6.0-alpha --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 2556e86c45..27c205dc58 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-beta.2" + self.version = "5.6.0-alpha" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) From ae608cdaf5c6516a10c67c4c4b54416eece04107 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 11:05:34 +0200 Subject: [PATCH 575/656] loosen deps version --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 2556e86c45..383f5631f9 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/0.1.0-beta.1") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 5f3a1f4cfb5c22d370a8a194eb6c0c7cc6a38632 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 10 Oct 2023 17:02:47 +0200 Subject: [PATCH 576/656] Basically working parameterizable prime tower with base CURA-10783 --- include/PrimeTower.h | 7 ++- include/SkirtBrim.h | 9 ---- src/FffGcodeWriter.cpp | 14 ----- src/PrimeTower.cpp | 109 ++++++++++++++++++++++++++------------- src/SkirtBrim.cpp | 49 ++---------------- src/raft.cpp | 16 +++--- src/sliceDataStorage.cpp | 9 +++- 7 files changed, 97 insertions(+), 116 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 5bb8192a82..fe29030ee2 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -40,13 +40,14 @@ class PrimeTower const unsigned int number_of_prime_tower_start_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations std::vector pattern_per_extruder; //!< For each extruder the pattern to print on all layers of the prime tower. - std::vector pattern_extra_brim_per_layer; //!< For each layer with an extra brim, the pattern to be added + std::vector> pattern_extra_brim_per_layer; //!< For each layer of each extruder with an extra brim, the pattern to be added public: bool enabled; //!< Whether the prime tower is enabled. bool would_have_actual_tower; //!< Whether there is an actual tower. bool multiple_extruders_on_first_layer; //!< Whether multiple extruders are allowed on the first layer of the prime tower (e.g. when a raft is there) Polygons outer_poly; //!< The outline of the outermost prime tower. + Polygons footprint; //!< The outline of the prime tower on layer 0 /* * In which order, from outside to inside, will we be printing the prime @@ -101,7 +102,9 @@ class PrimeTower void subtractFromSupport(SliceDataStorage& storage); private: - ExtrusionMoves generatePaths_extraBrim(const Polygons &outer_poly, coord_t extra_radius, coord_t line_width, bool add_inset); + ExtrusionMoves generatePaths_base(const Polygons &outer_poly, coord_t extra_radius, coord_t line_width); + + ExtrusionMoves generatePaths_inset(const Polygons &outer_poly, coord_t line_width, coord_t initial_inset); /*! * \see WipeTower::generatePaths diff --git a/include/SkirtBrim.h b/include/SkirtBrim.h index aefbd5bdd6..05f10a17e4 100644 --- a/include/SkirtBrim.h +++ b/include/SkirtBrim.h @@ -118,15 +118,6 @@ class SkirtBrim */ std::vector generateBrimOffsetPlan(std::vector& starting_outlines); - /*! - * In case that the models have skirt 'adhesion', but the prime tower has a brim, the covered areas are different. - * - * Since the output of this function will need to be handled differently than the rest of the adhesion lines, have a separate function. - * Specifically, for skirt an additional 'approximate convex hull' is applied to the initial 'covered area', which is detrimental to brim. - * \return An ordered list of offsets of the prime-tower to perform in the order in which they are to be performed. - */ - std::vector generatePrimeTowerBrimForSkirtAdhesionOffsetPlan(); - /*! * Generate the primary skirt/brim of the one skirt_brim_extruder or of all extruders simultaneously. * diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 71ebbdef42..f8dabab971 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -164,8 +164,6 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep run_multiple_producers_ordered_consumer( process_layer_starting_layer_nr, - //-Raft::getTotalExtraLayers(), - // total_layers + Raft::getFillerLayerCount() - 1, total_layers, [&storage, total_layers, this](int layer_nr) { @@ -687,8 +685,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raftLines.clear(); } - setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); - layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); } @@ -735,11 +731,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) std::vector raft_outline_paths; const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. - if (storage.primeRaftOutline.area() > 0) - { - raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); - raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. - } raft_outline_paths.emplace_back(storage.raftOutline.offset(-small_offset)); raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); @@ -841,11 +832,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) std::vector raft_outline_paths; const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. - if (storage.primeRaftOutline.area() > 0) - { - raft_outline_paths.emplace_back(storage.primeRaftOutline.offset(-small_offset)); - raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. - } raft_outline_paths.emplace_back(storage.raftOutline.offset(-small_offset)); raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 472edc4054..49de8b3db1 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -97,6 +97,18 @@ void PrimeTower::generateGroundpoly() middle = Point(x - tower_size / 2, y + tower_size / 2); post_wipe_point = Point(x - tower_size / 2, y + tower_size / 2); + + const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); + const coord_t base_extra_radius = mesh_group_settings.get("prime_tower_base_size"); + const coord_t base_height = mesh_group_settings.get("prime_tower_base_height"); + if (base_enabled && base_extra_radius > 0 && base_height > 0) + { + footprint = outer_poly.offset(base_extra_radius); + } + else + { + footprint = outer_poly; + } } void PrimeTower::generatePaths(const SliceDataStorage& storage) @@ -110,7 +122,7 @@ void PrimeTower::generatePaths(const SliceDataStorage& storage) } } -PrimeTower::ExtrusionMoves PrimeTower::generatePaths_extraBrim(const Polygons& outer_poly, coord_t extra_radius, coord_t line_width, bool add_inset) +PrimeTower::ExtrusionMoves PrimeTower::generatePaths_base(const Polygons& outer_poly, coord_t extra_radius, coord_t line_width) { const Scene& scene = Application::getInstance().current_slice->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; @@ -118,16 +130,6 @@ PrimeTower::ExtrusionMoves PrimeTower::generatePaths_extraBrim(const Polygons& o ExtrusionMoves pattern; - if (add_inset) - { - Polygons inset = outer_poly.offset(-line_width / 2); - while (! inset.empty()) - { - pattern.polygons.add(inset); - inset = inset.offset(-line_width); - } - } - int circles = 0; Polygons outset = outer_poly.offset(line_width / 2); while (outset.max() - outset.min() < (tower_size + extra_radius * 2)) @@ -140,15 +142,34 @@ PrimeTower::ExtrusionMoves PrimeTower::generatePaths_extraBrim(const Polygons& o return pattern; } -void PrimeTower::generatePaths_denseInfill() +PrimeTower::ExtrusionMoves PrimeTower::generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset) { - constexpr coord_t brim_extra_radius = 20000; - constexpr coord_t brim_extra_height = 20000; + const Scene& scene = Application::getInstance().current_slice->scene; + const Settings& mesh_group_settings = scene.current_mesh_group->settings; + + ExtrusionMoves pattern; + + Polygons inset = outer_poly.offset(-(initial_inset + line_width / 2)); + while (! inset.empty()) + { + pattern.polygons.add(inset); + inset = inset.offset(-line_width); + } + + return pattern; +} +void PrimeTower::generatePaths_denseInfill() +{ const Scene& scene = Application::getInstance().current_slice->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); + const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); + const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); + const coord_t base_height = scene.settings.get("prime_tower_base_height"); + const int magnitude = scene.settings.get("prime_tower_base_curve_magnitude"); pattern_per_extruder.resize(extruder_count); + pattern_extra_brim_per_layer.resize(extruder_count); coord_t cumulative_inset = 0; // Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. for (size_t extruder_nr : extruder_order) @@ -173,23 +194,33 @@ void PrimeTower::generatePaths_denseInfill() } } - // Only the most inside extruder needs to fill the inside of the prime tower - if (extruder_nr == extruder_order.front()) + // The most outside extruder is used for the base + if (base_enabled && extruder_nr == extruder_order.front()) { - // Generate the pattern for the first layer. - const coord_t line_width_layer0 = scene.extruders[extruder_nr].settings.get("raft_base_line_width"); - ExtrusionMoves pattern_layer0 = generatePaths_extraBrim(outer_poly, brim_extra_radius, line_width_layer0, true); - pattern_extra_brim_per_layer.push_back(pattern_layer0); - - for (coord_t z = layer_height; z < brim_extra_height; z += layer_height) + for (coord_t z = 0; z < base_height; z += layer_height) { - double brim_radius_factor = std::pow((1.0 - static_cast(z) / brim_extra_height), 4); - coord_t extra_radius = brim_extra_radius * brim_radius_factor; - ExtrusionMoves pattern = generatePaths_extraBrim(outer_poly, extra_radius, line_width, false); - pattern_extra_brim_per_layer.push_back(pattern); + double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), magnitude); + coord_t extra_radius = base_extra_radius * brim_radius_factor; + ExtrusionMoves pattern = generatePaths_base(outer_poly, extra_radius, line_width); + if (pattern.polygons.empty() && pattern.lines.empty()) + { + break; + } + pattern_extra_brim_per_layer[extruder_nr].push_back(pattern); } } + cumulative_inset += wall_nr * line_width; + + // Only the most inside extruder needs to fill the inside of the prime tower + if (extruder_nr == extruder_order.back()) + { + ExtrusionMoves pattern = generatePaths_inset(outer_poly, line_width, cumulative_inset); + if (! pattern.polygons.empty() || ! pattern.lines.empty()) + { + pattern_extra_brim_per_layer[extruder_nr].push_back(pattern); + } + } } } @@ -215,7 +246,7 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la } const LayerIndex layer_nr = gcode_layer.getLayerNr(); - if (/*layer_nr < 0 ||*/ layer_nr > storage.max_print_height_second_to_last_extruder + 1) + if (layer_nr > storage.max_print_height_second_to_last_extruder + 1) { return; } @@ -252,26 +283,30 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const { - LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + Raft::getTotalExtraLayers(); - assert(absolute_layer_number >= 0); + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + const ExtruderTrain& base_train = mesh_group_settings.get("raft_base_extruder_nr"); + const bool adhesion_raft = base_train.settings.get("adhesion_type") == EPlatformAdhesion::RAFT; + LayerIndex absolute_layer_number = gcode_layer.getLayerNr(); + if (adhesion_raft) + { + absolute_layer_number += Raft::getTotalExtraLayers(); + } - if (absolute_layer_number > 0) + if (! adhesion_raft || absolute_layer_number > 0) { // Actual prime pattern const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); } - if (absolute_layer_number < pattern_extra_brim_per_layer.size() && extruder_nr == extruder_order.front()) + const std::vector& pattern_extra_brim = pattern_extra_brim_per_layer[extruder_nr]; + if (absolute_layer_number < pattern_extra_brim.size()) { - // Specific case for first layer => very high adhesion - const GCodePathConfig& config - = absolute_layer_number == 0 ? gcode_layer.configs_storage.raft_base_config : gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - - const ExtrusionMoves& pattern = pattern_extra_brim_per_layer[absolute_layer_number]; + // Extra rings for stronger base + const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; + const ExtrusionMoves& pattern = pattern_extra_brim[absolute_layer_number]; gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); } diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 94761b0717..2ec4d932c2 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -110,39 +110,15 @@ std::vector SkirtBrim::generateBrimOffsetPlan(std::vector SkirtBrim::generatePrimeTowerBrimForSkirtAdhesionOffsetPlan() -{ - std::vector prime_brim_offsets; - - const Settings& global_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const bool prime_tower_brim_enable = global_settings.get("prime_tower_brim_enable"); - if (adhesion_type == EPlatformAdhesion::SKIRT && prime_tower_brim_enable && storage.primeTower.enabled) - { - const int extruder_nr = storage.primeTower.extruder_order[0]; - const ExtruderTrain& extruder = extruders[extruder_nr]; - int line_count = extruder.settings.get("brim_line_count"); - coord_t gap = extruder.settings.get("brim_gap"); - for (int line_idx = 0; line_idx < line_count; line_idx++) - { - const bool is_last = line_idx == line_count - 1; - coord_t offset = gap + line_widths[extruder_nr] / 2 + line_widths[extruder_nr] * line_idx; - prime_brim_offsets.emplace_back(&storage.primeTower.outer_poly, external_polys_only[extruder_nr], offset, offset, line_idx, extruder_nr, is_last); - } - } - - std::sort(prime_brim_offsets.begin(), prime_brim_offsets.end(), OffsetSorter); - return prime_brim_offsets; -} - void SkirtBrim::generate() { std::vector starting_outlines(extruder_count); std::vector all_brim_offsets = generateBrimOffsetPlan(starting_outlines); - std::vector prime_brim_offsets_for_skirt = generatePrimeTowerBrimForSkirtAdhesionOffsetPlan(); constexpr LayerIndex layer_nr = 0; constexpr bool include_support = true; - Polygons covered_area = storage.getLayerOutlines(layer_nr, include_support, /*include_prime_tower*/ true, /*external_polys_only*/ false); + const bool include_prime_tower = adhesion_type == EPlatformAdhesion::SKIRT; + Polygons covered_area = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, /*external_polys_only*/ false); std::vector allowed_areas_per_extruder(extruder_count); for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) @@ -161,14 +137,6 @@ void SkirtBrim::generate() } } - // Note that the brim generated here for the prime-tower is _only_ when the rest if the model uses skirt. - // If everything uses brim to begin with, _including_ the prime-tower, it's not generated here, but along the rest. - if (! prime_brim_offsets_for_skirt.empty()) - { - // Note that his ignores the returned lengths: Less 'correct' in a sense, will be predictable for the user. - generatePrimaryBrim(prime_brim_offsets_for_skirt, covered_area, allowed_areas_per_extruder); - } - // Apply 'approximate convex hull' if the adhesion is skirt _after_ any skirt but also prime-tower-brim adhesion. // Otherwise, the now expanded convex hull covered areas will mess with that brim. Fortunately this does not mess // with the other area calculation above, since they are either itself a simple/convex shape or relevant for brim. @@ -406,12 +374,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) if (skirt_around_prime_tower_brim) { - const int prime_tower_brim_extruder_nr = storage.primeTower.extruder_order[0]; - const ExtruderTrain& prime_tower_brim_extruder = extruders[prime_tower_brim_extruder_nr]; - int line_count = prime_tower_brim_extruder.settings.get("brim_line_count"); - coord_t tower_gap = prime_tower_brim_extruder.settings.get("brim_gap"); - coord_t brim_width = tower_gap + line_count * line_widths[prime_tower_brim_extruder_nr]; - first_layer_outline = first_layer_outline.unionPolygons(storage.primeTower.outer_poly.offset(brim_width)); + first_layer_outline = first_layer_outline.unionPolygons(storage.primeTower.footprint); } Polygons shields; @@ -435,7 +398,7 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) else { // add brim underneath support by removing support where there's brim around the model constexpr bool include_support = false; // Include manually below. - constexpr bool include_prime_tower = false; // Include manually below. + constexpr bool include_prime_tower = false; // Not included. constexpr bool external_outlines_only = false; // Remove manually below. first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_outlines_only, extruder_nr); first_layer_outline = first_layer_outline.unionPolygons(); // To guard against overlapping outlines, which would produce holes according to the even-odd rule. @@ -480,10 +443,6 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) first_layer_outline.add(support_layer.support_bottom); first_layer_outline.add(support_layer.support_roof); } - if (storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable") && (extruder_nr == -1 || int(storage.primeTower.extruder_order[0]) == extruder_nr)) - { - first_layer_outline.add(storage.primeTower.outer_poly); // don't remove parts of the prime tower, but make a brim for it - } } constexpr coord_t join_distance = 20; first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon diff --git a/src/raft.cpp b/src/raft.cpp index 384958afc4..bee9cc643b 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -66,14 +66,14 @@ void Raft::generate(SliceDataStorage& storage) } } - // storage.primeRaftOutline = storage.primeTower.outer_poly.offset(distance, ClipperLib::jtRound); - // NOTE: the raft doesn't take the prime tower brim into account, because it's (currently) not being printed when printing a raft - // if (settings.get("raft_remove_inside_corners")) - //{ - // storage.primeRaftOutline = storage.primeRaftOutline.unionPolygons(storage.raftOutline); - // storage.primeRaftOutline.makeConvex(); - // } - // storage.primeRaftOutline = storage.primeRaftOutline.difference(storage.raftOutline); // In case of overlaps. + const coord_t prime_tower_distance = settings.get("prime_tower_base_size"); + storage.primeRaftOutline = storage.primeTower.outer_poly.offset(prime_tower_distance, ClipperLib::jtRound); + if (settings.get("raft_remove_inside_corners")) + { + storage.primeRaftOutline = storage.primeRaftOutline.unionPolygons(storage.raftOutline); + storage.primeRaftOutline.makeConvex(); + } + storage.primeRaftOutline = storage.primeRaftOutline.difference(storage.raftOutline); // In case of overlaps. } coord_t Raft::getTotalThickness() diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 4533fbb8be..e5ba324ccb 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -333,7 +333,14 @@ Polygons { if (primeTower.enabled) { - total.add(primeTower.outer_poly); + if (layer_nr == 0) + { + total.add(primeTower.footprint); + } + else + { + total.add(primeTower.outer_poly); + } } } return total; From 1f8df4ce8be6663901d5c2bb2697455fae12a80e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 10 Oct 2023 17:03:15 +0200 Subject: [PATCH 577/656] Fix dark floating-point bug --- src/LayerPlanBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index 6551543bdc..b5bede2665 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -429,7 +429,7 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex return; } - assert((time_window >= 0 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); + assert((time_window >= -0.001 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); // ,layer change . // : ,precool command ,layer change . From 90f8a8f59b09dfc96049c206ecb705f781b21726 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Tue, 10 Oct 2023 15:04:01 +0000 Subject: [PATCH 578/656] Applied clang-format. --- include/PrimeTower.h | 4 +-- include/SkirtBrim.h | 82 +++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index fe29030ee2..bdbc36bc0e 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -102,9 +102,9 @@ class PrimeTower void subtractFromSupport(SliceDataStorage& storage); private: - ExtrusionMoves generatePaths_base(const Polygons &outer_poly, coord_t extra_radius, coord_t line_width); + ExtrusionMoves generatePaths_base(const Polygons& outer_poly, coord_t extra_radius, coord_t line_width); - ExtrusionMoves generatePaths_inset(const Polygons &outer_poly, coord_t line_width, coord_t initial_inset); + ExtrusionMoves generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset); /*! * \see WipeTower::generatePaths diff --git a/include/SkirtBrim.h b/include/SkirtBrim.h index 05f10a17e4..fa41df0999 100644 --- a/include/SkirtBrim.h +++ b/include/SkirtBrim.h @@ -1,17 +1,17 @@ -//Copyright (c) 2023 UltiMaker -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SKIRT_BRIM_H #define SKIRT_BRIM_H -#include "utils/Coord_t.h" #include "ExtruderTrain.h" #include "settings/EnumSettings.h" #include "sliceDataStorage.h" +#include "utils/Coord_t.h" #include -namespace cura +namespace cura { class Polygons; @@ -27,26 +27,25 @@ class SkirtBrim */ struct Offset { - Offset - ( + Offset( const std::variant& reference_outline_or_index, const bool external_only, const coord_t offset_value, const coord_t total_offset, const size_t inset_idx, const int extruder_nr, - const bool is_last - ) : - reference_outline_or_index(reference_outline_or_index), - external_only(external_only), - offset_value(offset_value), - total_offset(total_offset), - inset_idx(inset_idx), - extruder_nr(extruder_nr), - is_last(is_last) - {} - - std::variant reference_outline_or_index; + const bool is_last) + : reference_outline_or_index(reference_outline_or_index) + , external_only(external_only) + , offset_value(offset_value) + , total_offset(total_offset) + , inset_idx(inset_idx) + , extruder_nr(extruder_nr) + , is_last(is_last) + { + } + + std::variant reference_outline_or_index; bool external_only; //!< Wether to only offset outward from the reference polygons coord_t offset_value; //!< Distance by which to offset from the reference coord_t total_offset; //!< Total distance from the model @@ -58,15 +57,12 @@ class SkirtBrim /*! * Defines an order on offsets (potentially from different extruders) based on how far the offset is from the original outline. */ - static inline const auto OffsetSorter - { - [](const Offset& a, const Offset& b) - { - // Use extruder_nr in case both extruders have the same offset settings. - return a.total_offset != b.total_offset ? a.total_offset < b.total_offset : a.extruder_nr < b.extruder_nr; - } - }; - + static inline const auto OffsetSorter{ [](const Offset& a, const Offset& b) + { + // Use extruder_nr in case both extruders have the same offset settings. + return a.total_offset != b.total_offset ? a.total_offset < b.total_offset : a.extruder_nr < b.extruder_nr; + } }; + SliceDataStorage& storage; //!< Where to retrieve settings and store brim lines. const EPlatformAdhesion adhesion_type; //!< Whether we are generating brim, skirt, or raft const bool has_ooze_shield; //!< Whether the meshgroup has an ooze shield @@ -85,17 +81,17 @@ class SkirtBrim public: /*! * Precomputes some values used in several functions when calling \ref generate - * + * * \param storage Storage containing the parts at the first layer. */ SkirtBrim(SliceDataStorage& storage); /*! * Generate skirt or brim (depending on parameters). - * + * * When \p distance > 0 and \p count == 1 a skirt is generated, which has * slightly different configuration. Otherwise, a brim is generated. - * + * * \param storage Storage containing the parts at the first layer. * \param first_layer_outline The outline to generate skirt or brim around. * \param distance The distance of the first outset from the parts at the first @@ -108,7 +104,7 @@ class SkirtBrim private: /*! * Plan the offsets which we will be going to perform and put them in the right order. - * + * * In order for brims of different materials to grow toward the middle, * we need to perform the offsets alternatingly. * We therefore first create all planned Offset objects, @@ -120,7 +116,7 @@ class SkirtBrim /*! * Generate the primary skirt/brim of the one skirt_brim_extruder or of all extruders simultaneously. - * + * * \param[in,out] all_brim_offsets The offsets to perform. Adjusted when the minimal length constraint isn't met yet. * \param[in,out] covered_area The area of the first layer covered by model or generated brim lines. * \param[in,out] allowed_areas_per_extruder The difference between the machine bed area (offsetted by the nozzle offset) and the covered_area. @@ -130,9 +126,9 @@ class SkirtBrim /*! * Generate the brim inside the ooze shield and draft shield - * + * * \warning Adjusts brim_covered_area - * + * * \param storage Storage containing the parts at the first layer. * \param[in,out] brim_covered_area The area that was covered with brim before (in) and after (out) adding the shield brims * \param[in,out] allowed_areas_per_extruder The difference between the machine areas and the \p covered_area @@ -154,10 +150,10 @@ class SkirtBrim /*! * The disallowed area around the internal holes of parts with other parts inside which would get an external brim. - * + * * In order to prevent the external_only brim of a part inside another part to overlap with the internal holes of the outer part, * we generate a disallowed area around those internal hole polygons. - * + * * \param outline The full layer outlines * \param extruder_nr The extruder for which to compute disallowed areas * \return The disallowed areas @@ -166,9 +162,9 @@ class SkirtBrim /*! * Generate a brim line with offset parameters given by \p offset from the \p starting_outlines and store it in the \ref storage. - * + * * \warning Has side effects on \p covered_area, \p allowed_areas_per_extruder and \p total_length - * + * * \param offset The parameters with which to perform the offset * \param[in,out] covered_area The total area covered by the brims (and models) on the first layer. * \param[in,out] allowed_areas_per_extruder The difference between the machine areas and the \p covered_area @@ -179,11 +175,11 @@ class SkirtBrim /*! * Generate a skirt of extruders which don't yet comply with the minimum length requirement. - * + * * This skirt goes directly adjacent to all primary brims. - * + * * The skirt is stored in storage.skirt_brim. - * + * * \param[in,out] covered_area The total area covered by the brims (and models) on the first layer. * \param[in,out] allowed_areas_per_extruder The difference between the machine areas and the \p covered_area * \param[in,out] total_length The total length of the brim lines for each extruder. @@ -196,6 +192,6 @@ class SkirtBrim */ void generateSupportBrim(); }; -}//namespace cura +} // namespace cura -#endif //SKIRT_BRIM_H +#endif // SKIRT_BRIM_H From de2c993285915c9c2baf067fd807c97e0910f398 Mon Sep 17 00:00:00 2001 From: Remco Burema <41987080+rburema@users.noreply.github.com> Date: Wed, 11 Oct 2023 12:09:26 +0200 Subject: [PATCH 579/656] Fix comment to be technically correct. belatedly part of CURA-11041 Co-authored-by: Casper Lamboo --- include/sliceDataStorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 067a0a60a3..4c424a2b09 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -212,7 +212,7 @@ class SupportLayer std::vector support_infill_parts; //!< a list of support infill parts Polygons support_bottom; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof. Polygons support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. - Polygons support_fractional_roof_top; //!< If the support distance is less than a multiple of the layer height, + Polygons support_fractional_roof_top; //!< If the support distance is not exactly a multiple of the layer height, // the first part of support just underneath the model needs to be printed at a fracional layer height. Polygons support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support Polygons support_mesh; //!< Areas from support meshes which should NOT be supported by more support From 2dada4ceabe4c600ba3fad0caad640a8329c3ee6 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 12 Oct 2023 15:41:32 +0200 Subject: [PATCH 580/656] Properly handle overlapping brims/rafts CURA-10783 --- include/PrimeTower.h | 21 ++- include/sliceDataStorage.h | 2 - src/FffGcodeWriter.cpp | 252 ++++++++++++++++++------------------ src/FffPolygonGenerator.cpp | 4 +- src/PrimeTower.cpp | 77 +++++------ src/SkirtBrim.cpp | 15 ++- src/TreeModelVolumes.cpp | 2 +- src/raft.cpp | 10 -- src/sliceDataStorage.cpp | 9 +- 9 files changed, 196 insertions(+), 196 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index bdbc36bc0e..9133d3007a 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -4,6 +4,7 @@ #ifndef PRIME_TOWER_H #define PRIME_TOWER_H +#include "settings/types/LayerIndex.h" #include "utils/polygon.h" // Polygons #include "utils/polygonUtils.h" @@ -42,12 +43,20 @@ class PrimeTower std::vector pattern_per_extruder; //!< For each extruder the pattern to print on all layers of the prime tower. std::vector> pattern_extra_brim_per_layer; //!< For each layer of each extruder with an extra brim, the pattern to be added + Polygons outer_poly; //!< The outline of the outermost prime tower. + + struct BasePolygon + { + Polygons polygons; + size_t extra_rings; + }; + + std::vector outer_poly_base; //!< The outline of the prime tower for layers having a base + public: bool enabled; //!< Whether the prime tower is enabled. bool would_have_actual_tower; //!< Whether there is an actual tower. bool multiple_extruders_on_first_layer; //!< Whether multiple extruders are allowed on the first layer of the prime tower (e.g. when a raft is there) - Polygons outer_poly; //!< The outline of the outermost prime tower. - Polygons footprint; //!< The outline of the prime tower on layer 0 /* * In which order, from outside to inside, will we be printing the prime @@ -101,10 +110,14 @@ class PrimeTower */ void subtractFromSupport(SliceDataStorage& storage); + const Polygons &getOuterPoly(const LayerIndex &layer_nr) const; + + const Polygons &getGroundPoly() const; + private: - ExtrusionMoves generatePaths_base(const Polygons& outer_poly, coord_t extra_radius, coord_t line_width); + static ExtrusionMoves generatePaths_base(const Polygons &inset, size_t rings, coord_t line_width); - ExtrusionMoves generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset); + static ExtrusionMoves generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset); /*! * \see WipeTower::generatePaths diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 9b0661ed36..c3313e801a 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -333,8 +333,6 @@ class SliceDataStorage : public NoCopy std::vector skirt_brim[MAX_EXTRUDERS]; //!< Skirt/brim polygons per extruder, ordered from inner to outer polygons. Polygons support_brim; //!< brim lines for support, going from the edge of the support inward. \note Not ordered by inset. Polygons raftOutline; // Storage for the outline of the raft. Will be filled with lines when the GCode is generated. - Polygons primeRaftOutline; // ... the raft underneath the prime-tower will have to be printed first, if there is one. (When the raft has top layers with a different extruder - // for example.) int max_print_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder std::vector max_print_height_per_extruder; //!< For each extruder the highest layer number at which it is used. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index f8dabab971..0deaaff908 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -621,69 +621,65 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const coord_t max_resolution = base_settings.get("meshfix_maximum_resolution"); const coord_t max_deviation = base_settings.get("meshfix_maximum_deviation"); - std::vector raft_outline_paths; - if (storage.primeRaftOutline.area() > 0) + Polygons raft_outline_path = storage.raftOutline; + if (storage.primeTower.enabled) { - raft_outline_paths.emplace_back(storage.primeRaftOutline); + raft_outline_path = raft_outline_path.unionPolygons(storage.primeTower.getOuterPoly(layer_nr)); } - raft_outline_paths.emplace_back(storage.raftOutline); - for (const Polygons& raft_outline_path : raft_outline_paths) + Infill infill_comp( + EFillMethod::LINES, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + gcode_layer.configs_storage.raft_base_config.getLineWidth(), + line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + std::vector raft_paths; + infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); + if (! raft_paths.empty()) { - Infill infill_comp( - EFillMethod::LINES, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - gcode_layer.configs_storage.raft_base_config.getLineWidth(), - line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - std::vector raft_paths; - infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); - if (! raft_paths.empty()) - { - const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); - InsetOrderOptimizer wall_orderer( - *this, - storage, - gcode_layer, - base_settings, - base_extruder_nr, - config, - config, - config, - config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - base_extruder_nr, - base_extruder_nr, - z_seam_config, - raft_paths); - wall_orderer.addToLayer(); - } - gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); - - raft_polygons.clear(); - raftLines.clear(); + const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; + const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + base_settings, + base_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + base_extruder_nr, + base_extruder_nr, + z_seam_config, + raft_paths); + wall_orderer.addToLayer(); } + gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); + + raft_polygons.clear(); + raftLines.clear(); layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); @@ -728,11 +724,11 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, interface_layer_height); - std::vector raft_outline_paths; + Polygons raft_outline_path; const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. - raft_outline_paths.emplace_back(storage.raftOutline.offset(-small_offset)); - raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. + raft_outline_path = storage.raftOutline.offset(-small_offset); + raft_outline_path = Simplify(interface_settings).polygon(raft_outline_path); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); Polygons raft_lines; AngleDegrees fill_angle = (num_surface_layers + num_interface_layers - raft_interface_layer) % 2 ? 45 : 135; // 90 degrees rotated from the first top layer. @@ -749,40 +745,42 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; - for (const Polygons& raft_outline_path : raft_outline_paths) + if (storage.primeTower.enabled) { - Infill infill_comp( - EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - interface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - interface_max_resolution, - interface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - std::vector raft_paths; // Should remain empty, since we have no walls. - infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); - gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); - - raft_polygons.clear(); - raft_lines.clear(); + raft_outline_path = raft_outline_path.difference(storage.primeTower.getOuterPoly(layer_nr)); } + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + interface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + interface_max_resolution, + interface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + std::vector raft_paths; // Should remain empty, since we have no walls. + infill_comp.generate(raft_paths, raft_polygons, raft_lines, interface_settings, layer_nr, SectionType::ADHESION); + gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); + + raft_polygons.clear(); + raft_lines.clear(); + setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); layer_plan_buffer.handle(gcode_layer, gcode); @@ -829,11 +827,11 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Application::getInstance().communication->sendLayerComplete(layer_nr, z, surface_layer_height); - std::vector raft_outline_paths; + Polygons raft_outline_path; const coord_t small_offset = gcode_layer.configs_storage.raft_interface_config.getLineWidth() / 2; // Do this manually because of micron-movement created in corners when insetting a polygon that was offset with round joint type. - raft_outline_paths.emplace_back(storage.raftOutline.offset(-small_offset)); - raft_outline_paths.back() = Simplify(interface_settings).polygon(raft_outline_paths.back()); // Remove those micron-movements. + raft_outline_path = storage.raftOutline.offset(-small_offset); + raft_outline_path = Simplify(interface_settings).polygon(raft_outline_path); // Remove those micron-movements. const coord_t infill_outline_width = gcode_layer.configs_storage.raft_interface_config.getLineWidth(); Polygons raft_lines; AngleDegrees fill_angle @@ -851,40 +849,42 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) constexpr size_t zag_skip_count = 0; constexpr coord_t pocket_size = 0; - for (const Polygons& raft_outline_path : raft_outline_paths) + if (storage.primeTower.enabled) { - Infill infill_comp( - EFillMethod::ZIG_ZAG, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - infill_outline_width, - surface_line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - surface_max_resolution, - surface_max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - std::vector raft_paths; // Should remain empty, since we have no walls. - infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); - gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); - - raft_polygons.clear(); - raft_lines.clear(); + raft_outline_path = raft_outline_path.difference(storage.primeTower.getOuterPoly(layer_nr)); } + Infill infill_comp( + EFillMethod::ZIG_ZAG, + zig_zaggify_infill, + connect_polygons, + raft_outline_path, + infill_outline_width, + surface_line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + surface_max_resolution, + surface_max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + std::vector raft_paths; // Should remain empty, since we have no walls. + infill_comp.generate(raft_paths, raft_polygons, raft_lines, surface_settings, layer_nr, SectionType::ADHESION); + gcode_layer.addLinesByOptimizer(raft_lines, gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines, false, 0, 1.0, last_planned_position); + + raft_polygons.clear(); + raft_lines.clear(); + setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); layer_plan_buffer.handle(gcode_layer, gcode); diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index daff0196eb..640a563284 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -985,7 +985,7 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) } for (LayerIndex layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++) { - storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].difference(storage.primeTower.outer_poly.offset(max_line_width / 2)); + storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].difference(storage.primeTower.getOuterPoly(layer_nr).offset(max_line_width / 2)); } } } @@ -1035,7 +1035,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) max_line_width = std::max(max_line_width, extruders[extruder_nr].settings.get("skirt_brim_line_width")); } } - storage.draft_protection_shield = storage.draft_protection_shield.difference(storage.primeTower.outer_poly.offset(max_line_width / 2)); + storage.draft_protection_shield = storage.draft_protection_shield.difference(storage.primeTower.getGroundPoly().offset(max_line_width / 2)); } } diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 49de8b3db1..39c6ae3fe2 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -87,7 +87,8 @@ void PrimeTower::generateGroundpoly() return; } - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + const Scene& scene = Application::getInstance().current_slice->scene; + const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); const coord_t x = mesh_group_settings.get("prime_tower_position_x"); @@ -97,18 +98,6 @@ void PrimeTower::generateGroundpoly() middle = Point(x - tower_size / 2, y + tower_size / 2); post_wipe_point = Point(x - tower_size / 2, y + tower_size / 2); - - const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); - const coord_t base_extra_radius = mesh_group_settings.get("prime_tower_base_size"); - const coord_t base_height = mesh_group_settings.get("prime_tower_base_height"); - if (base_enabled && base_extra_radius > 0 && base_height > 0) - { - footprint = outer_poly.offset(base_extra_radius); - } - else - { - footprint = outer_poly; - } } void PrimeTower::generatePaths(const SliceDataStorage& storage) @@ -122,21 +111,15 @@ void PrimeTower::generatePaths(const SliceDataStorage& storage) } } -PrimeTower::ExtrusionMoves PrimeTower::generatePaths_base(const Polygons& outer_poly, coord_t extra_radius, coord_t line_width) +PrimeTower::ExtrusionMoves PrimeTower::generatePaths_base(const Polygons& inset, size_t rings, coord_t line_width) { - const Scene& scene = Application::getInstance().current_slice->scene; - const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); - ExtrusionMoves pattern; - int circles = 0; - Polygons outset = outer_poly.offset(line_width / 2); - while (outset.max() - outset.min() < (tower_size + extra_radius * 2)) + Polygons path = inset.offset(line_width / 2); + for (size_t ring = 0; ring < rings; ++ring) { - pattern.polygons.add(outset); - outset = outset.offset(line_width); - circles++; + pattern.polygons.add(path); + path = path.offset(line_width); } return pattern; @@ -167,7 +150,9 @@ void PrimeTower::generatePaths_denseInfill() const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); const coord_t base_height = scene.settings.get("prime_tower_base_height"); - const int magnitude = scene.settings.get("prime_tower_base_curve_magnitude"); + const int base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); + const coord_t line_width = scene.extruders[extruder_order.front()].settings.get("prime_tower_line_width"); + pattern_per_extruder.resize(extruder_count); pattern_extra_brim_per_layer.resize(extruder_count); @@ -195,18 +180,21 @@ void PrimeTower::generatePaths_denseInfill() } // The most outside extruder is used for the base - if (base_enabled && extruder_nr == extruder_order.front()) + if (extruder_nr == extruder_order.front() && base_enabled && base_extra_radius > 0 && base_height > 0) { for (coord_t z = 0; z < base_height; z += layer_height) { - double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), magnitude); + double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), base_curve_magnitude); coord_t extra_radius = base_extra_radius * brim_radius_factor; - ExtrusionMoves pattern = generatePaths_base(outer_poly, extra_radius, line_width); - if (pattern.polygons.empty() && pattern.lines.empty()) + size_t extra_rings = extra_radius / line_width; + if (extra_rings == 0) { break; } - pattern_extra_brim_per_layer[extruder_nr].push_back(pattern); + extra_radius = line_width * extra_rings; + outer_poly_base.push_back(outer_poly.offset(extra_radius)); + + pattern_extra_brim_per_layer[extruder_nr].push_back(generatePaths_base(outer_poly, extra_rings, line_width)); } } @@ -283,14 +271,9 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_la void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const { - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - const ExtruderTrain& base_train = mesh_group_settings.get("raft_base_extruder_nr"); - const bool adhesion_raft = base_train.settings.get("adhesion_type") == EPlatformAdhesion::RAFT; - LayerIndex absolute_layer_number = gcode_layer.getLayerNr(); - if (adhesion_raft) - { - absolute_layer_number += Raft::getTotalExtraLayers(); - } + const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); + const bool adhesion_raft = raft_total_extra_layers > 0; + LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + raft_total_extra_layers; if (! adhesion_raft || absolute_layer_number > 0) { @@ -324,6 +307,24 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) } } +const Polygons& PrimeTower::getOuterPoly(const LayerIndex& layer_nr) const +{ + const LayerIndex absolute_layer_nr = layer_nr + Raft::getTotalExtraLayers(); + if (absolute_layer_nr < outer_poly_base.size()) + { + return outer_poly_base[absolute_layer_nr]; + } + else + { + return outer_poly; + } +} + +const Polygons& PrimeTower::getGroundPoly() const +{ + return getOuterPoly(-Raft::getTotalExtraLayers()); +} + void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const int extruder_nr) const { int current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations) + number_of_prime_tower_start_locations) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 2ec4d932c2..522e0994f5 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -118,6 +118,7 @@ void SkirtBrim::generate() constexpr LayerIndex layer_nr = 0; constexpr bool include_support = true; const bool include_prime_tower = adhesion_type == EPlatformAdhesion::SKIRT; + const bool has_prime_tower = storage.primeTower.enabled; Polygons covered_area = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, /*external_polys_only*/ false); std::vector allowed_areas_per_extruder(extruder_count); @@ -135,6 +136,11 @@ void SkirtBrim::generate() // so that the brim lines don't overlap with the holes by half the line width allowed_areas_per_extruder[extruder_nr] = allowed_areas_per_extruder[extruder_nr].difference(getInternalHoleExclusionArea(covered_area, extruder_nr)); } + + if (has_prime_tower) + { + allowed_areas_per_extruder[extruder_nr] = allowed_areas_per_extruder[extruder_nr].difference(storage.primeTower.getGroundPoly()); + } } // Apply 'approximate convex hull' if the adhesion is skirt _after_ any skirt but also prime-tower-brim adhesion. @@ -344,12 +350,12 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) const int primary_line_count = line_count[reference_extruder_nr]; const bool external_only = adhesion_type == EPlatformAdhesion::SKIRT || external_polys_only[reference_extruder_nr]; // Whether to include holes or not. Skirt doesn't have any holes. + const bool has_prime_tower = storage.primeTower.enabled; const LayerIndex layer_nr = 0; if (adhesion_type == EPlatformAdhesion::SKIRT) { constexpr bool include_support = true; - const bool skirt_around_prime_tower_brim = storage.primeTower.enabled && global_settings.get("prime_tower_brim_enable"); - const bool include_prime_tower = ! skirt_around_prime_tower_brim; // include manually otherwise + const bool include_prime_tower = ! has_prime_tower; // include manually otherwise first_layer_outline = Polygons(); int skirt_height = 0; @@ -371,10 +377,9 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) } } - - if (skirt_around_prime_tower_brim) + if (has_prime_tower) { - first_layer_outline = first_layer_outline.unionPolygons(storage.primeTower.footprint); + first_layer_outline = first_layer_outline.unionPolygons(storage.primeTower.getGroundPoly()); } Polygons shields; diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 6ddcef2c93..aea31de82c 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -150,7 +150,7 @@ TreeModelVolumes::TreeModelVolumes( if (storage.primeTower.enabled) { - anti_overhang_[layer_idx].add(storage.primeTower.outer_poly); + anti_overhang_[layer_idx].add(storage.primeTower.getGroundPoly()); } anti_overhang_[layer_idx] = anti_overhang_[layer_idx].unionPolygons(); }); diff --git a/src/raft.cpp b/src/raft.cpp index bee9cc643b..af8daafde0 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -8,7 +8,6 @@ #include "Slice.h" #include "settings/EnumSettings.h" //For EPlatformAdhesion. #include "sliceDataStorage.h" -#include "support.h" #include "utils/math.h" #include @@ -65,15 +64,6 @@ void Raft::generate(SliceDataStorage& storage) return; } } - - const coord_t prime_tower_distance = settings.get("prime_tower_base_size"); - storage.primeRaftOutline = storage.primeTower.outer_poly.offset(prime_tower_distance, ClipperLib::jtRound); - if (settings.get("raft_remove_inside_corners")) - { - storage.primeRaftOutline = storage.primeRaftOutline.unionPolygons(storage.raftOutline); - storage.primeRaftOutline.makeConvex(); - } - storage.primeRaftOutline = storage.primeRaftOutline.difference(storage.raftOutline); // In case of overlaps. } coord_t Raft::getTotalThickness() diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index e5ba324ccb..c560a2b675 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -333,14 +333,7 @@ Polygons { if (primeTower.enabled) { - if (layer_nr == 0) - { - total.add(primeTower.footprint); - } - else - { - total.add(primeTower.outer_poly); - } + total.add(primeTower.getOuterPoly(layer_nr)); } } return total; From 23080f9069ad7046a43643fb391d1f121fbe0d1b Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Thu, 12 Oct 2023 13:44:20 +0000 Subject: [PATCH 581/656] Applied clang-format. --- include/PrimeTower.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 9133d3007a..63a84a254f 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -110,12 +110,12 @@ class PrimeTower */ void subtractFromSupport(SliceDataStorage& storage); - const Polygons &getOuterPoly(const LayerIndex &layer_nr) const; + const Polygons& getOuterPoly(const LayerIndex& layer_nr) const; - const Polygons &getGroundPoly() const; + const Polygons& getGroundPoly() const; private: - static ExtrusionMoves generatePaths_base(const Polygons &inset, size_t rings, coord_t line_width); + static ExtrusionMoves generatePaths_base(const Polygons& inset, size_t rings, coord_t line_width); static ExtrusionMoves generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset); From 01c239b7dc965d98f06c1009e70c8741184d1fd6 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 07:43:29 +0200 Subject: [PATCH 582/656] Fixed prime tower overlapping support CURA-10783 --- src/PrimeTower.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 39c6ae3fe2..5166fe5bc1 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -297,10 +297,10 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { - const Polygons outside_polygon = outer_poly.getOutsidePolygons(); - AABB outside_polygon_boundary_box(outside_polygon); for (size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) { + const Polygons outside_polygon = getOuterPoly(layer).getOutsidePolygons(); + AABB outside_polygon_boundary_box(outside_polygon); SupportLayer& support_layer = storage.support.supportLayers[layer]; // take the differences of the support infill parts and the prime tower area support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box); From c874b2b3cd38384fe8bb2443ad451e3c82e3a8e3 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 10:54:08 +0200 Subject: [PATCH 583/656] Code documentation and cleaning CURA-10783 --- include/PrimeTower.h | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 63a84a254f..2201ce7544 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -41,17 +41,10 @@ class PrimeTower const unsigned int number_of_prime_tower_start_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations std::vector pattern_per_extruder; //!< For each extruder the pattern to print on all layers of the prime tower. - std::vector> pattern_extra_brim_per_layer; //!< For each layer of each extruder with an extra brim, the pattern to be added + std::vector> pattern_extra_brim_per_layer; //!< For each layer of each extruder, the extra pattern to be added for adhesion and/or strength Polygons outer_poly; //!< The outline of the outermost prime tower. - - struct BasePolygon - { - Polygons polygons; - size_t extra_rings; - }; - - std::vector outer_poly_base; //!< The outline of the prime tower for layers having a base + std::vector outer_poly_base; //!< The outline of the layers having extra width for the base public: bool enabled; //!< Whether the prime tower is enabled. @@ -110,17 +103,46 @@ class PrimeTower */ void subtractFromSupport(SliceDataStorage& storage); + /*! + * Get the outer polygon for the given layer, which may be the priming polygon only, or a larger polygon for layers with a base + * + * \param[in] layer_nr The index of the layer + * \return The outer polygon for the prime tower at the given layer + */ const Polygons& getOuterPoly(const LayerIndex& layer_nr) const; + /*! + * Get the outer polygon for the very first layer, which may be the priming polygon only, or a larger polygon if there is a base + */ const Polygons& getGroundPoly() const; private: + /*! + * \see PrimeTower::generatePaths + * + * Generate extra rings around the actual prime rings for a stronger base + * + * \param inset The inner circle of the rings to start generating the rings from + * \param rings The number of rings to add + * \param line_width The actual line width to distance the rings from each other + * \return The generated rings paths + */ static ExtrusionMoves generatePaths_base(const Polygons& inset, size_t rings, coord_t line_width); + /*! + * \see PrimeTower::generatePaths + * + * Generate extra rings inside the given circle for a better adhesion on the first layer + * + * \param outer_poly The outer polygon to start generating the rings from + * \param line_width The actual line width to distance the rings from each other + * \param initial_inset The inset distance to be added to the first generated ring + * \return The generated rings paths + */ static ExtrusionMoves generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset); /*! - * \see WipeTower::generatePaths + * \see PrimeTower::generatePaths * * Generate the extrude paths for each extruder on even and odd layers * Fill the ground poly with dense infill. From 0400d0a03fe2ccf534650f3938f1b7c0fc270715 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 11:01:23 +0200 Subject: [PATCH 584/656] Revert dirty fix which does not seem necessary anymore CURA-10783 --- src/LayerPlanBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index b5bede2665..6551543bdc 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -429,7 +429,7 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex return; } - assert((time_window >= -0.001 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); + assert((time_window >= 0 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); // ,layer change . // : ,precool command ,layer change . From 466a1cbfca6ffef0c0a400847bb2dd6e1cceec07 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 11:38:18 +0200 Subject: [PATCH 585/656] More code documentation CURA-10783 --- src/FffGcodeWriter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 0deaaff908..8c16b8d8bc 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -624,6 +624,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Polygons raft_outline_path = storage.raftOutline; if (storage.primeTower.enabled) { + // Base layer is shared with prime tower base raft_outline_path = raft_outline_path.unionPolygons(storage.primeTower.getOuterPoly(layer_nr)); } @@ -747,6 +748,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) if (storage.primeTower.enabled) { + // Interface layer excludes prime tower base raft_outline_path = raft_outline_path.difference(storage.primeTower.getOuterPoly(layer_nr)); } @@ -851,6 +853,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) if (storage.primeTower.enabled) { + // Surface layers exclude prime tower base raft_outline_path = raft_outline_path.difference(storage.primeTower.getOuterPoly(layer_nr)); } From 9eab6b4f413dc76e1eab21bc5315214f172f1097 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 11:38:46 +0200 Subject: [PATCH 586/656] Removed useless dirty fix CURA-10783 --- src/raft.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/raft.cpp b/src/raft.cpp index af8daafde0..665cc833f5 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -104,14 +104,7 @@ coord_t Raft::getFillerLayerHeight() return normal_layer_height; } - if (getFillerLayerCount() != 0) - { - return round_divide(getZdiffBetweenRaftAndLayer0(), getFillerLayerCount()); - } - else - { - return mesh_group_settings.get("layer_height"); - } + return round_divide(getZdiffBetweenRaftAndLayer0(), getFillerLayerCount()); } From ae0dcd24b522b1d4984c23fbff33b97c2d2a1e20 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 13:51:02 +0200 Subject: [PATCH 587/656] Re-set dirty crash fix CURA-10783 --- src/LayerPlanBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index 6551543bdc..b5bede2665 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -429,7 +429,7 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector& ex return; } - assert((time_window >= 0 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); + assert((time_window >= -0.001 || last_extruder_plan.estimates.material == 0) && "Time window should always be positive if we actually extrude"); // ,layer change . // : ,precool command ,layer change . From 9e958a99e1e33fc559a776754450342b2cad723f Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 13 Oct 2023 14:39:25 +0200 Subject: [PATCH 588/656] Force prime tower base for raft CURA-10783 --- src/PrimeTower.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 5166fe5bc1..fa291fedc4 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -149,7 +149,8 @@ void PrimeTower::generatePaths_denseInfill() const coord_t layer_height = mesh_group_settings.get("layer_height"); const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); - const coord_t base_height = scene.settings.get("prime_tower_base_height"); + const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; + const coord_t base_height = std::max(scene.settings.get("prime_tower_base_height"), has_raft ? layer_height : 0); const int base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); const coord_t line_width = scene.extruders[extruder_order.front()].settings.get("prime_tower_line_width"); @@ -180,7 +181,7 @@ void PrimeTower::generatePaths_denseInfill() } // The most outside extruder is used for the base - if (extruder_nr == extruder_order.front() && base_enabled && base_extra_radius > 0 && base_height > 0) + if (extruder_nr == extruder_order.front() && (base_enabled || has_raft) && base_extra_radius > 0 && base_height > 0) { for (coord_t z = 0; z < base_height; z += layer_height) { From ad1aa1972ad9e33d86f66baee47478cb6e68d9c7 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 3 Oct 2023 12:24:18 +0200 Subject: [PATCH 589/656] Reverted the changes done in CURA-9521 CURA-11109 --- include/FffGcodeWriter.h | 3 +-- include/infill.h | 7 ++----- src/FffGcodeWriter.cpp | 9 +++------ src/infill.cpp | 10 ++++------ 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 0e3dea645c..96f504c1e3 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -569,8 +569,7 @@ class FffGcodeWriter : public NoCopy const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, - const bool is_bridge_skin = false) const; + double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to diff --git a/include/infill.h b/include/infill.h index 40ca7fbd59..289a0850bb 100644 --- a/include/infill.h +++ b/include/infill.h @@ -206,8 +206,7 @@ class Infill const std::shared_ptr& cross_fill_provider = nullptr, const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr, - const Polygons& prevent_small_exposed_to_air = Polygons(), - const bool is_bridge_skin = false); + const Polygons& prevent_small_exposed_to_air = Polygons()); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -219,7 +218,6 @@ class Infill * \param line_width [in] The optimum wall line width of the walls * \param infill_overlap [in] The overlap of the infill * \param settings [in] A settings storage to use for generating variable-width walls. - * \param is_bridge_skin [in] Setting to filter out the extra skin walls while bridging * \return The inner contour of the wall toolpaths */ static Polygons generateWallToolPaths( @@ -230,8 +228,7 @@ class Infill const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type, - const bool is_bridge_skin = false); + SectionType section_type); private: /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index e497b08a1f..55925dce5e 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2727,8 +2727,7 @@ void FffGcodeWriter::processTopBottom( skin_density, monotonic, added_something, - fan_speed, - is_bridge_skin); + fan_speed); } void FffGcodeWriter::processSkinPrintFeature( @@ -2745,8 +2744,7 @@ void FffGcodeWriter::processSkinPrintFeature( const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed, - const bool is_bridge_skin) const + double fan_speed) const { Polygons skin_polygons; Polygons skin_lines; @@ -2806,8 +2804,7 @@ void FffGcodeWriter::processSkinPrintFeature( nullptr, nullptr, nullptr, - small_areas_on_surface ? Polygons() : exposed_to_air, - is_bridge_skin); + small_areas_on_surface ? Polygons() : exposed_to_air); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) diff --git a/src/infill.cpp b/src/infill.cpp index 9137aa66ca..6da3c25c4f 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -60,14 +60,13 @@ Polygons Infill::generateWallToolPaths( const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type, - const bool is_bridge_skin) + SectionType section_type) { outer_contour = outer_contour.offset(infill_overlap); scripta::log("infill_outer_contour", outer_contour, section_type, layer_idx, scripta::CellVDI{ "infill_overlap", infill_overlap }); Polygons inner_contour; - if ((wall_line_count > 0) && (! is_bridge_skin)) + if (wall_line_count > 0) { constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. WallToolPaths wall_toolpaths(outer_contour, line_width, wall_line_count, wall_0_inset, settings, layer_idx, section_type); @@ -91,15 +90,14 @@ void Infill::generate( const std::shared_ptr& cross_fill_provider, const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh, - const Polygons& prevent_small_exposed_to_air, - const bool is_bridge_skin) + const Polygons& prevent_small_exposed_to_air) { if (outer_contour.empty()) { return; } - inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type, is_bridge_skin); + inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type); scripta::log("infill_inner_contour_0", inner_contour, section_type, layer_idx); // It does not make sense to print a pattern in a small region. So the infill region From 88e0f4c9bb8dc755f0209da71c432b974a63fde6 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 25 Sep 2023 13:53:00 +0200 Subject: [PATCH 590/656] Support brim printed only at layer 0 CURA-10968 --- src/FffGcodeWriter.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 55925dce5e..a0ba973c15 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,24 +1232,28 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer + //support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + if (layer_nr == 0) { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {}); + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + { + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); + } } } From 54e4592ba6d80740eb917b9df8077a1bcf6c34c7 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Mon, 25 Sep 2023 11:53:42 +0000 Subject: [PATCH 591/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index a0ba973c15..eab1cb7b29 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,7 +1232,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer - //support brim is only added in layer 0 + // support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. if (layer_nr == 0) { From 7a3aa045a9e87d835c450c38db67fd307af84d9e Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 25 Sep 2023 13:55:28 +0200 Subject: [PATCH 592/656] Comment fix CURA-10968 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index eab1cb7b29..fe08adf389 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,7 +1232,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer - // support brim is only added in layer 0 + // Support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. if (layer_nr == 0) { From ff858f3f80d6fdd515b7e3cbce33bb272e480170 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 29 Sep 2023 11:32:03 +0200 Subject: [PATCH 593/656] Comment fix CURA-10968 --- src/FffGcodeWriter.cpp | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe08adf389..50429e303b 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1234,26 +1234,23 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer // Support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. - if (layer_nr == 0) + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if ((layer_nr == 0) && (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr)) { - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) - { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {}); - } + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); } } From 0b18634a5e858e00ba026b9eadcb404e724ef14f Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 3 Oct 2023 19:39:58 +0200 Subject: [PATCH 594/656] Use correct path config CURA-11119 CURA-11121 --- src/FffGcodeWriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 50429e303b..6ac183f0f2 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2831,10 +2831,10 @@ void FffGcodeWriter::processSkinPrintFeature( gcode_layer, mesh.settings, extruder_nr, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, + config, + config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, From 09bbf91171c218df592f2cbe6dd8f3e71857987c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Oct 2023 09:54:12 +0200 Subject: [PATCH 595/656] Change seam position scoring to make it more consistent CURA-11100 --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 93198fcd19..464810cefa 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -646,7 +646,7 @@ class PathOrderOptimizer // so the user has some control over where the seam will lie. // the divisor here may need adjusting to obtain the best results (TBD) - corner_shift = score_distance / 10; + corner_shift = score_distance / 50; } float score = score_distance; From 69ff2c953a3e724843c950cf95a1ca7889822b4b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Oct 2023 17:18:08 +0200 Subject: [PATCH 596/656] Better version of the corner angle computation (to be optimized) CURA-11100 --- include/PathOrderOptimizer.h | 114 +++++++++++++++++------------------ src/FffGcodeWriter.cpp | 2 +- 2 files changed, 56 insertions(+), 60 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 464810cefa..b269b1e063 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -136,7 +136,7 @@ class PathOrderOptimizer * This reorders the \ref paths field and fills their starting vertices and * directions. */ - void optimize() + void optimize(bool precompute_start = true) { if(paths.empty()) { @@ -188,7 +188,7 @@ class PathOrderOptimizer //For some Z seam types the start position can be pre-computed. //This is faster since we don't need to re-compute the start position at each step then. - const bool precompute_start = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; + precompute_start &= seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; if(precompute_start) { for(auto& path : paths) @@ -656,13 +656,13 @@ class PathOrderOptimizer case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: if(corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. { - score -= (-corner_angle + 1.0) * corner_shift; + score += corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: if(corner_angle > 0) // Indeed a convex corner? { - score -= (corner_angle + 1.0) * corner_shift; + score -= corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: @@ -707,6 +707,43 @@ class PathOrderOptimizer return best_i; } + Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance) + { + assert(distance); + + const Point here_pos = (*path.converted)[here]; + int direction = distance > 0 ? 1 : -1; + distance = std::abs(distance); + + coord_t actual_distance = 0; + int actual_delta = 0; + + Point prev_pos = here_pos; + Point next_pos; + while(actual_distance < distance) + { + actual_delta += direction; + next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + actual_distance += vSize(next_pos - prev_pos); + prev_pos = next_pos; + } + + if(actual_distance > distance) // Which is veeeery likely + { + prev_pos = (*path.converted)[(here + actual_delta -direction + path.converted->size()) % path.converted->size()]; + + Point vector = next_pos - prev_pos; + coord_t vector_size = vSize(vector); + Point unit_vector = (vector * 1000) / vector_size; + Point vector_delta = unit_vector * (vector_size - (actual_distance - distance)); + return prev_pos + vector_delta / 1000; + } + else + { + return next_pos; + } + } + /*! * Some models have very sharp corners, but also have a high resolution. If a sharp corner * consists of many points each point individual might have a shallow corner, but the @@ -722,68 +759,27 @@ class PathOrderOptimizer */ float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) { - // If the edge length becomes too small we cannot accurately calculate the angle - // define a minimum edge length, so we don't get deviant values in the angle calculations - constexpr coord_t min_edge_length = 10; - constexpr coord_t min_edge_length2 = min_edge_length * min_edge_length; + static constexpr coord_t distance_step = 2000; + static constexpr coord_t max_distance = 5000; - const int offset_index = i % path.converted->size(); - Point here = (*path.converted)[offset_index]; + const Point here = (*path.converted)[i]; - const std::function find_neighbour_point = [&offset_index, &path](const int direction, const Point& here) - { - int offset_index_ = offset_index; - Point neighbour; - do - { - offset_index_ = (offset_index_ + path.converted->size() + direction) % path.converted->size(); - neighbour = (*path.converted)[offset_index_]; - } - while (vSize2(here - neighbour) < min_edge_length2 && offset_index_ != offset_index); // find previous point that is at least min_edge_length units away from here - return neighbour; - }; - - const std::function iterate_to_previous_point = [&find_neighbour_point](Point& previous_, Point& here_, Point& next_) - { - const auto dist = vSize(here_ - next_); - next_ = here_; - here_ = previous_; - previous_ = find_neighbour_point(-1, here_); - return dist; - }; - Point previous = find_neighbour_point(-1, here); + float angle = 0.0; + int computed_angles = 0; - const std::function iterate_to_next_point = [&find_neighbour_point](Point& previous_, Point& here_, Point& next_) + for(coord_t distance = distance_step ; distance <= max_distance ; distance += distance_step) { - const auto dist = vSize(here_ - previous_); - previous_ = here_; - here_ = next_; - next_ = find_neighbour_point(1, here_); - return dist; - }; - Point next = find_neighbour_point(1, here); + Point next = findNeighbourPoint(path, i, distance); + Point previous = findNeighbourPoint(path, i, -distance); - float corner_angle = LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - - for (const auto& iterate_func : {iterate_to_previous_point, iterate_to_next_point}) - { - Point next_ = next; - Point here_ = here; - Point previous_ = previous; - for - ( - coord_t distance_to_query = iterate_func(previous_, here_, next_); - distance_to_query < angle_query_distance && here_ != here; - distance_to_query += iterate_func(previous_, here_, next_) - ) - { - // angles further away from the query point are weighted less - const float angle_weight = 1.0 - pow(distance_to_query / angle_query_distance, fall_off_strength); - corner_angle += (LinearAlg2D::getAngleLeft(previous_, here_, next_) - M_PI) * angle_weight; - } + angle += LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; + computed_angles++; } - return corner_angle / M_PI; // Limit angle between -1 and 1. + angle /= computed_angles; + angle /= M_PI; + + return angle; } /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 6ac183f0f2..aedd5d439b 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1493,7 +1493,7 @@ void FffGcodeWriter::addMeshLayerToGCode( { part_order_optimizer.addPolygon(&part); } - part_order_optimizer.optimize(); + part_order_optimizer.optimize(false); for (const PathOrdering& path : part_order_optimizer.paths) { addMeshPartToGCode(storage, mesh, extruder_nr, mesh_config, *path.vertices, gcode_layer); From 82282725e5894dd3e776e37feba8e56e00ca8c2c Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Thu, 5 Oct 2023 15:18:51 +0000 Subject: [PATCH 597/656] Applied clang-format. --- include/PathOrderOptimizer.h | 302 +++++++++++++++++++---------------- 1 file changed, 160 insertions(+), 142 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index b269b1e063..939baf6b21 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -4,9 +4,6 @@ #ifndef PATHORDEROPTIMIZER_H #define PATHORDEROPTIMIZER_H -#include -#include - #include "InsetOrderOptimizer.h" // for makeOrderIncludeTransitive #include "PathOrdering.h" #include "pathPlanning/CombPath.h" //To calculate the combing distance if we want to use combing. @@ -16,12 +13,16 @@ #include "utils/linearAlg2D.h" //To find the angle of corners to hide seams. #include "utils/polygonUtils.h" #include "utils/views/dfs.h" -#include -#include + #include #include -#include #include +#include +#include +#include +#include + +#include namespace cura { @@ -99,10 +100,17 @@ class PathOrderOptimizer * it into a polygon. * \param combing_boundary Boundary to avoid when making travel moves. */ - PathOrderOptimizer(const Point start_point, const ZSeamConfig seam_config = ZSeamConfig(), const bool detect_loops = false, const Polygons* combing_boundary = nullptr, const bool reverse_direction = false, const std::unordered_multimap& order_requirements = no_order_requirements, const bool group_outer_walls = false) + PathOrderOptimizer( + const Point start_point, + const ZSeamConfig seam_config = ZSeamConfig(), + const bool detect_loops = false, + const Polygons* combing_boundary = nullptr, + const bool reverse_direction = false, + const std::unordered_multimap& order_requirements = no_order_requirements, + const bool group_outer_walls = false) : start_point(start_point) , seam_config(seam_config) - , combing_boundary((combing_boundary != nullptr && !combing_boundary->empty()) ? combing_boundary : nullptr) + , combing_boundary((combing_boundary != nullptr && ! combing_boundary->empty()) ? combing_boundary : nullptr) , detect_loops(detect_loops) , reverse_direction(reverse_direction) , order_requirements(&order_requirements) @@ -138,70 +146,70 @@ class PathOrderOptimizer */ void optimize(bool precompute_start = true) { - if(paths.empty()) + if (paths.empty()) { return; } - //Get the vertex data and store it in the paths. - for(auto& path : paths) + // Get the vertex data and store it in the paths. + for (auto& path : paths) { path.converted = path.getVertexData(); vertices_to_paths.emplace(path.vertices, &path); } - //If necessary, check polylines to see if they are actually polygons. - if(detect_loops) + // If necessary, check polylines to see if they are actually polygons. + if (detect_loops) { - for(auto& path : paths) + for (auto& path : paths) { - if(!path.is_closed) + if (! path.is_closed) { - //If we want to detect chains, first check if some of the polylines are secretly polygons. - path.is_closed = isLoopingPolyline(path); //If it is, we'll set the seam position correctly later. + // If we want to detect chains, first check if some of the polylines are secretly polygons. + path.is_closed = isLoopingPolyline(path); // If it is, we'll set the seam position correctly later. } } } - - //Add all vertices to a bucket grid so that we can find nearby endpoints quickly. + + // Add all vertices to a bucket grid so that we can find nearby endpoints quickly. const coord_t snap_radius = 10_mu; // 0.01mm grid cells. Chaining only needs to consider polylines which are next to each other. SparsePointGridInclusive line_bucket_grid(snap_radius); - for(const auto& [i, path]: paths | ranges::views::enumerate) + for (const auto& [i, path] : paths | ranges::views::enumerate) { if (path.converted->empty()) { continue; } - if(path.is_closed) + if (path.is_closed) { - for(const Point& point : *path.converted) + for (const Point& point : *path.converted) { - line_bucket_grid.insert(point, i); //Store by index so that we can also mark them down in the `picked` vector. + line_bucket_grid.insert(point, i); // Store by index so that we can also mark them down in the `picked` vector. } } - else //For polylines, only insert the endpoints. Those are the only places we can start from so the only relevant vertices to be near to. + else // For polylines, only insert the endpoints. Those are the only places we can start from so the only relevant vertices to be near to. { line_bucket_grid.insert(path.converted->front(), i); line_bucket_grid.insert(path.converted->back(), i); } } - //For some Z seam types the start position can be pre-computed. - //This is faster since we don't need to re-compute the start position at each step then. + // For some Z seam types the start position can be pre-computed. + // This is faster since we don't need to re-compute the start position at each step then. precompute_start &= seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; - if(precompute_start) + if (precompute_start) { - for(auto& path : paths) + for (auto& path : paths) { - if(!path.is_closed || path.converted->empty()) + if (! path.is_closed || path.converted->empty()) { - continue; //Can't pre-compute the seam for open polylines since they're at the endpoint nearest to the current position. + continue; // Can't pre-compute the seam for open polylines since they're at the endpoint nearest to the current position. } path.start_vertex = findStartLocation(path, seam_config.pos); } } - std::vector optimized_order; //To store our result in. At the end we'll std::swap. + std::vector optimized_order; // To store our result in. At the end we'll std::swap. if (order_requirements->empty()) { @@ -213,9 +221,9 @@ class PathOrderOptimizer } - if(reverse_direction && order_requirements->empty()) + if (reverse_direction && order_requirements->empty()) { - std::vector reversed = reverseOrderPaths(optimized_order); //Reverse-insert the optimized order, to invert the ordering. + std::vector reversed = reverseOrderPaths(optimized_order); // Reverse-insert the optimized order, to invert the ordering. std::swap(reversed, paths); } else @@ -225,6 +233,7 @@ class PathOrderOptimizer combing_grid.reset(); } + protected: /*! * If \ref detect_loops is enabled, endpoints of polylines that are closer @@ -276,18 +285,24 @@ class PathOrderOptimizer std::vector getOptimizedOrder(SparsePointGridInclusive line_bucket_grid, size_t snap_radius) { - std::vector optimized_order; //To store our result in. + std::vector optimized_order; // To store our result in. Point current_position = start_point; - std::unordered_map picked(paths.size()); //Fixed size boolean flag for whether each path is already in the optimized vector. + std::unordered_map picked(paths.size()); // Fixed size boolean flag for whether each path is already in the optimized vector. - auto isPicked = [&picked](OrderablePath* c) { return picked[c]; }; - auto notPicked = [&picked](OrderablePath* c) { return !picked[c]; }; + auto isPicked = [&picked](OrderablePath* c) + { + return picked[c]; + }; + auto notPicked = [&picked](OrderablePath* c) + { + return ! picked[c]; + }; - while(optimized_order.size() < paths.size()) + while (optimized_order.size() < paths.size()) { - //Use bucket grid to find paths within snap_radius + // Use bucket grid to find paths within snap_radius std::vector nearby_candidates; for (const auto i : line_bucket_grid.getNearbyVals(current_position, snap_radius)) { @@ -296,14 +311,14 @@ class PathOrderOptimizer std::vector available_candidates; available_candidates.reserve(nearby_candidates.size()); - for(auto candidate : nearby_candidates | ranges::views::filter(notPicked)) + for (auto candidate : nearby_candidates | ranges::views::filter(notPicked)) { available_candidates.push_back(candidate); } - if(available_candidates.empty()) // We need to broaden our search through all candidates + if (available_candidates.empty()) // We need to broaden our search through all candidates { - for(auto path : paths | ranges::views::addressof | ranges::views::filter(notPicked)) + for (auto path : paths | ranges::views::addressof | ranges::views::filter(notPicked)) { available_candidates.push_back(path); } @@ -315,15 +330,15 @@ class PathOrderOptimizer optimized_order.push_back(*best_path); picked[best_path] = true; - if(!best_path->converted->empty()) //If all paths were empty, the best path is still empty. We don't upate the current position then. + if (! best_path->converted->empty()) // If all paths were empty, the best path is still empty. We don't upate the current position then. { - if(best_path->is_closed) + if (best_path->is_closed) { - current_position = (*best_path->converted)[best_path->start_vertex]; //We end where we started. + current_position = (*best_path->converted)[best_path->start_vertex]; // We end where we started. } else { - //Pick the other end from where we started. + // Pick the other end from where we started. current_position = best_path->start_vertex == 0 ? best_path->converted->back() : best_path->converted->front(); } } @@ -332,9 +347,10 @@ class PathOrderOptimizer return optimized_order; } - std::vector getOptimizerOrderWithConstraints(SparsePointGridInclusive line_bucket_grid, size_t snap_radius, const std::unordered_multimap& order_requirements) + std::vector + getOptimizerOrderWithConstraints(SparsePointGridInclusive line_bucket_grid, size_t snap_radius, const std::unordered_multimap& order_requirements) { - std::vector optimized_order; //To store our result in. + std::vector optimized_order; // To store our result in. // initialize the roots set with all possible nodes std::unordered_set roots; @@ -358,8 +374,8 @@ class PathOrderOptimizer std::unordered_set visited; Point current_position = start_point; - std::function(const Path, const std::unordered_multimap&)> get_neighbours = - [current_position, this](const Path current_node, const std::unordered_multimap& graph) + std::function(const Path, const std::unordered_multimap&)> get_neighbours + = [current_position, this](const Path current_node, const std::unordered_multimap& graph) { std::vector order; // Output order to traverse neighbors @@ -382,13 +398,13 @@ class PathOrderOptimizer // update local_current_position auto path = vertices_to_paths[best_candidate]; - if(path->is_closed) + if (path->is_closed) { - local_current_position = (*path->converted)[path->start_vertex]; //We end where we started. + local_current_position = (*path->converted)[path->start_vertex]; // We end where we started. } else { - //Pick the other end from where we started. + // Pick the other end from where we started. local_current_position = path->start_vertex == 0 ? path->converted->back() : path->converted->front(); } } @@ -396,34 +412,33 @@ class PathOrderOptimizer return order; }; - const std::function handle_node = - [¤t_position, &optimized_order, this] - (const Path current_node, const std::nullptr_t _state) + const std::function handle_node + = [¤t_position, &optimized_order, this](const Path current_node, const std::nullptr_t _state) + { + // We should make map from node <-> path for this stuff + for (auto& path : paths) { - // We should make map from node <-> path for this stuff - for (auto& path : paths) + if (path.vertices == current_node) { - if (path.vertices == current_node) + if (path.is_closed) { - if(path.is_closed) - { - current_position = (*path.converted)[path.start_vertex]; //We end where we started. - } - else - { - //Pick the other end from where we started. - current_position = path.start_vertex == 0 ? path.converted->back() : path.converted->front(); - } - - // Add to optimized order - optimized_order.push_back(path); - - break; + current_position = (*path.converted)[path.start_vertex]; // We end where we started. } + else + { + // Pick the other end from where we started. + current_position = path.start_vertex == 0 ? path.converted->back() : path.converted->front(); + } + + // Add to optimized order + optimized_order.push_back(path); + + break; } + } - return nullptr; - }; + return nullptr; + }; if (group_outer_walls) { @@ -482,7 +497,7 @@ class PathOrderOptimizer } else { - while (!roots.empty()) + while (! roots.empty()) { Path root = findClosestPathVertices(current_position, roots); roots.erase(root); @@ -497,13 +512,13 @@ class PathOrderOptimizer std::vector reverseOrderPaths(std::vector pathsOrderPaths) { std::vector reversed; - //Don't replace with swap, assign or insert. They require functions that we can't implement for all template arguments for Path. + // Don't replace with swap, assign or insert. They require functions that we can't implement for all template arguments for Path. reversed.reserve(pathsOrderPaths.size()); - for(auto& path: pathsOrderPaths | ranges::views::reverse) + for (auto& path : pathsOrderPaths | ranges::views::reverse) { reversed.push_back(path); - reversed.back().backwards = !reversed.back().backwards; - if(!reversed.back().is_closed) + reversed.back().backwards = ! reversed.back().backwards; + if (! reversed.back().is_closed) { reversed.back().start_vertex = reversed.back().converted->size() - 1 - reversed.back().start_vertex; } @@ -530,33 +545,35 @@ class PathOrderOptimizer coord_t best_distance2 = std::numeric_limits::max(); OrderablePath* best_candidate = 0; - for(OrderablePath* path : candidate_paths) + for (OrderablePath* path : candidate_paths) { - if(path->converted->empty()) //No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. + if (path->converted->empty()) // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. { - if(best_distance2 == std::numeric_limits::max()) + if (best_distance2 == std::numeric_limits::max()) { best_candidate = path; } continue; } - const bool precompute_start = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; - if(!path->is_closed || !precompute_start) //Find the start location unless we've already precomputed it. + const bool precompute_start + = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; + if (! path->is_closed || ! precompute_start) // Find the start location unless we've already precomputed it. { path->start_vertex = findStartLocation(*path, start_position); - if(!path->is_closed) //Open polylines start at vertex 0 or vertex N-1. Indicate that they should be reversed if they start at N-1. + if (! path->is_closed) // Open polylines start at vertex 0 or vertex N-1. Indicate that they should be reversed if they start at N-1. { path->backwards = path->start_vertex > 0; } } const Point candidate_position = (*path->converted)[path->start_vertex]; coord_t distance2 = getDirectDistance(start_position, candidate_position); - if(distance2 < best_distance2 && combing_boundary) //If direct distance is longer than best combing distance, the combing distance can never be better, so only compute combing if necessary. + if (distance2 < best_distance2 + && combing_boundary) // If direct distance is longer than best combing distance, the combing distance can never be better, so only compute combing if necessary. { distance2 = getCombingDistance(start_position, candidate_position); } - if(distance2 < best_distance2) //Closer than the best candidate so far. + if (distance2 < best_distance2) // Closer than the best candidate so far. { best_candidate = path; best_distance2 = distance2; @@ -589,30 +606,31 @@ class PathOrderOptimizer * applicable. * \param is_closed Whether the polygon is closed (a polygon) or not * (a polyline). If the path is not closed, it will choose between the two - * endpoints rather than + * endpoints rather than * \return An index to a vertex in that path where printing must start. */ size_t findStartLocation(const OrderablePath& path, const Point& target_pos) { - if(!path.is_closed) + if (! path.is_closed) { - //For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. - const coord_t back_distance = (combing_boundary == nullptr) - ? getDirectDistance(path.converted->back(), target_pos) - : getCombingDistance(path.converted->back(), target_pos); - if(back_distance < getDirectDistance(path.converted->front(), target_pos) || (combing_boundary && back_distance < getCombingDistance(path.converted->front(), target_pos))) //Lazy or: Only compute combing distance if direct distance is closer. + // For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. + const coord_t back_distance + = (combing_boundary == nullptr) ? getDirectDistance(path.converted->back(), target_pos) : getCombingDistance(path.converted->back(), target_pos); + if (back_distance < getDirectDistance(path.converted->front(), target_pos) + || (combing_boundary + && back_distance < getCombingDistance(path.converted->front(), target_pos))) // Lazy or: Only compute combing distance if direct distance is closer. { - return path.converted->size() - 1; //Back end is closer. + return path.converted->size() - 1; // Back end is closer. } else { - return 0; //Front end is closer. + return 0; // Front end is closer. } } - //Rest of the function only deals with (closed) polygons. We need to be able to find the seam location of those polygons. + // Rest of the function only deals with (closed) polygons. We need to be able to find the seam location of those polygons. - if(seam_config.type == EZSeamType::RANDOM) + if (seam_config.type == EZSeamType::RANDOM) { size_t vert = getRandomPointInPolygon(*path.converted); return vert; @@ -620,14 +638,14 @@ class PathOrderOptimizer size_t best_i; float best_score = std::numeric_limits::infinity(); - for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - //For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) - ? getDirectDistance(here, target_pos) - : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + // For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) + ? MM2INT(10) + : vSize2(here - target_pos); float corner_angle = cornerAngle(path, i); // angles < 0 are concave (left turning) @@ -650,30 +668,30 @@ class PathOrderOptimizer } float score = score_distance; - switch(seam_config.corner_pref) + switch (seam_config.corner_pref) { default: case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: - if(corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. + if (corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. { score += corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: - if(corner_angle > 0) // Indeed a convex corner? + if (corner_angle > 0) // Indeed a convex corner? { score -= corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: - score -= std::abs(corner_angle) * corner_shift; //Still give sharper corners more advantage. + score -= std::abs(corner_angle) * corner_shift; // Still give sharper corners more advantage. break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE: break; - case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: //Give sharper corners some advantage, but sharper concave corners even more. + case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: // Give sharper corners some advantage, but sharper concave corners even more. { float score_corner = std::abs(corner_angle) * corner_shift; - if(corner_angle < 0) //Concave corner. + if (corner_angle < 0) // Concave corner. { score_corner *= 2; } @@ -683,7 +701,7 @@ class PathOrderOptimizer } constexpr float EPSILON = 25.0; - if(std::abs(best_score - score) <= EPSILON) + if (std::abs(best_score - score) <= EPSILON) { // add breaker for two candidate starting location with similar score // if we don't do this then we (can) get an un-even seam @@ -691,13 +709,13 @@ class PathOrderOptimizer // if x-coord for both points are equal then break ties by // favouring points with lower y-coord const Point& best_point = (*path.converted)[best_i]; - if(std::abs(here.Y - best_point.Y) <= EPSILON ? best_point.X < here.X : best_point.Y < here.Y) + if (std::abs(here.Y - best_point.Y) <= EPSILON ? best_point.X < here.X : best_point.Y < here.Y) { best_score = std::min(best_score, score); best_i = i; } } - else if(score < best_score) + else if (score < best_score) { best_i = i; best_score = score; @@ -720,7 +738,7 @@ class PathOrderOptimizer Point prev_pos = here_pos; Point next_pos; - while(actual_distance < distance) + while (actual_distance < distance) { actual_delta += direction; next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; @@ -728,9 +746,9 @@ class PathOrderOptimizer prev_pos = next_pos; } - if(actual_distance > distance) // Which is veeeery likely + if (actual_distance > distance) // Which is veeeery likely { - prev_pos = (*path.converted)[(here + actual_delta -direction + path.converted->size()) % path.converted->size()]; + prev_pos = (*path.converted)[(here + actual_delta - direction + path.converted->size()) % path.converted->size()]; Point vector = next_pos - prev_pos; coord_t vector_size = vSize(vector); @@ -745,18 +763,18 @@ class PathOrderOptimizer } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from all points within angle_query_distance of the query point. Angles closer - * to the current point are weighted more towards the total angle then points further away. - * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength - * \param path The vertex data of a path - * \param i index of the query point - * \param angle_query_distance query range (default to 0.1mm) - * \param fall_off_strength fall of strength of the angle weight - * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance - */ + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from all points within angle_query_distance of the query point. Angles closer + * to the current point are weighted more towards the total angle then points further away. + * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength + * \param path The vertex data of a path + * \param i index of the query point + * \param angle_query_distance query range (default to 0.1mm) + * \param fall_off_strength fall of strength of the angle weight + * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance + */ float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) { static constexpr coord_t distance_step = 2000; @@ -767,7 +785,7 @@ class PathOrderOptimizer float angle = 0.0; int computed_angles = 0; - for(coord_t distance = distance_step ; distance <= max_distance ; distance += distance_step) + for (coord_t distance = distance_step; distance <= max_distance; distance += distance_step) { Point next = findNeighbourPoint(path, i, distance); Point previous = findNeighbourPoint(path, i, -distance); @@ -806,11 +824,11 @@ class PathOrderOptimizer */ coord_t getCombingDistance(const Point& a, const Point& b) { - if(!PolygonUtils::polygonCollidesWithLineSegment(*combing_boundary, a, b)) + if (! PolygonUtils::polygonCollidesWithLineSegment(*combing_boundary, a, b)) { - return getDirectDistance(a, b); //No collision with any line. Just compute the direct distance then. + return getDirectDistance(a, b); // No collision with any line. Just compute the direct distance then. } - if(paths.size() > 100) + if (paths.size() > 100) { /* If we have many paths to optimize the order for, this combing calculation can become very expensive. Instead, penalize travels @@ -818,13 +836,13 @@ class PathOrderOptimizer return getDirectDistance(a, b) * 5; } - if(combing_grid == nullptr) + 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 = 2000; // 2mm grid cells. Smaller will use more memory, but reduce chance of unnecessary collision checks. combing_grid = PolygonUtils::createLocToLineGrid(*combing_boundary, grid_size); } - CombPath comb_path; //Output variable. + CombPath comb_path; // Output variable. constexpr coord_t rounding_error = -25; constexpr coord_t tiny_travel_threshold = 0; constexpr bool fail_on_unavoidable_obstacles = false; @@ -832,12 +850,12 @@ class PathOrderOptimizer coord_t sum = 0; Point last_point = a; - for(const Point& point : comb_path) + for (const Point& point : comb_path) { sum += vSize(point - last_point); last_point = point; } - return sum * sum; //Squared distance, for fair comparison with direct distance. + return sum * sum; // Squared distance, for fair comparison with direct distance. } /*! @@ -852,7 +870,7 @@ class PathOrderOptimizer bool isLoopingPolyline(const OrderablePath& path) { - if(path.converted->empty()) + if (path.converted->empty()) { return false; } @@ -863,6 +881,6 @@ class PathOrderOptimizer template const std::unordered_multimap PathOrderOptimizer::no_order_requirements; -} //namespace cura +} // namespace cura -#endif //PATHORDEROPTIMIZER_H +#endif // PATHORDEROPTIMIZER_H From 9311a7fa06f505d51da42d1af33e0d86f632a806 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 6 Oct 2023 13:07:14 +0200 Subject: [PATCH 598/656] Clean and optimized new corner angle computation algorithm CURA-11100 --- include/PathOrderOptimizer.h | 136 ++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 939baf6b21..108b49b61a 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -636,18 +636,35 @@ class PathOrderOptimizer return vert; } + //Precompute segments lengths because we are going to need them multiple times + std::vector segments_sizes(path.converted->size()); + coord_t total_length = 0; + for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + { + const Point &next = (*path.converted)[(i + 1) % path.converted->size()]; + coord_t segment_size = vSize(next - here); + segments_sizes[i] = segment_size; + total_length += segment_size; + } + size_t best_i; float best_score = std::numeric_limits::infinity(); for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - // For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) - ? MM2INT(10) - : vSize2(here - target_pos); + if(i == path.converted->size() - 1) + { + //The path is closed so the last point is the same as the first, don't process it twice + continue; + } - float corner_angle = cornerAngle(path, i); + //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + //For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) + ? getDirectDistance(here, target_pos) + : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + + float corner_angle = cornerAngle(path, i, segments_sizes, total_length); // angles < 0 are concave (left turning) // angles > 0 are convex (right turning) @@ -725,79 +742,82 @@ class PathOrderOptimizer return best_i; } - Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance) + /*! + * Finds a neighbour point on the path, located before or after the given reference point. The neighbour point + * is computed by travelling on the path and stopping when the distance has been reached, For example: + * |------|---------|------|--------------*---| + * H A B C N D + * In this case, H is the start point of the path and ABCD are the actual following points of the path. + * The neighbour point N is found by reaching point D then going a bit backward on the previous segment. + * This approach gets rid of the mesh actual resolution and gives a neighbour point that is on the path + * at a given physical distance. + * \param path The vertex data of a path + * \param here The starting point index + * \param distance The distance we want to travel on the path, which may be positive to go forward + * or negative to go backward + * \param segments_sizes The pre-computed sizes of the segments + * \return The position of the path a the given distance from the reference point + */ + static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector &segments_sizes) { - assert(distance); - - const Point here_pos = (*path.converted)[here]; - int direction = distance > 0 ? 1 : -1; + const int direction = distance > 0 ? 1 : -1; + const int size_delta = distance > 0 ? -1 : 0; distance = std::abs(distance); - coord_t actual_distance = 0; + // Travel on the path until we reach the distance int actual_delta = 0; - - Point prev_pos = here_pos; - Point next_pos; - while (actual_distance < distance) + coord_t travelled_distance = 0; + coord_t segment_size = 0; + while(travelled_distance < distance) { actual_delta += direction; - next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; - actual_distance += vSize(next_pos - prev_pos); - prev_pos = next_pos; + segment_size = segments_sizes[(here + actual_delta + size_delta + path.converted->size()) % path.converted->size()]; + travelled_distance += segment_size; } - if (actual_distance > distance) // Which is veeeery likely + const Point &next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + + if(travelled_distance > distance) [[likely]] { - prev_pos = (*path.converted)[(here + actual_delta - direction + path.converted->size()) % path.converted->size()]; + // We have overtaken the required distance, go backward on the last segment + int prev = (here + actual_delta -direction + path.converted->size()) % path.converted->size(); + const Point &prev_pos = (*path.converted)[prev]; - Point vector = next_pos - prev_pos; - coord_t vector_size = vSize(vector); - Point unit_vector = (vector * 1000) / vector_size; - Point vector_delta = unit_vector * (vector_size - (actual_distance - distance)); + const Point vector = next_pos - prev_pos; + const Point unit_vector = (vector * 1000) / segment_size; + const Point vector_delta = unit_vector * (segment_size - (travelled_distance - distance)); return prev_pos + vector_delta / 1000; } else { + // Luckily, the required distance stops exactly on an existing point return next_pos; } } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from all points within angle_query_distance of the query point. Angles closer - * to the current point are weighted more towards the total angle then points further away. - * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength - * \param path The vertex data of a path - * \param i index of the query point - * \param angle_query_distance query range (default to 0.1mm) - * \param fall_off_strength fall of strength of the angle weight - * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance - */ - float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from two points within angle_query_distance of the query point, no matter + * what segment this leads us to + * \param path The vertex data of a path + * \param i index of the query point + * \param segments_sizes The pre-computed sizes of the segments + * \param total_length The path total length + * \param angle_query_distance query range (default to 1mm) + * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] + */ + static float cornerAngle(const OrderablePath& path, int i, const std::vector &segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) { - static constexpr coord_t distance_step = 2000; - static constexpr coord_t max_distance = 5000; - - const Point here = (*path.converted)[i]; - - float angle = 0.0; - int computed_angles = 0; - - for (coord_t distance = distance_step; distance <= max_distance; distance += distance_step) - { - Point next = findNeighbourPoint(path, i, distance); - Point previous = findNeighbourPoint(path, i, -distance); - - angle += LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - computed_angles++; - } + const coord_t bounded_distance = std::min(angle_query_distance, total_length / 2); + const Point &here = (*path.converted)[i]; + const Point next = findNeighbourPoint(path, i, bounded_distance, segments_sizes); + const Point previous = findNeighbourPoint(path, i, -bounded_distance, segments_sizes); - angle /= computed_angles; - angle /= M_PI; + float angle = LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - return angle; + return angle / M_PI; } /*! From 55c43275b86220ab88fe3aeb3e61b4d56e26d8cc Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Fri, 6 Oct 2023 11:12:44 +0000 Subject: [PATCH 599/656] Applied clang-format. --- include/PathOrderOptimizer.h | 62 ++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 108b49b61a..d8d6d9ca17 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -636,12 +636,12 @@ class PathOrderOptimizer return vert; } - //Precompute segments lengths because we are going to need them multiple times + // Precompute segments lengths because we are going to need them multiple times std::vector segments_sizes(path.converted->size()); coord_t total_length = 0; - for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - const Point &next = (*path.converted)[(i + 1) % path.converted->size()]; + const Point& next = (*path.converted)[(i + 1) % path.converted->size()]; coord_t segment_size = vSize(next - here); segments_sizes[i] = segment_size; total_length += segment_size; @@ -651,18 +651,18 @@ class PathOrderOptimizer float best_score = std::numeric_limits::infinity(); for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - if(i == path.converted->size() - 1) + if (i == path.converted->size() - 1) { - //The path is closed so the last point is the same as the first, don't process it twice + // The path is closed so the last point is the same as the first, don't process it twice continue; } - //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - //For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) - ? getDirectDistance(here, target_pos) - : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + // For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) + ? MM2INT(10) + : vSize2(here - target_pos); float corner_angle = cornerAngle(path, i, segments_sizes, total_length); // angles < 0 are concave (left turning) @@ -758,7 +758,7 @@ class PathOrderOptimizer * \param segments_sizes The pre-computed sizes of the segments * \return The position of the path a the given distance from the reference point */ - static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector &segments_sizes) + static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector& segments_sizes) { const int direction = distance > 0 ? 1 : -1; const int size_delta = distance > 0 ? -1 : 0; @@ -768,20 +768,20 @@ class PathOrderOptimizer int actual_delta = 0; coord_t travelled_distance = 0; coord_t segment_size = 0; - while(travelled_distance < distance) + while (travelled_distance < distance) { actual_delta += direction; segment_size = segments_sizes[(here + actual_delta + size_delta + path.converted->size()) % path.converted->size()]; travelled_distance += segment_size; } - const Point &next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + const Point& next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; - if(travelled_distance > distance) [[likely]] + if (travelled_distance > distance) [[likely]] { // We have overtaken the required distance, go backward on the last segment - int prev = (here + actual_delta -direction + path.converted->size()) % path.converted->size(); - const Point &prev_pos = (*path.converted)[prev]; + int prev = (here + actual_delta - direction + path.converted->size()) % path.converted->size(); + const Point& prev_pos = (*path.converted)[prev]; const Point vector = next_pos - prev_pos; const Point unit_vector = (vector * 1000) / segment_size; @@ -796,22 +796,22 @@ class PathOrderOptimizer } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from two points within angle_query_distance of the query point, no matter - * what segment this leads us to - * \param path The vertex data of a path - * \param i index of the query point - * \param segments_sizes The pre-computed sizes of the segments - * \param total_length The path total length - * \param angle_query_distance query range (default to 1mm) - * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] - */ - static float cornerAngle(const OrderablePath& path, int i, const std::vector &segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from two points within angle_query_distance of the query point, no matter + * what segment this leads us to + * \param path The vertex data of a path + * \param i index of the query point + * \param segments_sizes The pre-computed sizes of the segments + * \param total_length The path total length + * \param angle_query_distance query range (default to 1mm) + * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] + */ + static float cornerAngle(const OrderablePath& path, int i, const std::vector& segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) { const coord_t bounded_distance = std::min(angle_query_distance, total_length / 2); - const Point &here = (*path.converted)[i]; + const Point& here = (*path.converted)[i]; const Point next = findNeighbourPoint(path, i, bounded_distance, segments_sizes); const Point previous = findNeighbourPoint(path, i, -bounded_distance, segments_sizes); From 57a0daec11b3b5e76af7df4c500c35e39b672b3b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 10:38:33 +0200 Subject: [PATCH 600/656] Removed debug output --- src/SkeletalTrapezoidationGraph.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/SkeletalTrapezoidationGraph.cpp b/src/SkeletalTrapezoidationGraph.cpp index ed8cc1035d..4657b8ee97 100644 --- a/src/SkeletalTrapezoidationGraph.cpp +++ b/src/SkeletalTrapezoidationGraph.cpp @@ -3,17 +3,18 @@ #include "SkeletalTrapezoidationGraph.h" -#include +#include "utils/linearAlg2D.h" +#include "utils/macros.h" #include -#include "utils/linearAlg2D.h" -#include "utils/macros.h" +#include namespace cura { -STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) : HalfEdge(data) +STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) + : HalfEdge(data) { } @@ -131,7 +132,8 @@ STHalfEdge* STHalfEdge::getNextUnconnected() return result->twin; } -STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) : HalfEdgeNode(data, p) +STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) + : HalfEdgeNode(data, p) { } @@ -223,7 +225,10 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) } }; - auto should_collapse = [snap_dist](node_t* a, node_t* b) { return shorterThen(a->p - b->p, snap_dist); }; + auto should_collapse = [snap_dist](node_t* a, node_t* b) + { + return shorterThen(a->p - b->p, snap_dist); + }; for (auto edge_it = edges.begin(); edge_it != edges.end();) { @@ -253,10 +258,6 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) { edge_from_3->from = quad_mid->from; edge_from_3->twin->to = quad_mid->from; - if (count > 50) - { - std::cerr << edge_from_3->from->p << " - " << edge_from_3->to->p << '\n'; - } if (++count > 1000) { break; From 26ea92b20a383b9f4dcfba7f432a66923d11dd9c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 10:52:13 +0200 Subject: [PATCH 601/656] Fixed some edge-cases with new angle calculation CURA-11100 --- include/PathOrderOptimizer.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index d8d6d9ca17..ab58d30651 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -689,16 +689,12 @@ class PathOrderOptimizer { default: case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: - if (corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. - { - score += corner_angle * corner_shift; - } + // Give advantage to concave corners. More advantage for sharper corners. + score += corner_angle * corner_shift; break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: - if (corner_angle > 0) // Indeed a convex corner? - { - score -= corner_angle * corner_shift; - } + // Give advantage to convex corners. More advantage for sharper corners. + score -= corner_angle * corner_shift; break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: score -= std::abs(corner_angle) * corner_shift; // Still give sharper corners more advantage. @@ -717,7 +713,7 @@ class PathOrderOptimizer } } - constexpr float EPSILON = 25.0; + constexpr float EPSILON = 5.0; if (std::abs(best_score - score) <= EPSILON) { // add breaker for two candidate starting location with similar score From 31399b138dfd827ee31ac38b23e9e7c8d2afe4cc Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 5 Oct 2023 11:48:49 +0200 Subject: [PATCH 602/656] Remove unused variable boyscouting CURA-11110 --- src/skin.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/skin.cpp b/src/skin.cpp index 227591a8c4..28fc60c238 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -346,8 +346,6 @@ void SkinInfillAreaComputation::generateRoofingFillAndSkinFill(SliceLayerPart& p */ Polygons SkinInfillAreaComputation::generateFilledAreaAbove(SliceLayerPart& part, size_t roofing_layer_count) { - const size_t wall_idx = std::min(size_t(2), mesh.settings.get("wall_line_count")); - Polygons filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); if (! no_small_gaps_heuristic) { From e44da002c65a2ed90a1d961c836a4a48158d28fd Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 5 Oct 2023 12:45:17 +0200 Subject: [PATCH 603/656] Use different config for roofing inner/outer walls Implementation is not fully desired since it has some downsides 1. When a layer-part is partially a roof the whole outer/inner wall uses the inner/outer wall roofing print-configuration 2. Some logic is duplicated, namely the function that calculates the `filled_area_above`. This function previously lived in `SkinInfillAreaComputation`. It's not logical to create a `SkinInfillAreaComputation` instance just to re-use this utility. Proposal to fix this is to move the logic of calculating the `filled_area_above` to a more central location, this will be done in a seperate ticket. 3. The `inset0_roofing_config` and `insetX_roofing_config` contain `line_width` properties. Changing the line-widths here doesn't actually change the line width. The line widths can only be changed through the `insetX_config` and `inset0_config` configs CURA-11110 --- include/settings/MeshPathConfigs.h | 2 + src/FffGcodeWriter.cpp | 96 +++++++++++++++++++++++++++++- src/settings/MeshPathConfigs.cpp | 26 ++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/include/settings/MeshPathConfigs.h b/include/settings/MeshPathConfigs.h index 43457fbc8c..61c993b9a8 100644 --- a/include/settings/MeshPathConfigs.h +++ b/include/settings/MeshPathConfigs.h @@ -15,6 +15,8 @@ struct MeshPathConfigs { GCodePathConfig inset0_config{}; GCodePathConfig insetX_config{}; + GCodePathConfig inset0_roofing_config{}; + GCodePathConfig insetX_roofing_config{}; GCodePathConfig bridge_inset0_config{}; GCodePathConfig bridge_insetX_config{}; GCodePathConfig skin_config{}; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index aedd5d439b..c3750c3b18 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -21,6 +21,8 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include +#include #include #include @@ -2392,6 +2394,96 @@ bool FffGcodeWriter::processInsets( } else { + // for layers that (partially) do not have any layers above we apply the roofing configuration + auto use_roofing_config = [&part, &mesh, &gcode_layer](){ + const auto getOutlineOnLayer = [mesh](const SliceLayerPart& part_here, const LayerIndex layer2_nr) -> Polygons + { + Polygons result; + if (layer2_nr >= static_cast(mesh.layers.size())) + { + return result; + } + const SliceLayer& layer2 = mesh.layers[layer2_nr]; + for (const SliceLayerPart& part2 : layer2.parts) + { + if (part_here.boundaryBox.hit(part2.boundaryBox)) + { + result.add(part2.outline); + } + } + return result; + }; + + const auto filled_area_above = [&getOutlineOnLayer, &part, &mesh, &gcode_layer]() -> Polygons + { + const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); + const bool no_small_gaps_heuristic = mesh.settings.get("skin_no_small_gaps_heuristic"); + const int layer_nr = gcode_layer.getLayerNr(); + auto filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); + if (! no_small_gaps_heuristic) + { + for (int layer_nr_above = layer_nr + 1; layer_nr_above < layer_nr + roofing_layer_count; layer_nr_above++) + { + Polygons outlines_above = getOutlineOnLayer(part, layer_nr_above); + filled_area_above = filled_area_above.intersection(outlines_above); + } + } + if (layer_nr > 0) + { + // if the skin has air below it then cutting it into regions could cause a region + // to be wholely or partly above air and it may not be printable so restrict + // the regions that have air above (the visible regions) to not include any area that + // has air below (fixes https://github.com/Ultimaker/Cura/issues/2656) + + // set air_below to the skin area for the current layer that has air below it + Polygons air_below = getOutlineOnLayer(part, layer_nr).difference(getOutlineOnLayer(part, layer_nr - 1)); + + if (! air_below.empty()) + { + // add the polygons that have air below to the no air above polygons + filled_area_above = filled_area_above.unionPolygons(air_below); + } + } + + return filled_area_above; + }(); + + if (filled_area_above.empty()) + { + return true; + } + + const auto point_view = ranges::views::transform([](auto extrusion_junction) { return extrusion_junction.p; }); + + for (const auto& path: part.wall_toolpaths) + { + for (const auto& wall: path) + { + for (const auto& p : wall | point_view) + { + if (!filled_area_above.inside(p)) + { + return true; + } + } + + for (const auto& window : wall | point_view | ranges::views::sliding(2)) + { + auto p0 = window[0]; + auto p1 = window[1]; + if (PolygonUtils::polygonCollidesWithLineSegment(filled_area_above, p0, p1)) + { + return true; + } + } + } + } + return false; + }(); + + const GCodePathConfig& inset0_config = use_roofing_config ? mesh_config.inset0_roofing_config : mesh_config.inset0_config; + const GCodePathConfig& insetX_config = use_roofing_config ? mesh_config.insetX_roofing_config : mesh_config.insetX_config; + // Main case: Optimize the insets with the InsetOrderOptimizer. const coord_t wall_x_wipe_dist = 0; const ZSeamConfig z_seam_config( @@ -2405,8 +2497,8 @@ bool FffGcodeWriter::processInsets( gcode_layer, mesh.settings, extruder_nr, - mesh_config.inset0_config, - mesh_config.insetX_config, + inset0_config, + insetX_config, mesh_config.bridge_inset0_config, mesh_config.bridge_insetX_config, mesh.settings.get("travel_retract_before_outer_wall"), diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index 942084a231..a89b444bd3 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -28,6 +28,32 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } + , inset0_roofing_config + { + .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { + .speed = mesh.settings.get("speed_wall_0_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), + .jerk = mesh.settings.get("jerk_wall_0_roofing") + } + } + , insetX_roofing_config + { + .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { + .speed = mesh.settings.get("speed_wall_x_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), + .jerk = mesh.settings.get("jerk_wall_x_roofing") + } + } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") From 5a815f6ea769cbbf39d0fc21a1eeffe7c64e3e42 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 5 Oct 2023 10:47:34 +0000 Subject: [PATCH 604/656] Applied clang-format. --- src/FffGcodeWriter.cpp | 15 +++++++---- src/settings/MeshPathConfigs.cpp | 46 ++++++++++++++------------------ 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index c3750c3b18..7d15063a22 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2395,7 +2395,8 @@ bool FffGcodeWriter::processInsets( else { // for layers that (partially) do not have any layers above we apply the roofing configuration - auto use_roofing_config = [&part, &mesh, &gcode_layer](){ + auto use_roofing_config = [&part, &mesh, &gcode_layer]() + { const auto getOutlineOnLayer = [mesh](const SliceLayerPart& part_here, const LayerIndex layer2_nr) -> Polygons { Polygons result; @@ -2453,15 +2454,19 @@ bool FffGcodeWriter::processInsets( return true; } - const auto point_view = ranges::views::transform([](auto extrusion_junction) { return extrusion_junction.p; }); + const auto point_view = ranges::views::transform( + [](auto extrusion_junction) + { + return extrusion_junction.p; + }); - for (const auto& path: part.wall_toolpaths) + for (const auto& path : part.wall_toolpaths) { - for (const auto& wall: path) + for (const auto& wall : path) { for (const auto& p : wall | point_view) { - if (!filled_area_above.inside(p)) + if (! filled_area_above.inside(p)) { return true; } diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index a89b444bd3..aa71d89e07 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -28,32 +28,26 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } - , inset0_roofing_config - { - .type = PrintFeatureType::OuterWall, - .line_width = static_cast( - mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), - .speed_derivatives = { - .speed = mesh.settings.get("speed_wall_0_roofing"), - .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), - .jerk = mesh.settings.get("jerk_wall_0_roofing") - } - } - , insetX_roofing_config - { - .type = PrintFeatureType::OuterWall, - .line_width = static_cast( - mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), - .speed_derivatives = { - .speed = mesh.settings.get("speed_wall_x_roofing"), - .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), - .jerk = mesh.settings.get("jerk_wall_x_roofing") - } - } + , inset0_roofing_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") + * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), + .jerk = mesh.settings.get("jerk_wall_0_roofing") } } + , insetX_roofing_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") + * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), + .jerk = mesh.settings.get("jerk_wall_x_roofing") } } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") From 0a91c6544fa68d942c14b9f9c8f995bf2e93f130 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 5 Oct 2023 15:17:34 +0200 Subject: [PATCH 605/656] the length of brim is correctly calculated in case of no adhesion CURA-11097 --- src/FffGcodeWriter.cpp | 1 - src/SkirtBrim.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 7d15063a22..3534122248 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3147,7 +3147,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } island_order_optimizer.optimize(); - const auto support_brim_line_count = infill_extruder.settings.get("support_brim_line_count"); const auto support_connect_zigzags = infill_extruder.settings.get("support_connect_zigzags"); const auto support_structure = infill_extruder.settings.get("support_structure"); const Point infill_origin; diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 6aed96d7fe..2c1c134d3c 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); - const coord_t length = skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE)? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 3f811919eb9625a6b24f235203dbc06c711b2af9 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Thu, 5 Oct 2023 13:18:20 +0000 Subject: [PATCH 606/656] Applied clang-format. --- src/SkirtBrim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 2c1c134d3c..98992c67d9 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE)? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From edfd1ce608135f220ce0cd199d3ec77417d84058 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 6 Oct 2023 10:54:26 +0200 Subject: [PATCH 607/656] comment for adding if statement CURA-11097 --- src/SkirtBrim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 98992c67d9..1731714763 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -667,8 +667,8 @@ void SkirtBrim::generateSupportBrim() } storage.support_brim.add(brim_line); - - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); + // In case of adhesion::NONE length of support brim is only the length of the brims formed for the support + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 1be41240e9718e37a732ebde45a8b0e242025ee5 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Fri, 6 Oct 2023 08:56:34 +0000 Subject: [PATCH 608/656] Applied clang-format. --- src/SkirtBrim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 1731714763..94761b0717 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); // In case of adhesion::NONE length of support brim is only the length of the brims formed for the support - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 77028a968349074d5a1a3e14d2bb6f6931a90c3e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 15:26:34 +0200 Subject: [PATCH 609/656] Minor optimization CURA-11100 Co-authored-by: Casper Lamboo --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index ab58d30651..ce11a18b12 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -642,7 +642,7 @@ class PathOrderOptimizer for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { const Point& next = (*path.converted)[(i + 1) % path.converted->size()]; - coord_t segment_size = vSize(next - here); + const coord_t segment_size = vSize(next - here); segments_sizes[i] = segment_size; total_length += segment_size; } From 784ecfc5b21673caf498e7b3af4266e4f783831b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 15:44:51 +0200 Subject: [PATCH 610/656] Applied code suggestion by @casperlamboo CURA-11100 --- include/PathOrderOptimizer.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index ce11a18b12..506e624927 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -649,14 +650,8 @@ class PathOrderOptimizer size_t best_i; float best_score = std::numeric_limits::infinity(); - for (const auto& [i, here] : **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::drop_last(1) | ranges::views::enumerate) { - if (i == path.converted->size() - 1) - { - // The path is closed so the last point is the same as the first, don't process it twice - continue; - } - // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. // For SHARPEST_CORNER, use a fixed starting score of 0. const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); From 948f860bd13e31473190bc34fc1880e2a1d8232e Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Mon, 9 Oct 2023 13:45:37 +0000 Subject: [PATCH 611/656] Applied clang-format. --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 506e624927..ae7591db45 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -17,10 +17,10 @@ #include #include #include +#include #include #include #include -#include #include #include From a96e603c50d1d14c8efcd75cb3f4ec652cb54196 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:40:09 +0200 Subject: [PATCH 612/656] pin gRPC defs to 0.1.0-beta.1 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index dea1860300..841ec2d936 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/0.1.0-beta.1") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 6c20ccd6a9538411fcb220c4813ab8ade3be5207 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:40:52 +0200 Subject: [PATCH 613/656] set version to 5.5.0-beta.2 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 841ec2d936..2556e86c45 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-beta.1" + self.version = "5.5.0-beta.2" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) From 9ab698b750974212ec9bd68c5e5b107ea37b46a7 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:42:59 +0200 Subject: [PATCH 614/656] set version to 5.6.0-alpha --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 2556e86c45..27c205dc58 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-beta.2" + self.version = "5.6.0-alpha" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) From 81883c036b0a0e1c620ac26ce847787980388797 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 11:05:34 +0200 Subject: [PATCH 615/656] loosen deps version --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 27c205dc58..b498c3b84d 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/0.1.0-beta.1") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From ab538dc676bb351446f10ccb07dffbfb6af4a5b3 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 13 Oct 2023 15:50:39 +0200 Subject: [PATCH 616/656] Small refactor. done as part of CURA-10407 --- src/support.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/support.cpp b/src/support.cpp index a4cf4a711e..2c2ddb9fa3 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -686,28 +686,26 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) } } - for (LayerIndex layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++) + for (Polygons& support_areas : global_support_areas_per_layer) { - Polygons& support_areas = global_support_areas_per_layer[layer_idx]; support_areas = support_areas.unionPolygons(); } // handle support interface - for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) + for (auto& mesh : storage.meshes) { - SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; - if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) + if (mesh->settings.get("infill_mesh") || mesh->settings.get("anti_overhang_mesh")) { continue; } - if (mesh.settings.get("support_roof_enable")) + if (mesh->settings.get("support_roof_enable")) { - generateSupportRoof(storage, mesh, global_support_areas_per_layer); + generateSupportRoof(storage, *mesh, global_support_areas_per_layer); } - if (mesh.settings.get("support_bottom_enable")) + if (mesh->settings.get("support_bottom_enable")) { - generateSupportBottom(storage, mesh, global_support_areas_per_layer); + generateSupportBottom(storage, *mesh, global_support_areas_per_layer); } } From 8c3740ddad305c434558401e459222175c9de6b0 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 13 Oct 2023 16:02:19 +0200 Subject: [PATCH 617/656] Support-infill can has fractional layers + refactor spike-code for roofs. - Move code outside of the gcode-writer. For example, z-offset is now calulated in the settings themselves. - Other than support roofs, support infill now also is fractional layer 'aware'. this is the 'main' commit for CURA-10407 -- splitting this into different commits would've been a bit more hassle than it's worth, as several of the changes are closely related --- include/FffGcodeWriter.h | 4 +- include/SupportInfillPart.h | 3 +- include/settings/PathConfigStorage.h | 2 + include/sliceDataStorage.h | 5 +- src/FffGcodeWriter.cpp | 188 +++++++++++++-------------- src/SupportInfillPart.cpp | 3 +- src/settings/PathConfigStorage.cpp | 15 +++ src/support.cpp | 36 ++--- 8 files changed, 134 insertions(+), 122 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 96f504c1e3..e46f2b471c 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -634,10 +634,12 @@ class FffGcodeWriter : public NoCopy * layer. * * \param[in] storage Where the slice data is stored. + * \param[in] support_roof_outlines which polygons to generate roofs for -- originally split-up because of fractional (layer-height) layers + * \param[in] current_roof_config config to be used -- most importantly, support has slightly different configs for fractional (layer-height) layers * \param gcodeLayer The initial planning of the g-code of the layer. * \return Whether any support skin was added to the layer plan. */ - bool addSupportRoofsToGCode(const SliceDataStorage& storage, LayerPlan& gcodeLayer) const; + bool addSupportRoofsToGCode(const SliceDataStorage& storage, const Polygons& support_roof_outlines, const GCodePathConfig& current_roof_config, LayerPlan& gcode_layer) const; /*! * Add the support bottoms to the layer plan \p gcodeLayer of the current diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 2a1849ab55..8e00845ac5 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -34,8 +34,9 @@ class SupportInfillPart std::vector wall_toolpaths; //!< Any walls go here, not in the areas, where they could be combined vertically (don't combine walls). Binned by inset_idx. coord_t custom_line_distance; + bool use_fractional_config; - SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, int inset_count_to_generate = 0, coord_t custom_line_distance = 0 ); + SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0 ); const Polygons& getInfillArea() const; }; diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index e7bd54773a..4ba5bf2837 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -48,7 +48,9 @@ class PathConfigStorage std::vector prime_tower_config_per_extruder; //!< Configuration for the prime tower per extruder. std::vector support_infill_config; //!< The config used to print the normal support, rather than the support interface + std::vector support_fractional_infill_config; //!< The config used to print the normal support on fractional layer-height parts. GCodePathConfig support_roof_config; //!< The config used to print the dense roofs of support. + GCodePathConfig support_fractional_roof_config; //!< The config used to print the dense roofs of support on fractional layer-height parts. GCodePathConfig support_bottom_config; //!< The config to use to print the dense bottoms of support std::vector mesh_configs; //!< For each meash the config for all its feature types diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 4c424a2b09..80b825211a 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -212,8 +212,9 @@ class SupportLayer std::vector support_infill_parts; //!< a list of support infill parts Polygons support_bottom; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof. Polygons support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. - Polygons support_fractional_roof_top; //!< If the support distance is not exactly a multiple of the layer height, - // the first part of support just underneath the model needs to be printed at a fracional layer height. + // NOTE: This is _all_ of the support_roof, and as such, overlaps with support_fractional_roof! + Polygons support_fractional_roof; //!< If the support distance is not exactly a multiple of the layer height, + // the first part of support just underneath the model needs to be printed at a fracional layer height. Polygons support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support Polygons support_mesh; //!< Areas from support meshes which should NOT be supported by more support Polygons anti_overhang; //!< Areas where no overhang should be detected. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 3534122248..2d74becd90 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3064,7 +3064,8 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla } if (extruder_nr == support_roof_extruder_nr) { - support_added |= addSupportRoofsToGCode(storage, gcode_layer); + support_added |= addSupportRoofsToGCode(storage, support_layer.support_roof.difference(support_layer.support_fractional_roof), gcode_layer.configs_storage.support_roof_config, gcode_layer); + support_added |= addSupportRoofsToGCode(storage, support_layer.support_fractional_roof, gcode_layer.configs_storage.support_fractional_roof_config, gcode_layer); } if (extruder_nr == support_bottom_extruder_nr) { @@ -3160,6 +3161,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer for (const PathOrdering& path : island_order_optimizer.paths) { const SupportInfillPart& part = *path.vertices; + const auto& configs = part.use_fractional_config ? gcode_layer.configs_storage.support_fractional_infill_config : gcode_layer.configs_storage.support_infill_config; // always process the wall overlap if walls are generated const int current_support_infill_overlap = (part.inset_count_to_generate > 0) ? default_support_infill_overlap : 0; @@ -3169,7 +3171,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer if (! wall_toolpaths.empty()) { - const GCodePathConfig& config = gcode_layer.configs_storage.support_infill_config[0]; + const GCodePathConfig& config = configs[0]; constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); @@ -3223,7 +3225,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer { support_line_distance_here /= 2; } - const Polygons& area = part.infill_area_per_combine_per_density[density_idx][combine_idx]; + const Polygons& area = Simplify(infill_extruder.settings).polygon(part.infill_area_per_combine_per_density[density_idx][combine_idx]); constexpr size_t wall_count = 0; // Walls are generated somewhere else, so their layers aren't vertically combined. const coord_t small_area_width = 0; @@ -3239,7 +3241,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), infill_multiplier, support_infill_angle, - gcode_layer.z, + gcode_layer.z + configs[combine_idx].z_offset, support_shift, max_resolution, max_deviation, @@ -3303,7 +3305,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer gcode_layer.addPolygonsByOptimizer( support_polygons, - gcode_layer.configs_storage.support_infill_config[combine_idx], + configs[combine_idx], z_seam_config, wall_0_wipe_dist, spiralize, @@ -3324,7 +3326,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer gcode_layer.addLinesByOptimizer( support_lines, - gcode_layer.configs_storage.support_infill_config[combine_idx], + configs[combine_idx], (support_pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, enable_travel_optimization, wipe_dist, @@ -3340,7 +3342,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer // If not, the pattern may still generate gap filling (if it's connected infill or zigzag). We still want to print those. if (wall_line_count == 0 || ! wall_toolpaths_here.empty()) { - const GCodePathConfig& config = gcode_layer.configs_storage.support_infill_config[0]; + const GCodePathConfig& config = configs[0]; constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; constexpr coord_t simplify_curvature = 0; @@ -3375,7 +3377,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } -bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, LayerPlan& gcode_layer) const +bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, const Polygons& support_roof_outlines, const GCodePathConfig& current_roof_config, LayerPlan& gcode_layer) const { const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; @@ -3421,103 +3423,87 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, Lay support_roof_line_distance *= roof_extruder.settings.get("initial_layer_line_width_factor"); } - const auto layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); - const auto support_top_distance = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_top_distance"); - const coord_t leftover_support_distance = support_top_distance % layer_height; - - std::vector infill_outlines = { Simplify(roof_extruder.settings).polygon(support_layer.support_roof.difference(support_layer.support_fractional_roof_top)), - Simplify(roof_extruder.settings).polygon(support_layer.support_fractional_roof_top) }; - auto current_roof_config = gcode_layer.configs_storage.support_roof_config; // copy! - bool generated_something = false; - for (auto& infill_outline : infill_outlines) + Polygons infill_outline = support_roof_outlines; + Polygons wall; + // make sure there is a wall if this is on the first layer + if (gcode_layer.getLayerNr() == 0) { - Polygons wall; - // make sure there is a wall if this is on the first layer - if (gcode_layer.getLayerNr() == 0) - { - wall = support_layer.support_roof.offset(-support_roof_line_width / 2); - infill_outline = wall.offset(-support_roof_line_width / 2); - } - - Infill roof_computation( - pattern, - zig_zaggify_infill, - connect_polygons, - infill_outline, - current_roof_config.getLineWidth(), - support_roof_line_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z + current_roof_config.z_offset, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - Polygons roof_polygons; - std::vector roof_paths; - Polygons roof_lines; - roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); - if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) - { - current_roof_config.z_offset = -leftover_support_distance; - current_roof_config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); - - continue; // We didn't create any support roof. - } - generated_something = true; // We _did_ create at least some support roof. - gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support - if (gcode_layer.getLayerNr() == 0) - { - gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); - } - if (! roof_polygons.empty()) - { - constexpr bool force_comb_retract = false; - gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); - } - if (! roof_paths.empty()) - { - const GCodePathConfig& config = current_roof_config; - constexpr bool retract_before_outer_wall = false; - constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + wall = support_layer.support_roof.offset(-support_roof_line_width / 2); + infill_outline = wall.offset(-support_roof_line_width / 2); + } + infill_outline = Simplify(roof_extruder.settings).polygon(infill_outline); - InsetOrderOptimizer wall_orderer( - *this, - storage, - gcode_layer, - roof_extruder.settings, - roof_extruder_nr, - config, - config, - config, - config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - roof_extruder_nr, - roof_extruder_nr, - z_seam_config, - roof_paths); - wall_orderer.addToLayer(); - } - gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_outline, + current_roof_config.getLineWidth(), + support_roof_line_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z + current_roof_config.z_offset, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + Polygons roof_polygons; + std::vector roof_paths; + Polygons roof_lines; + roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings, gcode_layer.getLayerNr(), SectionType::SUPPORT); + if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) + { + return false; // We didn't create any support roof. + } + gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support + if (gcode_layer.getLayerNr() == 0) + { + gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); + } + if (! roof_polygons.empty()) + { + constexpr bool force_comb_retract = false; + gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); + gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); + } + if (! roof_paths.empty()) + { + const GCodePathConfig& config = current_roof_config; + constexpr bool retract_before_outer_wall = false; + constexpr coord_t wipe_dist = 0; + const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); - current_roof_config.z_offset = -leftover_support_distance; - current_roof_config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + roof_extruder.settings, + roof_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + roof_extruder_nr, + roof_extruder_nr, + z_seam_config, + roof_paths); + wall_orderer.addToLayer(); } - return generated_something; + gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + return true; } bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, LayerPlan& gcode_layer) const diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index 76344af468..fbc7c66096 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -7,12 +7,13 @@ using namespace cura; -SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, int inset_count_to_generate, coord_t custom_line_distance) +SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance) : outline(outline) , outline_boundary_box(outline) , support_line_width(support_line_width) , inset_count_to_generate(inset_count_to_generate) , custom_line_distance(custom_line_distance) +, use_fractional_config(use_fractional_config) { infill_area_per_combine_per_density.clear(); } diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index f6e6d1b703..2ecf94eb70 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -150,6 +150,21 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye { handleInitialLayerSpeedup(storage, layer_nr, initial_speedup_layer_count); } + + const auto layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); + const auto support_top_distance = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("support_top_distance"); + const coord_t leftover_support_distance = support_top_distance % layer_height; + + support_fractional_infill_config = support_infill_config; // copy + for (auto& config : support_fractional_infill_config) + { + config.z_offset = -leftover_support_distance; + config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); + } + + support_fractional_roof_config = support_roof_config; // copy + support_fractional_roof_config.z_offset = -leftover_support_distance; + support_fractional_roof_config.flow *= Ratio(layer_height - leftover_support_distance, layer_height); } void MeshPathConfigs::smoothAllSpeeds(const SpeedDerivatives& first_layer_config, const LayerIndex layer_nr, const LayerIndex max_speed_layer) diff --git a/src/support.cpp b/src/support.cpp index 2c2ddb9fa3..46246f0f2c 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -106,7 +106,6 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( { // The first layer will be printed with a grid pattern wall_line_count_this_layer++; } - assert(storage.support.supportLayers[layer_nr].support_infill_parts.empty() && "support infill part list is supposed to be uninitialized"); const Polygons& global_support_areas = global_support_areas_per_layer[layer_nr]; if (global_support_areas.size() == 0 || layer_nr < min_layer || layer_nr > max_layer) @@ -116,19 +115,26 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( continue; } - std::vector support_islands = global_support_areas.splitIntoParts(); - for (const PolygonsPart& island_outline : support_islands) + const Polygons& global_support_areas_above = (layer_nr + 1) >= global_support_areas_per_layer.size() ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; + const auto all_support_areas_in_layer = { global_support_areas.intersection(global_support_areas_above), global_support_areas.difference(global_support_areas_above) }; + bool use_fractional_config = false; + for (auto& support_areas : all_support_areas_in_layer) { - coord_t support_line_width_here = support_line_width; - if (layer_nr == 0 && mesh_group_settings.get("adhesion_type") != EPlatformAdhesion::RAFT) + std::vector support_islands = support_areas.splitIntoParts(); + for (const PolygonsPart& island_outline : support_islands) { - support_line_width_here *= infill_extruder.settings.get("initial_layer_line_width_factor"); - } - // We don't generate insets and infill area for the parts yet because later the skirt/brim and prime - // tower will remove themselves from the support, so the outlines of the parts can be changed. - SupportInfillPart support_infill_part(island_outline, support_line_width_here, wall_line_count_this_layer); + coord_t support_line_width_here = support_line_width; + if (layer_nr == 0 && mesh_group_settings.get("adhesion_type") != EPlatformAdhesion::RAFT) + { + support_line_width_here *= infill_extruder.settings.get("initial_layer_line_width_factor"); + } + // We don't generate insets and infill area for the parts yet because later the skirt/brim and prime + // tower will remove themselves from the support, so the outlines of the parts can be changed. + SupportInfillPart support_infill_part(island_outline, support_line_width_here, use_fractional_config, wall_line_count_this_layer); - storage.support.supportLayers[layer_nr].support_infill_parts.push_back(support_infill_part); + storage.support.supportLayers[layer_nr].support_infill_parts.push_back(support_infill_part); + } + use_fractional_config = true; } } } @@ -1816,16 +1822,14 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh Polygons roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); support_layers[layer_idx].support_roof.add(roofs); - support_layers[layer_idx].support_fractional_roof_top = roofs.difference(support_layers[layer_idx + 1].support_roof); + support_layers[layer_idx].support_fractional_roof.add(roofs.difference(support_layers[layer_idx + 1].support_roof)); scripta::log("support_interface_roofs", roofs, SectionType::SUPPORT, layer_idx); } // Remove support in between the support roof and the model. Subtracts the roof polygons from the support polygons on the layers above it. for (auto [layer_idx, support_layer] : support_layers | ranges::views::enumerate | ranges::views::drop(1) | ranges::views::drop_last(z_distance_top)) { - Polygons roof = support_layer.support_roof; - - if (roof.empty()) + if (support_layer.support_roof.empty()) { continue; } @@ -1834,7 +1838,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh int upper = std::min(static_cast(layer_idx + roof_layer_count + z_distance_top + 5), static_cast(global_support_areas_per_layer.size()) - 1); for (Polygons& global_support : global_support_areas_per_layer | ranges::views::slice(lower, upper)) { - global_support = global_support.difference(roof); + global_support = global_support.difference(support_layer.support_roof); } } } From 830c56a1179f6690c8694fa67fcb33e4f8cc18fa Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 13 Oct 2023 16:17:09 +0200 Subject: [PATCH 618/656] Fractional(-support)-layers: Prevent nozzle from impacting build-plate. part of CURA-10407 --- src/support.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/support.cpp b/src/support.cpp index 46246f0f2c..23fa7e66c6 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -115,7 +115,7 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( continue; } - const Polygons& global_support_areas_above = (layer_nr + 1) >= global_support_areas_per_layer.size() ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; + const Polygons& global_support_areas_above = (layer_nr + 1) >= global_support_areas_per_layer.size() || layer_nr <= 0 ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; const auto all_support_areas_in_layer = { global_support_areas.intersection(global_support_areas_above), global_support_areas.difference(global_support_areas_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) @@ -1822,7 +1822,10 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh Polygons roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); support_layers[layer_idx].support_roof.add(roofs); - support_layers[layer_idx].support_fractional_roof.add(roofs.difference(support_layers[layer_idx + 1].support_roof)); + if (layer_idx > 0) + { + support_layers[layer_idx].support_fractional_roof.add(roofs.difference(support_layers[layer_idx + 1].support_roof)); + } scripta::log("support_interface_roofs", roofs, SectionType::SUPPORT, layer_idx); } From 06a0157abfc38a0850537820f74e3724f7ad6c1d Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 13 Oct 2023 14:18:03 +0000 Subject: [PATCH 619/656] Applied clang-format. --- include/SupportInfillPart.h | 18 +++++++++--------- src/FffGcodeWriter.cpp | 12 ++++++++++-- src/SupportInfillPart.cpp | 13 +++++++------ src/support.cpp | 3 ++- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 8e00845ac5..45ff44e4e2 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -4,12 +4,12 @@ #ifndef SUPPORT_INFILL_PART_H #define SUPPORT_INFILL_PART_H -#include - #include "utils/AABB.h" #include "utils/ExtrusionLine.h" #include "utils/polygon.h" +#include + namespace cura { @@ -25,18 +25,18 @@ namespace cura class SupportInfillPart { public: - PolygonsPart outline; //!< The outline of the support infill area - AABB outline_boundary_box; //!< The boundary box for the infill area - coord_t support_line_width; //!< The support line width - int inset_count_to_generate; //!< The number of insets need to be generated from the outline. This is not the actual insets that will be generated. - std::vector> infill_area_per_combine_per_density; //!< a list of separated sub-areas which requires different infill densities and combined thicknesses - // for infill_areas[x][n], x means the density level and n means the thickness + PolygonsPart outline; //!< The outline of the support infill area + AABB outline_boundary_box; //!< The boundary box for the infill area + coord_t support_line_width; //!< The support line width + int inset_count_to_generate; //!< The number of insets need to be generated from the outline. This is not the actual insets that will be generated. + std::vector> infill_area_per_combine_per_density; //!< a list of separated sub-areas which requires different infill densities and combined thicknesses + // for infill_areas[x][n], x means the density level and n means the thickness std::vector wall_toolpaths; //!< Any walls go here, not in the areas, where they could be combined vertically (don't combine walls). Binned by inset_idx. coord_t custom_line_distance; bool use_fractional_config; - SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0 ); + SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0); const Polygons& getInfillArea() const; }; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 2d74becd90..e7adca9412 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3064,7 +3064,11 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla } if (extruder_nr == support_roof_extruder_nr) { - support_added |= addSupportRoofsToGCode(storage, support_layer.support_roof.difference(support_layer.support_fractional_roof), gcode_layer.configs_storage.support_roof_config, gcode_layer); + support_added |= addSupportRoofsToGCode( + storage, + support_layer.support_roof.difference(support_layer.support_fractional_roof), + gcode_layer.configs_storage.support_roof_config, + gcode_layer); support_added |= addSupportRoofsToGCode(storage, support_layer.support_fractional_roof, gcode_layer.configs_storage.support_fractional_roof_config, gcode_layer); } if (extruder_nr == support_bottom_extruder_nr) @@ -3377,7 +3381,11 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } -bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, const Polygons& support_roof_outlines, const GCodePathConfig& current_roof_config, LayerPlan& gcode_layer) const +bool FffGcodeWriter::addSupportRoofsToGCode( + const SliceDataStorage& storage, + const Polygons& support_roof_outlines, + const GCodePathConfig& current_roof_config, + LayerPlan& gcode_layer) const { const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index fbc7c66096..ae0b8cf7c1 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -2,18 +2,19 @@ // CuraEngine is released under the terms of the AGPLv3 or higher. #include "SupportInfillPart.h" + #include "support.h" using namespace cura; SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance) -: outline(outline) -, outline_boundary_box(outline) -, support_line_width(support_line_width) -, inset_count_to_generate(inset_count_to_generate) -, custom_line_distance(custom_line_distance) -, use_fractional_config(use_fractional_config) + : outline(outline) + , outline_boundary_box(outline) + , support_line_width(support_line_width) + , inset_count_to_generate(inset_count_to_generate) + , custom_line_distance(custom_line_distance) + , use_fractional_config(use_fractional_config) { infill_area_per_combine_per_density.clear(); } diff --git a/src/support.cpp b/src/support.cpp index 23fa7e66c6..5db9c9932e 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -115,7 +115,8 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( continue; } - const Polygons& global_support_areas_above = (layer_nr + 1) >= global_support_areas_per_layer.size() || layer_nr <= 0 ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; + const Polygons& global_support_areas_above + = (layer_nr + 1) >= global_support_areas_per_layer.size() || layer_nr <= 0 ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; const auto all_support_areas_in_layer = { global_support_areas.intersection(global_support_areas_above), global_support_areas.difference(global_support_areas_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) From 4682128777e104805e295d5c1a0aa2428a3b8410 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 13 Oct 2023 21:21:41 +0200 Subject: [PATCH 620/656] Fix unit-test. done for CURA-10407 --- tests/LayerPlanTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/LayerPlanTest.cpp b/tests/LayerPlanTest.cpp index b583005053..e7428f421c 100644 --- a/tests/LayerPlanTest.cpp +++ b/tests/LayerPlanTest.cpp @@ -169,6 +169,7 @@ class LayerPlanTest : public testing::Test settings->add("support_roof_extruder_nr", "0"); settings->add("support_roof_line_width", "0.404"); settings->add("support_roof_material_flow", "104"); + settings->add("support_top_distance", "200"); settings->add("wall_line_count", "3"); settings->add("wall_line_width_x", "0.3"); settings->add("wall_line_width_0", "0.301"); From 115f0b98e582764561b2a50180e15c39be46da5f Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 16 Oct 2023 11:04:56 +0200 Subject: [PATCH 621/656] Fix roofing visualisation bug CURA-11140 --- src/settings/MeshPathConfigs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index aa71d89e07..d91595ce37 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -38,7 +38,7 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0_roofing"), .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), .jerk = mesh.settings.get("jerk_wall_0_roofing") } } - , insetX_roofing_config{ .type = PrintFeatureType::OuterWall, + , insetX_roofing_config{ .type = PrintFeatureType::InnerWall, .line_width = static_cast( mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), From 3bea2989ad1c0b2cb4e8a5cec651fbba6bbacb28 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 17 Oct 2023 15:52:37 +0200 Subject: [PATCH 622/656] Introduce fractional roof-top layer-heigths in tree-support. Add fractional support-roof and split non-interface support according to layers above. Might refactor the (now) duplicated code (fragments) later if feasible. part of CURA-10407 --- src/TreeSupport.cpp | 11 +++++++++-- src/TreeSupportTipGenerator.cpp | 30 ++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index c226588ebe..eb4509bad6 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2226,9 +2226,16 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. } - for (PolygonsPart part : support_layer_storage[layer_idx].splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. + const Polygons support_layer_storage_above = (layer_idx + 1) >= support_layer_storage.size() || layer_idx <= 0 ? Polygons() : support_layer_storage[layer_idx + 1].offset(config.maximum_move_distance); + const auto all_support_areas_in_layer = { support_layer_storage[layer_idx].intersection(support_layer_storage_above), support_layer_storage[layer_idx].difference(support_layer_storage_above) }; + bool use_fractional_config = false; + for (auto& support_areas : all_support_areas_in_layer) { - storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, config.support_wall_count); + for (auto& part : support_areas.splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. + { + storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, config.support_wall_count); + } + use_fractional_config = true; } { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 99677f9c09..91ec303194 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1164,9 +1164,20 @@ void TreeSupportTipGenerator::generateTips( { if (use_fake_roof) { - for (auto part : support_roof_drawn[layer_idx].splitIntoParts()) + const Polygons support_roof_drawn_above = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance);; + const auto all_support_areas_in_layer = { support_roof_drawn[layer_idx].intersection(support_roof_drawn_above), support_roof_drawn[layer_idx].difference(support_roof_drawn_above)}; + bool use_fractional_config = false; + for (auto& support_areas : all_support_areas_in_layer) { - storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, 0, support_roof_line_distance); + for (const auto& part : support_areas.splitIntoParts()) + { + if (part.area() < config.min_feature_size * config.min_feature_size) + { + continue; + } + storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, 0, support_roof_line_distance); + } + use_fractional_config = true; } placed_support_lines_support_areas[layer_idx].add(TreeSupportUtils::generateSupportInfillLines( support_roof_drawn[layer_idx], @@ -1186,6 +1197,21 @@ void TreeSupportTipGenerator::generateTips( } }); + cura::parallel_for( + 1, + mesh.overhang_areas.size() - z_distance_delta, + [&](const LayerIndex layer_idx) + { + if (layer_idx > 0) + { + storage.support.supportLayers[layer_idx].support_fractional_roof.add( + storage.support.supportLayers[layer_idx].support_roof.difference( + storage.support.supportLayers[layer_idx + 1].support_roof + ) + ); + } + }); + removeUselessAddedPoints(new_tips, storage, additional_support_areas); for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) From ab39a7738099ea6d9d44984d8b1cb2eacbf59cb4 Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 17 Oct 2023 14:01:46 +0000 Subject: [PATCH 623/656] Applied clang-format. --- src/TreeSupport.cpp | 6 ++++-- src/TreeSupportTipGenerator.cpp | 15 ++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index eb4509bad6..0c9a145cc3 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2226,8 +2226,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. } - const Polygons support_layer_storage_above = (layer_idx + 1) >= support_layer_storage.size() || layer_idx <= 0 ? Polygons() : support_layer_storage[layer_idx + 1].offset(config.maximum_move_distance); - const auto all_support_areas_in_layer = { support_layer_storage[layer_idx].intersection(support_layer_storage_above), support_layer_storage[layer_idx].difference(support_layer_storage_above) }; + const Polygons support_layer_storage_above + = (layer_idx + 1) >= support_layer_storage.size() || layer_idx <= 0 ? Polygons() : support_layer_storage[layer_idx + 1].offset(config.maximum_move_distance); + const auto all_support_areas_in_layer + = { support_layer_storage[layer_idx].intersection(support_layer_storage_above), support_layer_storage[layer_idx].difference(support_layer_storage_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 91ec303194..110acd0436 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1164,8 +1164,11 @@ void TreeSupportTipGenerator::generateTips( { if (use_fake_roof) { - const Polygons support_roof_drawn_above = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance);; - const auto all_support_areas_in_layer = { support_roof_drawn[layer_idx].intersection(support_roof_drawn_above), support_roof_drawn[layer_idx].difference(support_roof_drawn_above)}; + const Polygons support_roof_drawn_above + = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance); + ; + const auto all_support_areas_in_layer + = { support_roof_drawn[layer_idx].intersection(support_roof_drawn_above), support_roof_drawn[layer_idx].difference(support_roof_drawn_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) { @@ -1175,7 +1178,8 @@ void TreeSupportTipGenerator::generateTips( { continue; } - storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, 0, support_roof_line_distance); + storage.support.supportLayers[layer_idx] + .support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, 0, support_roof_line_distance); } use_fractional_config = true; } @@ -1205,10 +1209,7 @@ void TreeSupportTipGenerator::generateTips( if (layer_idx > 0) { storage.support.supportLayers[layer_idx].support_fractional_roof.add( - storage.support.supportLayers[layer_idx].support_roof.difference( - storage.support.supportLayers[layer_idx + 1].support_roof - ) - ); + storage.support.supportLayers[layer_idx].support_roof.difference(storage.support.supportLayers[layer_idx + 1].support_roof)); } }); From e1e90a44fdfcdd575c246919e2484d111fcaf0cd Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 17 Oct 2023 17:50:53 +0200 Subject: [PATCH 624/656] Print lower fractional layer heights before full ones, pt 1. Take care of the ordering before it gets to all the order optimizers. part of CURA-10407 --- src/FffGcodeWriter.cpp | 5 ++++- src/TreeSupport.cpp | 2 +- src/TreeSupportTipGenerator.cpp | 2 +- src/support.cpp | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index e7adca9412..d232cc5d7c 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3058,6 +3058,10 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla return support_added; } + if (extruder_nr == support_roof_extruder_nr) + { + support_added |= addSupportRoofsToGCode(storage, support_layer.support_fractional_roof, gcode_layer.configs_storage.support_fractional_roof_config, gcode_layer); + } if (extruder_nr == support_infill_extruder_nr) { support_added |= processSupportInfill(storage, gcode_layer); @@ -3069,7 +3073,6 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla support_layer.support_roof.difference(support_layer.support_fractional_roof), gcode_layer.configs_storage.support_roof_config, gcode_layer); - support_added |= addSupportRoofsToGCode(storage, support_layer.support_fractional_roof, gcode_layer.configs_storage.support_fractional_roof_config, gcode_layer); } if (extruder_nr == support_bottom_extruder_nr) { diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 0c9a145cc3..f1a0504461 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2229,7 +2229,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor const Polygons support_layer_storage_above = (layer_idx + 1) >= support_layer_storage.size() || layer_idx <= 0 ? Polygons() : support_layer_storage[layer_idx + 1].offset(config.maximum_move_distance); const auto all_support_areas_in_layer - = { support_layer_storage[layer_idx].intersection(support_layer_storage_above), support_layer_storage[layer_idx].difference(support_layer_storage_above) }; + = { support_layer_storage[layer_idx].difference(support_layer_storage_above), support_layer_storage[layer_idx].intersection(support_layer_storage_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 110acd0436..2b4d9db870 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1168,7 +1168,7 @@ void TreeSupportTipGenerator::generateTips( = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance); ; const auto all_support_areas_in_layer - = { support_roof_drawn[layer_idx].intersection(support_roof_drawn_above), support_roof_drawn[layer_idx].difference(support_roof_drawn_above) }; + = { support_roof_drawn[layer_idx].difference(support_roof_drawn_above), support_roof_drawn[layer_idx].intersection(support_roof_drawn_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) { diff --git a/src/support.cpp b/src/support.cpp index 5db9c9932e..ce5c8b5e9b 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -117,7 +117,7 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( const Polygons& global_support_areas_above = (layer_nr + 1) >= global_support_areas_per_layer.size() || layer_nr <= 0 ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; - const auto all_support_areas_in_layer = { global_support_areas.intersection(global_support_areas_above), global_support_areas.difference(global_support_areas_above) }; + const auto all_support_areas_in_layer = { global_support_areas.difference(global_support_areas_above), global_support_areas.intersection(global_support_areas_above) }; bool use_fractional_config = false; for (auto& support_areas : all_support_areas_in_layer) { From f61991bbf0c4528eb9992a82d8361f4f1d0dd957 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 17 Oct 2023 17:51:41 +0200 Subject: [PATCH 625/656] Remove debug code and typos. while working on CURA-10407 --- src/TreeSupportTipGenerator.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 2b4d9db870..908544513d 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1166,7 +1166,6 @@ void TreeSupportTipGenerator::generateTips( { const Polygons support_roof_drawn_above = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance); - ; const auto all_support_areas_in_layer = { support_roof_drawn[layer_idx].difference(support_roof_drawn_above), support_roof_drawn[layer_idx].intersection(support_roof_drawn_above) }; bool use_fractional_config = false; @@ -1174,10 +1173,6 @@ void TreeSupportTipGenerator::generateTips( { for (const auto& part : support_areas.splitIntoParts()) { - if (part.area() < config.min_feature_size * config.min_feature_size) - { - continue; - } storage.support.supportLayers[layer_idx] .support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, 0, support_roof_line_distance); } From 774cc0f1176e5ab6a207623221dc1b74bb6dba00 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 17 Oct 2023 18:09:00 +0200 Subject: [PATCH 626/656] Fix flip-around of boolean. Refactor in prep for consolidation. There's some code duplication that needs to be consolidated, so refactor the 'copy' (or original rather) so it's like the others and can be easily combined into one funciton. While that was being done, found that the booleans weren't flipped after the order of the input array was changed. (In order to print the lower bits _first_, _then_ the 'normal' support. part of CURA-10407 --- src/TreeSupport.cpp | 4 ++-- src/TreeSupportTipGenerator.cpp | 4 ++-- src/support.cpp | 26 ++++++++++++-------------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index f1a0504461..8d228ffa71 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2230,14 +2230,14 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor = (layer_idx + 1) >= support_layer_storage.size() || layer_idx <= 0 ? Polygons() : support_layer_storage[layer_idx + 1].offset(config.maximum_move_distance); const auto all_support_areas_in_layer = { support_layer_storage[layer_idx].difference(support_layer_storage_above), support_layer_storage[layer_idx].intersection(support_layer_storage_above) }; - bool use_fractional_config = false; + bool use_fractional_config = true; for (auto& support_areas : all_support_areas_in_layer) { for (auto& part : support_areas.splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. { storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, config.support_wall_count); } - use_fractional_config = true; + use_fractional_config = false; } { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 908544513d..7d669396c9 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1168,7 +1168,7 @@ void TreeSupportTipGenerator::generateTips( = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance); const auto all_support_areas_in_layer = { support_roof_drawn[layer_idx].difference(support_roof_drawn_above), support_roof_drawn[layer_idx].intersection(support_roof_drawn_above) }; - bool use_fractional_config = false; + bool use_fractional_config = true; for (auto& support_areas : all_support_areas_in_layer) { for (const auto& part : support_areas.splitIntoParts()) @@ -1176,7 +1176,7 @@ void TreeSupportTipGenerator::generateTips( storage.support.supportLayers[layer_idx] .support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, 0, support_roof_line_distance); } - use_fractional_config = true; + use_fractional_config = false; } placed_support_lines_support_areas[layer_idx].add(TreeSupportUtils::generateSupportInfillLines( support_roof_drawn[layer_idx], diff --git a/src/support.cpp b/src/support.cpp index ce5c8b5e9b..973b3528e7 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -115,27 +115,25 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( continue; } + coord_t support_line_width_here = support_line_width; + if (layer_nr == 0 && mesh_group_settings.get("adhesion_type") != EPlatformAdhesion::RAFT) + { + support_line_width_here *= infill_extruder.settings.get("initial_layer_line_width_factor"); + } + // We don't generate insets and infill area for the parts yet because later the skirt/brim and prime + // tower will remove themselves from the support, so the outlines of the parts can be changed. + const Polygons& global_support_areas_above = (layer_nr + 1) >= global_support_areas_per_layer.size() || layer_nr <= 0 ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; const auto all_support_areas_in_layer = { global_support_areas.difference(global_support_areas_above), global_support_areas.intersection(global_support_areas_above) }; - bool use_fractional_config = false; + bool use_fractional_config = true; for (auto& support_areas : all_support_areas_in_layer) { - std::vector support_islands = support_areas.splitIntoParts(); - for (const PolygonsPart& island_outline : support_islands) + for (const PolygonsPart& island_outline : support_areas.splitIntoParts()) { - coord_t support_line_width_here = support_line_width; - if (layer_nr == 0 && mesh_group_settings.get("adhesion_type") != EPlatformAdhesion::RAFT) - { - support_line_width_here *= infill_extruder.settings.get("initial_layer_line_width_factor"); - } - // We don't generate insets and infill area for the parts yet because later the skirt/brim and prime - // tower will remove themselves from the support, so the outlines of the parts can be changed. - SupportInfillPart support_infill_part(island_outline, support_line_width_here, use_fractional_config, wall_line_count_this_layer); - - storage.support.supportLayers[layer_nr].support_infill_parts.push_back(support_infill_part); + storage.support.supportLayers[layer_nr].support_infill_parts.emplace_back(island_outline, support_line_width_here, use_fractional_config, wall_line_count_this_layer); } - use_fractional_config = true; + use_fractional_config = false; } } } From 7c02094b0f16fc0dac9a1bbbb55e32c729d428e8 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 17 Oct 2023 18:52:28 +0200 Subject: [PATCH 627/656] Consolidate duplicated code into 'fillInfillParts'. done as part of CURA-10407 --- include/sliceDataStorage.h | 11 +++++++++++ src/TreeSupport.cpp | 15 ++------------- src/TreeSupportTipGenerator.cpp | 15 +-------------- src/sliceDataStorage.cpp | 16 ++++++++++++++++ src/support.cpp | 14 +------------- 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 80b825211a..81d14f24a7 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -226,6 +226,17 @@ class SupportLayer * \param exclude_polygons_boundary_box The boundary box for the polygons to exclude */ void excludeAreasFromSupportInfillAreas(const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box); + + /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height support layers. + * + * \param layer_nr Current layer index. + * \param support_fill_per_layer All of the (infill) support (since the layer above might be needed). + * \param support_line_width Line width of the support extrusions. + * \param wall_line_count Wall-line count around the fill. + * \param grow_layer_above (optional, default to 0) In cases where support shrinks per layer up, an appropriate offset may be nescesary. + * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. + */ + void fillInfillParts(const LayerIndex layer_nr, const std::vector& support_fill_per_layer, const coord_t support_line_width, const coord_t wall_line_count, const coord_t grow_layer_above = 0, const bool unionAll = false); }; class SupportStorage diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 8d228ffa71..7c4fcda0a7 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2226,19 +2226,8 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. } - const Polygons support_layer_storage_above - = (layer_idx + 1) >= support_layer_storage.size() || layer_idx <= 0 ? Polygons() : support_layer_storage[layer_idx + 1].offset(config.maximum_move_distance); - const auto all_support_areas_in_layer - = { support_layer_storage[layer_idx].difference(support_layer_storage_above), support_layer_storage[layer_idx].intersection(support_layer_storage_above) }; - bool use_fractional_config = true; - for (auto& support_areas : all_support_areas_in_layer) - { - for (auto& part : support_areas.splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. - { - storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, config.support_wall_count); - } - use_fractional_config = false; - } + constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. + storage.support.supportLayers[layer_idx].fillInfillParts(layer_idx, support_layer_storage, config.support_line_width, config.support_wall_count, config.maximum_move_distance, convert_every_part); { std::lock_guard critical_section_progress(critical_sections); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 7d669396c9..552f229dd6 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1164,20 +1164,7 @@ void TreeSupportTipGenerator::generateTips( { if (use_fake_roof) { - const Polygons support_roof_drawn_above - = (layer_idx + 1) >= support_roof_drawn.size() || layer_idx <= 0 ? Polygons() : support_roof_drawn[layer_idx + 1].offset(config.maximum_move_distance); - const auto all_support_areas_in_layer - = { support_roof_drawn[layer_idx].difference(support_roof_drawn_above), support_roof_drawn[layer_idx].intersection(support_roof_drawn_above) }; - bool use_fractional_config = true; - for (auto& support_areas : all_support_areas_in_layer) - { - for (const auto& part : support_areas.splitIntoParts()) - { - storage.support.supportLayers[layer_idx] - .support_infill_parts.emplace_back(part, config.support_line_width, use_fractional_config, 0, support_roof_line_distance); - } - use_fractional_config = false; - } + storage.support.supportLayers[layer_idx].fillInfillParts(layer_idx, support_roof_drawn, config.support_line_width, support_roof_line_distance, config.maximum_move_distance); placed_support_lines_support_areas[layer_idx].add(TreeSupportUtils::generateSupportInfillLines( support_roof_drawn[layer_idx], config, diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 4533fbb8be..61c6ff1689 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -705,4 +705,20 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(const Polygons& exclude_po } } +void SupportLayer::fillInfillParts(const LayerIndex layer_nr, const std::vector& support_fill_per_layer, const coord_t support_line_width, const coord_t wall_line_count, const coord_t grow_layer_above /*has default 0*/, const bool unionAll /*has default false*/) +{ + const Polygons& support_this_layer = support_fill_per_layer[layer_nr]; + const Polygons& support_layer_above = (layer_nr + 1) >= support_fill_per_layer.size() || layer_nr <= 0 ? Polygons() : support_fill_per_layer[layer_nr + 1].offset(grow_layer_above); + const auto all_support_areas_in_layer = { support_this_layer.difference(support_layer_above), support_this_layer.intersection(support_layer_above) }; + bool use_fractional_config = true; + for (auto& support_areas : all_support_areas_in_layer) + { + for (const PolygonsPart& island_outline : support_areas.splitIntoParts(unionAll)) + { + support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count); + } + use_fractional_config = false; + } +} + } // namespace cura diff --git a/src/support.cpp b/src/support.cpp index 973b3528e7..a0b049fe04 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -122,19 +122,7 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts( } // We don't generate insets and infill area for the parts yet because later the skirt/brim and prime // tower will remove themselves from the support, so the outlines of the parts can be changed. - - const Polygons& global_support_areas_above - = (layer_nr + 1) >= global_support_areas_per_layer.size() || layer_nr <= 0 ? Polygons() : global_support_areas_per_layer[layer_nr + 1]; - const auto all_support_areas_in_layer = { global_support_areas.difference(global_support_areas_above), global_support_areas.intersection(global_support_areas_above) }; - bool use_fractional_config = true; - for (auto& support_areas : all_support_areas_in_layer) - { - for (const PolygonsPart& island_outline : support_areas.splitIntoParts()) - { - storage.support.supportLayers[layer_nr].support_infill_parts.emplace_back(island_outline, support_line_width_here, use_fractional_config, wall_line_count_this_layer); - } - use_fractional_config = false; - } + storage.support.supportLayers[layer_nr].fillInfillParts(layer_nr, global_support_areas_per_layer, support_line_width_here, wall_line_count_this_layer); } } From bd1b63430edd92a5f8f6815a4d3d219fe70351fb Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 17 Oct 2023 19:22:21 +0200 Subject: [PATCH 628/656] Print lower fractional layer heights before full ones, pt 2. Take care of the order optimizer w.r.t. partially fractional-height support-layers. part of CURA-10407 --- src/FffGcodeWriter.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index d232cc5d7c..2350305217 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -21,6 +21,7 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include #include #include #include @@ -3148,11 +3149,13 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const auto zag_skip_count = infill_extruder.settings.get("support_zag_skip_count"); // create a list of outlines and use PathOrderOptimizer to optimize the travel move + PathOrderOptimizer island_order_optimizer_initial(gcode_layer.getLastPlannedPositionOrStartingPosition()); PathOrderOptimizer island_order_optimizer(gcode_layer.getLastPlannedPositionOrStartingPosition()); for (const SupportInfillPart& part : support_layer.support_infill_parts) { - island_order_optimizer.addPolygon(&part); + (part.use_fractional_config ? island_order_optimizer_initial : island_order_optimizer).addPolygon(&part); } + island_order_optimizer_initial.optimize(); island_order_optimizer.optimize(); const auto support_connect_zigzags = infill_extruder.settings.get("support_connect_zigzags"); @@ -3165,7 +3168,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer bool need_travel_to_end_of_last_spiral = true; // Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers) - for (const PathOrdering& path : island_order_optimizer.paths) + for (const PathOrdering& path : ranges::views::concat(island_order_optimizer_initial.paths, island_order_optimizer.paths)) { const SupportInfillPart& part = *path.vertices; const auto& configs = part.use_fractional_config ? gcode_layer.configs_storage.support_fractional_infill_config : gcode_layer.configs_storage.support_infill_config; From 42b3a7115585e5efbfb27c5cb9c27ac9d256c53b Mon Sep 17 00:00:00 2001 From: rburema Date: Tue, 17 Oct 2023 17:25:07 +0000 Subject: [PATCH 629/656] Applied clang-format. --- include/sliceDataStorage.h | 13 ++++++++++--- src/TreeSupport.cpp | 5 +++-- src/TreeSupportTipGenerator.cpp | 3 ++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 81d14f24a7..dd99d8d197 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -227,16 +227,23 @@ class SupportLayer */ void excludeAreasFromSupportInfillAreas(const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box); - /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height support layers. + /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height + * support layers. * * \param layer_nr Current layer index. * \param support_fill_per_layer All of the (infill) support (since the layer above might be needed). * \param support_line_width Line width of the support extrusions. * \param wall_line_count Wall-line count around the fill. * \param grow_layer_above (optional, default to 0) In cases where support shrinks per layer up, an appropriate offset may be nescesary. - * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. + * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. */ - void fillInfillParts(const LayerIndex layer_nr, const std::vector& support_fill_per_layer, const coord_t support_line_width, const coord_t wall_line_count, const coord_t grow_layer_above = 0, const bool unionAll = false); + void fillInfillParts( + const LayerIndex layer_nr, + const std::vector& support_fill_per_layer, + const coord_t support_line_width, + const coord_t wall_line_count, + const coord_t grow_layer_above = 0, + const bool unionAll = false); }; class SupportStorage diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 7c4fcda0a7..9ce458d7f7 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2226,8 +2226,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. } - constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. - storage.support.supportLayers[layer_idx].fillInfillParts(layer_idx, support_layer_storage, config.support_line_width, config.support_wall_count, config.maximum_move_distance, convert_every_part); + constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. + storage.support.supportLayers[layer_idx] + .fillInfillParts(layer_idx, support_layer_storage, config.support_line_width, config.support_wall_count, config.maximum_move_distance, convert_every_part); { std::lock_guard critical_section_progress(critical_sections); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 552f229dd6..d362052739 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1164,7 +1164,8 @@ void TreeSupportTipGenerator::generateTips( { if (use_fake_roof) { - storage.support.supportLayers[layer_idx].fillInfillParts(layer_idx, support_roof_drawn, config.support_line_width, support_roof_line_distance, config.maximum_move_distance); + storage.support.supportLayers[layer_idx] + .fillInfillParts(layer_idx, support_roof_drawn, config.support_line_width, support_roof_line_distance, config.maximum_move_distance); placed_support_lines_support_areas[layer_idx].add(TreeSupportUtils::generateSupportInfillLines( support_roof_drawn[layer_idx], config, From 9e1e888d4e6d7f49b9db2e55ea79504fb5527fdb Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 18 Oct 2023 13:13:49 +0200 Subject: [PATCH 630/656] Code cleaning/readability CURA-10783 --- .clang-format | 6 +++--- include/PrimeTower.h | 15 ++++++++++----- include/SkirtBrim.h | 4 ++-- src/PrimeTower.cpp | 20 ++++++++++---------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/.clang-format b/.clang-format index 5d90caa166..e5f6fc2257 100644 --- a/.clang-format +++ b/.clang-format @@ -59,11 +59,11 @@ ForEachMacros: IncludeBlocks: Regroup IncludeCategories: - Priority: 2 - Regex: ^<(scripta|spdlog|range|fmt|Arcus|agrpc|grpc|boost)/ + Regex: '^<(scripta|spdlog|range|fmt|Arcus|agrpc|grpc|boost)/.*' - Priority: 3 - Regex: ^(<|"(gtest|gmock|isl|json)/) + Regex: '^((<|")(gtest|gmock|isl|json)/)' - Priority: 1 - Regex: .* + Regex: '^<.*' IncludeIsMainRegex: (Test)?$ IndentCaseLabels: false IndentWidth: 4 diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 2201ce7544..0ae2350b6a 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -4,11 +4,12 @@ #ifndef PRIME_TOWER_H #define PRIME_TOWER_H +#include + #include "settings/types/LayerIndex.h" #include "utils/polygon.h" // Polygons #include "utils/polygonUtils.h" -#include namespace cura { @@ -30,7 +31,11 @@ class PrimeTower Polygons polygons; Polygons lines; }; - unsigned int extruder_count; //!< Number of extruders + + using MovesByExtruder = std::vector; + using MovesByLayer = std::vector; + + size_t extruder_count; //!< Number of extruders bool wipe_from_middle; //!< Whether to wipe on the inside of the hollow prime tower Point middle; //!< The middle of the prime tower @@ -40,8 +45,8 @@ class PrimeTower std::vector prime_tower_start_locations; //!< The differernt locations where to pre-wipe the active nozzle const unsigned int number_of_prime_tower_start_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations - std::vector pattern_per_extruder; //!< For each extruder the pattern to print on all layers of the prime tower. - std::vector> pattern_extra_brim_per_layer; //!< For each layer of each extruder, the extra pattern to be added for adhesion and/or strength + MovesByExtruder prime_moves; //!< For each extruder, the moves to be processed for actual priming. + MovesByLayer base_extra_moves; //!< For each layer and each extruder, the extra moves to be processed for better adhesion/strength Polygons outer_poly; //!< The outline of the outermost prime tower. std::vector outer_poly_base; //!< The outline of the layers having extra width for the base @@ -58,7 +63,7 @@ class PrimeTower * This is the spatial order from outside to inside. This is NOT the actual * order in time in which they are printed. */ - std::vector extruder_order; + std::vector extruder_order; /*! * \brief Creates a prime tower instance that will determine where and how diff --git a/include/SkirtBrim.h b/include/SkirtBrim.h index fa41df0999..896e4aac45 100644 --- a/include/SkirtBrim.h +++ b/include/SkirtBrim.h @@ -4,13 +4,13 @@ #ifndef SKIRT_BRIM_H #define SKIRT_BRIM_H +#include + #include "ExtruderTrain.h" #include "settings/EnumSettings.h" #include "sliceDataStorage.h" #include "utils/Coord_t.h" -#include - namespace cura { diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index fa291fedc4..a9c1461cf1 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -3,6 +3,9 @@ #include "PrimeTower.h" +#include +#include + #include "Application.h" //To get settings. #include "ExtruderTrain.h" #include "LayerPlan.h" @@ -14,9 +17,6 @@ #include "raft.h" #include "sliceDataStorage.h" -#include -#include - #define CIRCLE_RESOLUTION 32 // The number of vertices in each circle. @@ -154,8 +154,8 @@ void PrimeTower::generatePaths_denseInfill() const int base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); const coord_t line_width = scene.extruders[extruder_order.front()].settings.get("prime_tower_line_width"); - pattern_per_extruder.resize(extruder_count); - pattern_extra_brim_per_layer.resize(extruder_count); + prime_moves.resize(extruder_count); + base_extra_moves.resize(extruder_count); coord_t cumulative_inset = 0; // Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. for (size_t extruder_nr : extruder_order) @@ -164,7 +164,7 @@ void PrimeTower::generatePaths_denseInfill() const coord_t required_volume = MM3_2INT(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]; + ExtrusionMoves& pattern = prime_moves[extruder_nr]; // Create the walls of the prime tower. unsigned int wall_nr = 0; @@ -195,7 +195,7 @@ void PrimeTower::generatePaths_denseInfill() extra_radius = line_width * extra_rings; outer_poly_base.push_back(outer_poly.offset(extra_radius)); - pattern_extra_brim_per_layer[extruder_nr].push_back(generatePaths_base(outer_poly, extra_rings, line_width)); + base_extra_moves[extruder_nr].push_back(generatePaths_base(outer_poly, extra_rings, line_width)); } } @@ -207,7 +207,7 @@ void PrimeTower::generatePaths_denseInfill() ExtrusionMoves pattern = generatePaths_inset(outer_poly, line_width, cumulative_inset); if (! pattern.polygons.empty() || ! pattern.lines.empty()) { - pattern_extra_brim_per_layer[extruder_nr].push_back(pattern); + base_extra_moves[extruder_nr].push_back(pattern); } } } @@ -280,12 +280,12 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext { // Actual prime pattern const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - const ExtrusionMoves& pattern = pattern_per_extruder[extruder_nr]; + const ExtrusionMoves& pattern = prime_moves[extruder_nr]; gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); } - const std::vector& pattern_extra_brim = pattern_extra_brim_per_layer[extruder_nr]; + const std::vector& pattern_extra_brim = base_extra_moves[extruder_nr]; if (absolute_layer_number < pattern_extra_brim.size()) { // Extra rings for stronger base From e58f31c3cfbe331dfa1910e913e05642ba5932a7 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Wed, 18 Oct 2023 11:14:33 +0000 Subject: [PATCH 631/656] Applied clang-format. --- include/sliceDataStorage.h | 8 ++++---- src/FffGcodeWriter.cpp | 24 ++++++++++++------------ src/FffPolygonGenerator.cpp | 4 ++-- src/LayerPlanBuffer.cpp | 4 ++-- src/SkirtBrim.cpp | 4 ++-- src/TreeModelVolumes.cpp | 10 +++++----- src/raft.cpp | 4 ++-- src/sliceDataStorage.cpp | 4 ++-- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index c3313e801a..6a362522ef 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -4,6 +4,10 @@ #ifndef SLICE_DATA_STORAGE_H #define SLICE_DATA_STORAGE_H +#include +#include +#include + #include "PrimeTower.h" #include "RetractionConfig.h" #include "SupportInfillPart.h" @@ -18,10 +22,6 @@ #include "utils/NoCopy.h" #include "utils/polygon.h" -#include -#include -#include - // libArachne #include "utils/ExtrusionLine.h" diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 78a541c00e..db904f0e3a 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3,6 +3,18 @@ #include "FffGcodeWriter.h" +#include +#include // numeric_limits +#include +#include +#include +#include + +#include +#include +#include +#include + #include "Application.h" #include "ExtruderTrain.h" #include "FffProcessor.h" @@ -21,18 +33,6 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" -#include -#include -#include -#include - -#include -#include // numeric_limits -#include -#include -#include -#include - namespace cura { diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 640a563284..3b1a5e4c38 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -1,14 +1,14 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include - #include #include #include // ifstream.good() #include // multimap (ordered map allowing duplicate keys) #include +#include + // Code smell: Order of the includes is important here, probably due to some forward declarations which might be masking some undefined behaviours // clang-format off #include "Application.h" diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index b5bede2665..d5a033a136 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -3,6 +3,8 @@ #include "LayerPlanBuffer.h" +#include + #include "Application.h" //To flush g-code through the communication channel. #include "ExtruderTrain.h" #include "FffProcessor.h" @@ -11,8 +13,6 @@ #include "communication/Communication.h" //To flush g-code through the communication channel. #include "gcodeExport.h" -#include - namespace cura { diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 522e0994f5..4b77b449d3 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -3,6 +3,8 @@ #include "SkirtBrim.h" +#include + #include "Application.h" #include "ExtruderTrain.h" #include "Slice.h" @@ -13,8 +15,6 @@ #include "utils/PolylineStitcher.h" #include "utils/Simplify.h" //Simplifying the brim/skirt at every inset. -#include - namespace cura { diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index aea31de82c..4604967baf 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -3,6 +3,11 @@ #include "TreeModelVolumes.h" +#include +#include +#include +#include + #include "TreeSupport.h" #include "TreeSupportEnums.h" #include "progress/Progress.h" @@ -10,11 +15,6 @@ #include "utils/ThreadPool.h" #include "utils/algorithm.h" -#include -#include -#include -#include - namespace cura { diff --git a/src/raft.cpp b/src/raft.cpp index 665cc833f5..e13836b43f 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -3,6 +3,8 @@ #include "raft.h" +#include + #include "Application.h" //To get settings. #include "ExtruderTrain.h" #include "Slice.h" @@ -10,8 +12,6 @@ #include "sliceDataStorage.h" #include "utils/math.h" -#include - namespace cura { diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index c560a2b675..b9b978d557 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -3,6 +3,8 @@ #include "sliceDataStorage.h" +#include + #include "Application.h" //To get settings. #include "ExtruderTrain.h" #include "FffProcessor.h" //To create a mesh group with if none is provided. @@ -14,8 +16,6 @@ #include "raft.h" #include "utils/math.h" //For PI. -#include - namespace cura { From 87fbc0aca0dfd1a90eee2d36c20ef98aa7b62460 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 19 Oct 2023 10:15:45 +0200 Subject: [PATCH 632/656] Moved and optimized generic geometric methods --- include/PrimeTower.h | 32 +--- include/utils/polygonUtils.h | 330 ++++++++++++++++++++++------------- src/PrimeTower.cpp | 53 ++---- src/utils/polygonUtils.cpp | 44 ++++- 4 files changed, 251 insertions(+), 208 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 0ae2350b6a..a493cfb3be 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -26,13 +26,7 @@ class LayerPlan; class PrimeTower { private: - struct ExtrusionMoves - { - Polygons polygons; - Polygons lines; - }; - - using MovesByExtruder = std::vector; + using MovesByExtruder = std::vector; using MovesByLayer = std::vector; size_t extruder_count; //!< Number of extruders @@ -122,30 +116,6 @@ class PrimeTower const Polygons& getGroundPoly() const; private: - /*! - * \see PrimeTower::generatePaths - * - * Generate extra rings around the actual prime rings for a stronger base - * - * \param inset The inner circle of the rings to start generating the rings from - * \param rings The number of rings to add - * \param line_width The actual line width to distance the rings from each other - * \return The generated rings paths - */ - static ExtrusionMoves generatePaths_base(const Polygons& inset, size_t rings, coord_t line_width); - - /*! - * \see PrimeTower::generatePaths - * - * Generate extra rings inside the given circle for a better adhesion on the first layer - * - * \param outer_poly The outer polygon to start generating the rings from - * \param line_width The actual line width to distance the rings from each other - * \param initial_inset The inset distance to be added to the first generated ring - * \return The generated rings paths - */ - static ExtrusionMoves generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset); - /*! * \see PrimeTower::generatePaths * diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 13786e71c7..c4ea2c2db6 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -1,20 +1,20 @@ -//Copyright (c) 2021 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2021 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef UTILS_POLYGON_UTILS_H #define UTILS_POLYGON_UTILS_H #include // function #include -#include #include // unique_ptr +#include -#include "polygon.h" -#include "SparsePointGridInclusive.h" -#include "SparseLineGrid.h" #include "PolygonsPointIndex.h" +#include "SparseLineGrid.h" +#include "SparsePointGridInclusive.h" +#include "polygon.h" -namespace cura +namespace cura { /*! @@ -26,10 +26,23 @@ struct ClosestPolygonPoint ConstPolygonPointer poly; //!< Polygon in which the result was found (or nullptr if no result was found) unsigned int poly_idx; //!< The index of the polygon in some Polygons where ClosestPolygonPoint::poly can be found unsigned int point_idx; //!< Index to the first point in the polygon of the line segment on which the result was found - ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly) : location(p), poly(poly), poly_idx(NO_INDEX), point_idx(pos) {}; - ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly, int poly_idx) : location(p), poly(poly), poly_idx(poly_idx), point_idx(pos) {}; - ClosestPolygonPoint(ConstPolygonRef poly) : poly(poly), poly_idx(NO_INDEX), point_idx(NO_INDEX) {}; - ClosestPolygonPoint() : poly_idx(NO_INDEX), point_idx(NO_INDEX) {}; + ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly) + : location(p) + , poly(poly) + , poly_idx(NO_INDEX) + , point_idx(pos){}; + ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly, int poly_idx) + : location(p) + , poly(poly) + , poly_idx(poly_idx) + , point_idx(pos){}; + ClosestPolygonPoint(ConstPolygonRef poly) + : poly(poly) + , poly_idx(NO_INDEX) + , point_idx(NO_INDEX){}; + ClosestPolygonPoint() + : poly_idx(NO_INDEX) + , point_idx(NO_INDEX){}; Point p() const { // conformity with other classes return location; @@ -50,7 +63,7 @@ struct ClosestPolygonPoint namespace std { -template <> +template<> struct hash { size_t operator()(const cura::ClosestPolygonPoint& cpp) const @@ -58,12 +71,12 @@ struct hash return std::hash()(cpp.p()); } }; -}//namespace std +} // namespace std namespace std { -template +template struct hash> { size_t operator()(const std::pair& pair) const @@ -71,7 +84,7 @@ struct hash> return 31 * std::hash()(pair.first) + 59 * std::hash()(pair.second); } }; -}//namespace std +} // namespace std namespace cura @@ -88,18 +101,18 @@ struct GivenDistPoint typedef SparseLineGrid LocToLineGrid; -class PolygonUtils +class PolygonUtils { public: static const std::function no_penalty_function; //!< Function always returning zero /*! * compute the length of a segment of a polygon - * + * * if \p end == \p start then the full polygon is taken - * + * * \warning assumes that start and end lie on the same polygon! - * + * * \param start The start vertex of the segment * \param end the end vertex of the segment * \return the total length of all the line segments in between the two vertices. @@ -108,12 +121,12 @@ class PolygonUtils /*! * Generate evenly spread out dots along a segment of a polygon - * + * * Start at a distance from \p start and end at a distance from \p end, * unless \p end == \p start; then that point is in the result - * + * * \warning Assumes that start and end lie on the same polygon! - * + * * \param start The start vertex of the segment * \param end the end vertex of the segment * \param n_dots number of dots to spread out @@ -131,48 +144,54 @@ class PolygonUtils /*! * Whether a polygon intersects with a line-segment. If true, the closest collision point to 'b' is stored in the result. */ - static bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const Polygons& current_outlines, const LocToLineGrid& outline_locator, Point& result, const coord_t within_max_dist); + static bool lineSegmentPolygonsIntersection( + const Point& a, + const Point& b, + const Polygons& current_outlines, + const LocToLineGrid& outline_locator, + Point& result, + const coord_t within_max_dist); /*! * Get the normal of a boundary point, pointing outward. * Only the direction is set. * Nothing is said about the length of the vector returned. - * + * * \param poly The polygon. * \param point_idx The index of the point in the polygon. */ static Point getVertexInwardNormal(ConstPolygonRef poly, unsigned int point_idx); /*! - * Get a point from the \p poly with a given \p offset. - * - * \param poly The polygon. - * \param point_idx The index of the point in the polygon. - * \param offset The distance the point has to be moved outward from the polygon. - * \return A point at the given distance inward from the point on the boundary polygon. - */ + * Get a point from the \p poly with a given \p offset. + * + * \param poly The polygon. + * \param point_idx The index of the point in the polygon. + * \param offset The distance the point has to be moved outward from the polygon. + * \return A point at the given distance inward from the point on the boundary polygon. + */ static Point getBoundaryPointWithOffset(ConstPolygonRef poly, unsigned int point_idx, int64_t offset); /*! * Move a point away from the boundary by looking at the boundary normal of the nearest vert. - * + * * \param point_on_boundary The object holding the point on the boundary along with the information of which line segment the point is on. * \param offset The distance the point has to be moved inward from the polygon. */ static Point moveInsideDiagonally(ClosestPolygonPoint point_on_boundary, int64_t inset); /*! - * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. - * Given a \p distance more than zero, the point will end up inside, and conversely outside. - * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. - * When the point is in/outside by less than \p distance, \p from is moved to the correct place. - * - * \param polygons The polygons onto which to move the point - * \param from[in,out] The point to move. - * \param distance The distance by which to move the point. - * \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon. - * \return The index to the polygon onto which we have moved the point. - */ + * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. + * Given a \p distance more than zero, the point will end up inside, and conversely outside. + * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. + * When the point is in/outside by less than \p distance, \p from is moved to the correct place. + * + * \param polygons The polygons onto which to move the point + * \param from[in,out] The point to move. + * \param distance The distance by which to move the point. + * \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon. + * \return The index to the polygon onto which we have moved the point. + */ static unsigned int moveInside(const Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits::max()); /** @@ -198,11 +217,11 @@ class PolygonUtils * Given a \p distance more than zero, the point will end up inside, and conversely outside. * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. * When the point is in/outside by less than \p distance, \p from is moved to the correct place. - * + * * \warning If \p loc_to_line_grid is used, it's best to have all and only \p polygons in there. * If \p from is not closest to \p polygons this function may * return a ClosestPolygonPoint on a polygon in \p loc_to_line_grid which is not in \p polygons. - * + * * \param polygons The polygons onto which to move the point * \param from[in,out] The point to move. * \param distance The distance by which to move the point. @@ -212,18 +231,25 @@ class PolygonUtils * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. * \return The point on the polygon closest to \p from */ - static ClosestPolygonPoint moveInside2(const Polygons& polygons, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits::max(), const Polygons* loc_to_line_polygons = nullptr, const LocToLineGrid* loc_to_line_grid = nullptr, const std::function& penalty_function = no_penalty_function); + static ClosestPolygonPoint moveInside2( + const Polygons& polygons, + Point& from, + const int distance = 0, + const int64_t max_dist2 = std::numeric_limits::max(), + const Polygons* loc_to_line_polygons = nullptr, + const LocToLineGrid* loc_to_line_grid = nullptr, + const std::function& penalty_function = no_penalty_function); /*! * Moves the point \p from onto the nearest segment of \p polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. * Given a \p distance more than zero, the point will end up inside, and conversely outside. * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. * When the point is in/outside by less than \p distance, \p from is moved to the correct place. - * + * * \warning When a \p loc_to_line is given this function only considers nearby elements. * Even when the penalty function favours elements farther away. * Also using the \p loc_to_line_grid automatically considers \p all_polygons - * + * * \param loc_to_line_polygons All polygons which are present in the \p loc_to_line_grid of which \p polygon is an element * \param polygon The polygon onto which to move the point * \param from[in,out] The point to move. @@ -233,16 +259,23 @@ class PolygonUtils * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. * \return The point on the polygon closest to \p from */ - static ClosestPolygonPoint moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits::max(), const LocToLineGrid* loc_to_line_grid = nullptr, const std::function& penalty_function = no_penalty_function); + static ClosestPolygonPoint moveInside2( + const Polygons& loc_to_line_polygons, + ConstPolygonRef polygon, + Point& from, + const int distance = 0, + const int64_t max_dist2 = std::numeric_limits::max(), + const LocToLineGrid* loc_to_line_grid = nullptr, + const std::function& penalty_function = no_penalty_function); /*! * The opposite of moveInside. - * + * * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within \p distance. * Given a \p distance more than zero, the point will end up outside, and conversely inside. * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. * When the point is in/outside by less than \p distance, \p from is moved to the correct place. - * + * * \param polygons The polygons onto which to move the point * \param from[in,out] The point to move. * \param distance The distance by which to move the point. @@ -251,23 +284,23 @@ class PolygonUtils * \return The index to the polygon onto which we have moved the point. */ static unsigned int moveOutside(const Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits::max()); - + /*! * Compute a point at a distance from a point on the boundary in orthogonal direction to the boundary. * Given a \p distance more than zero, the point will end up inside, and conversely outside. - * + * * \param cpp The object holding the point on the boundary along with the information of which line segment the point is on. * \param distance The distance by which to move the point. * \return A point at a \p distance from the point in \p cpp orthogonal to the boundary there. */ static Point moveInside(const ClosestPolygonPoint& cpp, const int distance); - + /*! * The opposite of moveInside. - * + * * Compute a point at a distance from a point on the boundary in orthogonal direction to the boundary. * Given a \p distance more than zero, the point will end up outside, and conversely inside. - * + * * \param cpp The object holding the point on the boundary along with the information of which line segment the point is on. * \param distance The distance by which to move the point. * \return A point at a \p distance from the point in \p cpp orthogonal to the boundary there. @@ -279,15 +312,15 @@ class PolygonUtils * Given a \p distance more than zero, the point will end up inside, and conversely outside. * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. * When the point is in/outside by less than \p distance, \p from is moved to the correct place. - * + * * \warning May give false positives. - * Some checking is done to make sure we end up inside the polygon, + * Some checking is done to make sure we end up inside the polygon, * but it might still be the case that we end up outside: * when the closest point on the boundary is very close to another polygon - * + * * \warning When using a \p loc_to_line_grid which contains more polygons than just \p polygons, * the results is only correct if \p from is already closest to \p polygons, rather than other polygons in the \p loc_to_line_grid. - * + * * \param polygons The polygons onto which to move the point * \param from[in,out] The point to move. * \param preferred_dist_inside The preferred distance from the boundary to the point @@ -297,22 +330,29 @@ class PolygonUtils * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. * \return The point on the polygon closest to \p from */ - static ClosestPolygonPoint ensureInsideOrOutside(const Polygons& polygons, Point& from, int preferred_dist_inside, int64_t max_dist2 = std::numeric_limits::max(), const Polygons* loc_to_line_polygons = nullptr, const LocToLineGrid* loc_to_line_grid = nullptr, const std::function& penalty_function = no_penalty_function); + static ClosestPolygonPoint ensureInsideOrOutside( + const Polygons& polygons, + Point& from, + int preferred_dist_inside, + int64_t max_dist2 = std::numeric_limits::max(), + const Polygons* loc_to_line_polygons = nullptr, + const LocToLineGrid* loc_to_line_grid = nullptr, + const std::function& penalty_function = no_penalty_function); /*! * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within \p distance. * Given a \p distance more than zero, the point will end up inside, and conversely outside. * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. * When the point is in/outside by less than \p distance, \p from is moved to the correct place. - * + * * \warning May give false positives. - * Some checking is done to make sure we end up inside the polygon, + * Some checking is done to make sure we end up inside the polygon, * but it might still be the case that we end up outside: * when the closest point on the boundary is very close to another polygon - * + * * \warning When using a \p loc_to_line_grid which contains more polygons than just \p polygons, * the results is only correct if \p from is already closest to \p polygons, rather than other polygons in the \p loc_to_line_grid. - * + * * \param polygons The polygons onto which to move the point * \param from[in,out] The point to move. * \param closest_polygon_point The point on \p polygons closest to \p from @@ -322,49 +362,56 @@ class PolygonUtils * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. * \return The point on the polygon closest to \p from */ - static ClosestPolygonPoint ensureInsideOrOutside(const Polygons& polygons, Point& from, const ClosestPolygonPoint& closest_polygon_point, int preferred_dist_inside, const Polygons* loc_to_line_polygons = nullptr, const LocToLineGrid* loc_to_line_grid = nullptr, const std::function& penalty_function = no_penalty_function); + static ClosestPolygonPoint ensureInsideOrOutside( + const Polygons& polygons, + Point& from, + const ClosestPolygonPoint& closest_polygon_point, + int preferred_dist_inside, + const Polygons* loc_to_line_polygons = nullptr, + const LocToLineGrid* loc_to_line_grid = nullptr, + const std::function& penalty_function = no_penalty_function); /*! - * - * \warning Assumes \p poly1_result and \p poly2_result have their pos and poly fields initialized! - */ + * + * \warning Assumes \p poly1_result and \p poly2_result have their pos and poly fields initialized! + */ static void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result); /*! - * Find the nearest closest point on a polygon from a given index. - * - * \param from The point from which to get the smallest distance. - * \param polygon The polygon on which to find the point with the smallest distance. - * \param start_idx The index of the point in the polygon from which to start looking. - * \return The nearest point from \p start_idx going along the \p polygon (in both directions) with a locally minimal distance to \p from. - */ + * Find the nearest closest point on a polygon from a given index. + * + * \param from The point from which to get the smallest distance. + * \param polygon The polygon on which to find the point with the smallest distance. + * \param start_idx The index of the point in the polygon from which to start looking. + * \return The nearest point from \p start_idx going along the \p polygon (in both directions) with a locally minimal distance to \p from. + */ static ClosestPolygonPoint findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx); /*! - * Find the nearest closest point on a polygon from a given index walking in one direction along the polygon. - * - * \param from The point from which to get the smallest distance. - * \param polygon The polygon on which to find the point with the smallest distance. - * \param start_idx The index of the point in the polygon from which to start looking. - * \param direction The direction to walk: 1 for walking along the \p polygon, -1 for walking in opposite direction - * \return The nearest point from \p start_idx going along the \p polygon with a locally minimal distance to \p from. - */ + * Find the nearest closest point on a polygon from a given index walking in one direction along the polygon. + * + * \param from The point from which to get the smallest distance. + * \param polygon The polygon on which to find the point with the smallest distance. + * \param start_idx The index of the point in the polygon from which to start looking. + * \param direction The direction to walk: 1 for walking along the \p polygon, -1 for walking in opposite direction + * \return The nearest point from \p start_idx going along the \p polygon with a locally minimal distance to \p from. + */ static ClosestPolygonPoint findNearestClosest(const Point from, ConstPolygonRef polygon, int start_idx, int direction); /*! * Find the point closest to \p from in all polygons in \p polygons. - * + * * \note The penalty term is applied to the *squared* distance score - * + * * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. */ static ClosestPolygonPoint findClosest(Point from, const Polygons& polygons, const std::function& penalty_function = no_penalty_function); - + /*! * Find the point closest to \p from in the polygon \p polygon. - * + * * \note The penalty term is applied to the *squared* distance score - * + * * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. */ static ClosestPolygonPoint findClosest(Point from, ConstPolygonRef polygon, const std::function& penalty_function = no_penalty_function); @@ -387,9 +434,9 @@ class PolygonUtils /*! * Create a SparsePointGridInclusive mapping from locations to line segments occurring in the \p polygons - * + * * \warning The caller of this function is responsible for deleting the returned object - * + * * \param polygons The polygons for which to create the mapping * \param square_size The cell size used to bundle line segments (also used to chop up lines so that multiple cells contain the same long line) * \return A bucket grid mapping spatial locations to poly-point indices into \p polygons @@ -398,57 +445,63 @@ class PolygonUtils /*! * Find the line segment closest to a given point \p from within a cell-block of a size defined in the SparsePointGridInclusive \p loc_to_line - * + * * \note The penalty term is applied to the *squared* distance score. * Note also that almost only nearby points are considered even when the penalty function would favour points farther away. - * + * * \param from The location to find a polygon edge close to * \param polygons The polygons for which the \p loc_to_line has been built up - * \param loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segmetns of the \p polygons + * \param loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segmetns of the \p polygons * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. * \return The nearest point on the polygon if the polygon was within a distance equal to the cell_size of the SparsePointGridInclusive */ - static std::optional findClose(Point from, const Polygons& polygons, const LocToLineGrid& loc_to_line, const std::function& penalty_function = no_penalty_function); + static std::optional + findClose(Point from, const Polygons& polygons, const LocToLineGrid& loc_to_line, const std::function& penalty_function = no_penalty_function); /*! * Find the line segment closest to any point on \p from within cell-blocks of a size defined in the SparsePointGridInclusive \p destination_loc_to_line - * + * * \note The penalty term is applied to the *squared* distance score. * Note also that almost only nearby points are considered even when the penalty function would favour points farther away. - * + * * \param from The polygon for which to find a polygon edge close to * \param destination The polygons for which the \p destination_loc_to_line has been built up - * \param destination_loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segments of the \p destination + * \param destination_loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segments of the \p destination * \param penalty_function A function returning a penalty term on the squared distance score of a candidate point. - * \return A collection of near crossing from the \p from polygon to the \p destination polygon. Each element in the sollection is a pair with as first a cpp in the \p from polygon and as second a cpp in the \p destination polygon. + * \return A collection of near crossing from the \p from polygon to the \p destination polygon. Each element in the sollection is a pair with as first a cpp in the \p from + * polygon and as second a cpp in the \p destination polygon. */ - static std::vector> findClose(ConstPolygonRef from, const Polygons& destination, const LocToLineGrid& destination_loc_to_line, const std::function& penalty_function = no_penalty_function); + static std::vector> findClose( + ConstPolygonRef from, + const Polygons& destination, + const LocToLineGrid& destination_loc_to_line, + const std::function& penalty_function = no_penalty_function); /*! * Checks whether a given line segment collides with polygons as given in a loc_to_line grid. - * + * * If the line segment doesn't intersect with any edge of the polygon, but * merely touches it, a collision is also reported. For instance, a * collision is reported when the an endpoint of the line is exactly on the * polygon, and when the line coincides with an edge. - * + * * \param[in] from The start point * \param[in] to The end point - * \param[in] loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segmetns of the \p polygons + * \param[in] loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segmetns of the \p polygons * \param[out] collision_result (optional) The polygons segment intersecting with the line segment * \return whether the line segment collides with the boundary of the polygons */ static bool polygonCollidesWithLineSegment(const Point from, const Point to, const LocToLineGrid& loc_to_line, PolygonsPointIndex* collision_result = nullptr); /*! - * Find the next point (going along the direction of the polygon) with a distance \p dist from the point \p from within the \p poly. - * Returns whether another point could be found within the \p poly which can be found before encountering the point at index \p start_idx. - * The point \p from and the polygon \p poly are assumed to lie on the same plane. - * - * \param from The point from whitch to find a point on the polygon satisfying the conditions - * \param start_idx the index of the prev poly point on the poly. - * \param poly_start_idx The index of the point in the polygon which is to be handled as the start of the polygon. No point further than this point will be the result. - */ + * Find the next point (going along the direction of the polygon) with a distance \p dist from the point \p from within the \p poly. + * Returns whether another point could be found within the \p poly which can be found before encountering the point at index \p start_idx. + * The point \p from and the polygon \p poly are assumed to lie on the same plane. + * + * \param from The point from whitch to find a point on the polygon satisfying the conditions + * \param start_idx the index of the prev poly point on the poly. + * \param poly_start_idx The index of the point in the polygon which is to be handled as the start of the polygon. No point further than this point will be the result. + */ static bool getNextPointWithDistance(Point from, int64_t dist, ConstPolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result); /*! @@ -458,10 +511,10 @@ class PolygonUtils /*! * Get the point on a polygon which intersects a line parallel to a line going through the starting point and through another point. - * + * * Note that the looking direction \p forward doesn't neccesarily determine on which side of the line we cross a parallel line. * Depending on the geometry of the polygon the next intersection may be left or right of the input line. - * + * * \param start The starting point of the search and the starting point of the line * \param line_to The end point of the line * \param dist The distance from the parallel line to the line defined by the previous two parameters @@ -474,12 +527,12 @@ class PolygonUtils * Checks whether a given line segment collides with a given polygon(s). * The transformed_startPoint and transformed_endPoint should have the same * Y coordinate. - * + * * If the line segment doesn't intersect with any edge of the polygon, but * merely touches it, a collision is also reported. For instance, a * collision is reported when the an endpoint of the line is exactly on the * polygon, and when the line coincides with an edge. - * + * * \param poly The polygon * \param transformed_startPoint The start point transformed such that it is * on the same horizontal line as the end point @@ -494,12 +547,12 @@ class PolygonUtils /*! * Checks whether a given line segment collides with a given polygon(s). - * + * * If the line segment doesn't intersect with any edge of the polygon, but * merely touches it, a collision is also reported. For instance, a * collision is reported when the an endpoint of the line is exactly on the * polygon, and when the line coincides with an edge. - * + * * \param poly The polygon * \param startPoint The start point * \param endPoint The end point @@ -512,12 +565,12 @@ class PolygonUtils * Checks whether a given line segment collides with a given polygon(s). * The transformed_startPoint and transformed_endPoint should have the same * Y coordinate. - * + * * If the line segment doesn't intersect with any edge of the polygon, but * merely touches it, a collision is also reported. For instance, a * collision is reported when the an endpoint of the line is exactly on the * polygon, and when the line coincides with an edge. - * + * * \param poly The polygon * \param transformed_startPoint The start point transformed such that it is * on the same horizontal line as the end point @@ -532,12 +585,12 @@ class PolygonUtils /*! * Checks whether a given line segment collides with a given polygon(s). - * + * * If the line segment doesn't intersect with any edge of the polygon, but * merely touches it, a collision is also reported. For instance, a * collision is reported when the an endpoint of the line is exactly on the * polygon, and when the line coincides with an edge. - * + * * \param poly The polygon * \param startPoint The start point * \param endPoint The end point @@ -573,7 +626,11 @@ class PolygonUtils * \param[in] possible_adjacent_polys The vector of polygons we are testing. * \param[in] max_gap Polygons must be closer together than this distance to be considered adjacent. */ - static void findAdjacentPolygons(std::vector& adjacent_poly_indices, const ConstPolygonRef& poly, const std::vector& possible_adjacent_polys, const coord_t max_gap); + static void findAdjacentPolygons( + std::vector& adjacent_poly_indices, + const ConstPolygonRef& poly, + const std::vector& possible_adjacent_polys, + const coord_t max_gap); /*! * Calculate the Hamming Distance between two polygons relative to their own @@ -619,10 +676,31 @@ class PolygonUtils */ static Polygons clipPolygonWithAABB(const Polygons& src, const AABB& aabb); + /*! + * Generate a few outset polygons around the given base, according to the given line width + * + * \param inner_poly The inner polygon to start generating the outset from + * \param count The number of outer polygons to add + * \param line_width The actual line width to distance the polygons from each other (and from the base) + * \return The generated outset polygons + */ + static Polygons generateOutset(const Polygons& inner_poly, size_t count, coord_t line_width); + + /*! + * Generate inset polygons inside the given base, until there is no space left, according to the given line width + * + * \param outer_poly The outer polygon to start generating the inset from + * \param line_width The actual line width to distance the polygons from each other (and from the base) + * \param initial_inset The inset distance to be added to the first generated polygon + * \return The generated inset polygons + */ + static Polygons generateInset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset = 0); + private: /*! - * Helper function for PolygonUtils::moveInside2: moves a point \p from which was moved onto \p closest_polygon_point towards inside/outside when it's not already inside/outside by enough distance. - * + * Helper function for PolygonUtils::moveInside2: moves a point \p from which was moved onto \p closest_polygon_point towards inside/outside when it's not already + * inside/outside by enough distance. + * * \param closest_polygon_point The ClosestPolygonPoint we have to move inside * \param distance The distance by which to move the point. * \param from[in,out] The point to move. @@ -633,6 +711,6 @@ class PolygonUtils }; -}//namespace cura +} // namespace cura -#endif//POLYGON_OPTIMIZER_H +#endif // POLYGON_OPTIMIZER_H diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index a9c1461cf1..7374d836a7 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -111,37 +111,6 @@ void PrimeTower::generatePaths(const SliceDataStorage& storage) } } -PrimeTower::ExtrusionMoves PrimeTower::generatePaths_base(const Polygons& inset, size_t rings, coord_t line_width) -{ - ExtrusionMoves pattern; - - Polygons path = inset.offset(line_width / 2); - for (size_t ring = 0; ring < rings; ++ring) - { - pattern.polygons.add(path); - path = path.offset(line_width); - } - - return pattern; -} - -PrimeTower::ExtrusionMoves PrimeTower::generatePaths_inset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset) -{ - const Scene& scene = Application::getInstance().current_slice->scene; - const Settings& mesh_group_settings = scene.current_mesh_group->settings; - - ExtrusionMoves pattern; - - Polygons inset = outer_poly.offset(-(initial_inset + line_width / 2)); - while (! inset.empty()) - { - pattern.polygons.add(inset); - inset = inset.offset(-line_width); - } - - return pattern; -} - void PrimeTower::generatePaths_denseInfill() { const Scene& scene = Application::getInstance().current_slice->scene; @@ -164,7 +133,7 @@ void PrimeTower::generatePaths_denseInfill() const coord_t required_volume = MM3_2INT(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 = prime_moves[extruder_nr]; + Polygons& pattern = prime_moves[extruder_nr]; // Create the walls of the prime tower. unsigned int wall_nr = 0; @@ -172,7 +141,7 @@ void PrimeTower::generatePaths_denseInfill() { // Create a new polygon with an offset from the outer polygon. Polygons polygons = outer_poly.offset(-cumulative_inset - wall_nr * line_width - line_width / 2); - pattern.polygons.add(polygons); + pattern.add(polygons); current_volume += polygons.polygonLength() * line_width * layer_height * flow; if (polygons.empty()) // Don't continue. We won't ever reach the required volume because it doesn't fit. { @@ -195,7 +164,7 @@ void PrimeTower::generatePaths_denseInfill() extra_radius = line_width * extra_rings; outer_poly_base.push_back(outer_poly.offset(extra_radius)); - base_extra_moves[extruder_nr].push_back(generatePaths_base(outer_poly, extra_rings, line_width)); + base_extra_moves[extruder_nr].push_back(PolygonUtils::generateOutset(outer_poly, extra_rings, line_width)); } } @@ -204,8 +173,8 @@ void PrimeTower::generatePaths_denseInfill() // Only the most inside extruder needs to fill the inside of the prime tower if (extruder_nr == extruder_order.back()) { - ExtrusionMoves pattern = generatePaths_inset(outer_poly, line_width, cumulative_inset); - if (! pattern.polygons.empty() || ! pattern.lines.empty()) + Polygons pattern = PolygonUtils::generateInset(outer_poly, line_width, cumulative_inset); + if (! pattern.empty()) { base_extra_moves[extruder_nr].push_back(pattern); } @@ -280,19 +249,17 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext { // Actual prime pattern const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - const ExtrusionMoves& pattern = prime_moves[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); - gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + const Polygons& pattern = prime_moves[extruder_nr]; + gcode_layer.addPolygonsByOptimizer(pattern, config); } - const std::vector& pattern_extra_brim = base_extra_moves[extruder_nr]; + const std::vector& pattern_extra_brim = base_extra_moves[extruder_nr]; if (absolute_layer_number < pattern_extra_brim.size()) { // Extra rings for stronger base const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; - const ExtrusionMoves& pattern = pattern_extra_brim[absolute_layer_number]; - gcode_layer.addPolygonsByOptimizer(pattern.polygons, config); - gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines); + const Polygons& pattern = pattern_extra_brim[absolute_layer_number]; + gcode_layer.addPolygonsByOptimizer(pattern, config); } } diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 0a8f627d53..d1f4debe21 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -3,22 +3,22 @@ #include "utils/polygonUtils.h" -#include "infill.h" -#include "utils/SparsePointGridInclusive.h" -#include "utils/linearAlg2D.h" - -#include - #include #include #include #include +#include + +#include "infill.h" +#include "utils/SparsePointGridInclusive.h" +#include "utils/linearAlg2D.h" + #ifdef DEBUG +#include + #include "utils/AABB.h" #include "utils/SVG.h" - -#include #endif namespace cura @@ -1607,4 +1607,32 @@ Polygons PolygonUtils::clipPolygonWithAABB(const Polygons& src, const AABB& aabb return out; } +Polygons PolygonUtils::generateOutset(const Polygons& inner_poly, size_t count, coord_t line_width) +{ + Polygons outset; + + Polygons current_outset; + for (size_t index = 0; index < count; ++index) + { + current_outset = index == 0 ? inner_poly.offset(line_width / 2) : current_outset.offset(line_width); + outset.add(current_outset); + } + + return outset; +} + +Polygons PolygonUtils::generateInset(const Polygons& outer_poly, coord_t line_width, coord_t initial_inset) +{ + Polygons inset; + + Polygons current_inset = outer_poly.offset(-(initial_inset + line_width / 2)); + while (! current_inset.empty()) + { + inset.add(current_inset); + current_inset = current_inset.offset(-line_width); + } + + return inset; +} + } // namespace cura From 3a7f8c336f7317075870e85d0af0962be72b96ae Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 23 Oct 2023 11:26:39 +0200 Subject: [PATCH 633/656] Moved target machine name to end of header CURA-11158 --- src/gcodeExport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index caff200811..2ef08a0474 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -266,7 +266,6 @@ std::string GCodeExport::getFileHeader( break; default: prefix << ";FLAVOR:" << flavorToString(flavor) << new_line; - prefix << ";TARGET_MACHINE.NAME:" << transliterate(machine_name) << new_line; prefix << ";TIME:" << ((print_time) ? static_cast(*print_time) : 6666) << new_line; if (flavor == EGCodeFlavor::ULTIGCODE) { @@ -309,6 +308,7 @@ std::string GCodeExport::getFileHeader( 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 << ";TARGET_MACHINE.NAME:" << transliterate(machine_name) << new_line; } return prefix.str(); From 21ee7634cb7293337d3d1d0239d3b91a96863c3c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 23 Oct 2023 11:32:35 +0200 Subject: [PATCH 634/656] Update unit-tests accordingly CURA-11158 --- tests/GCodeExportTest.cpp | 66 ++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/tests/GCodeExportTest.cpp b/tests/GCodeExportTest.cpp index 134a48b503..3fb07c9be9 100644 --- a/tests/GCodeExportTest.cpp +++ b/tests/GCodeExportTest.cpp @@ -2,6 +2,7 @@ // CuraEngine is released under the terms of the AGPLv3 or higher #include "gcodeExport.h" // The unit under test. + #include "Application.h" // To set up a slice with settings. #include "RetractionConfig.h" // For extruder switch tests. #include "Slice.h" // To set up a slice with settings. @@ -9,6 +10,7 @@ #include "arcus/MockCommunication.h" // To prevent calls to any missing Communication class. #include "utils/Coord_t.h" #include "utils/Date.h" // To check the Griffin header. + #include // NOLINTBEGIN(*-magic-numbers) @@ -101,12 +103,13 @@ TEST_F(GCodeExportTest, CommentMultiLine) "You can honestly say\n" "You made on that day\n" "A Chilean chinchilla's chin chilly"); - EXPECT_EQ(std::string(";If you catch a chinchilla in Chile\n" - ";And cut off its beard, willy-nilly\n" - ";You can honestly say\n" - ";You made on that day\n" - ";A Chilean chinchilla's chin chilly\n"), - output.str()) + EXPECT_EQ( + std::string(";If you catch a chinchilla in Chile\n" + ";And cut off its beard, willy-nilly\n" + ";You can honestly say\n" + ";You made on that day\n" + ";A Chilean chinchilla's chin chilly\n"), + output.str()) << "Each line must be preceded by a semicolon."; } @@ -115,10 +118,11 @@ TEST_F(GCodeExportTest, CommentMultiple) gcode.writeComment("Thunderbolt and lightning"); gcode.writeComment("Very very frightening me"); gcode.writeComment(" - Galileo (1638)"); - EXPECT_EQ(std::string(";Thunderbolt and lightning\n" - ";Very very frightening me\n" - "; - Galileo (1638)\n"), - output.str()) + EXPECT_EQ( + std::string(";Thunderbolt and lightning\n" + ";Very very frightening me\n" + "; - Galileo (1638)\n"), + output.str()) << "Semicolon before each line, and newline in between."; } @@ -328,9 +332,10 @@ TEST_F(GCodeExportTest, HeaderUltiGCode) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); - EXPECT_EQ(result, - ";FLAVOR:UltiGCode\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;MATERIAL:100\n;MATERIAL2:200\n;NOZZLE_DIAMETER:0.4\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;" - "MAXY:1\n;MAXZ:1\n"); + EXPECT_EQ( + result, + ";FLAVOR:UltiGCode\n;TIME:1337\n;MATERIAL:100\n;MATERIAL2:200\n;NOZZLE_DIAMETER:0.4\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;" + "MAXY:1\n;MAXZ:1\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n"); } TEST_F(GCodeExportTest, HeaderRepRap) @@ -347,9 +352,10 @@ TEST_F(GCodeExportTest, HeaderRepRap) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); - EXPECT_EQ(result, - ";FLAVOR:RepRap\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " - "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n"); + EXPECT_EQ( + result, + ";FLAVOR:RepRap\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " + "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n"); } TEST_F(GCodeExportTest, HeaderMarlin) @@ -366,9 +372,10 @@ TEST_F(GCodeExportTest, HeaderMarlin) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); - EXPECT_EQ(result, - ";FLAVOR:Marlin\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " - "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n"); + EXPECT_EQ( + result, + ";FLAVOR:Marlin\n;TIME:1337\n;Filament used: 0.02m, 0.05m\n;Layer height: " + "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n"); } TEST_F(GCodeExportTest, HeaderMarlinVolumetric) @@ -383,9 +390,10 @@ TEST_F(GCodeExportTest, HeaderMarlinVolumetric) std::string result = gcode.getFileHeader(extruder_is_used, &print_time, filament_used); - EXPECT_EQ(result, - ";FLAVOR:Marlin(Volumetric)\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n;TIME:1337\n;Filament used: 100mm3, 200mm3\n;Layer height: " - "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n"); + EXPECT_EQ( + result, + ";FLAVOR:Marlin(Volumetric)\n;TIME:1337\n;Filament used: 100mm3, 200mm3\n;Layer height: " + "0.123\n;MINX:0\n;MINY:0\n;MINZ:0\n;MAXX:1\n;MAXY:1\n;MAXZ:1\n;TARGET_MACHINE.NAME:Your favourite 3D printer\n"); } /* @@ -405,8 +413,9 @@ TEST_F(GCodeExportTest, EVsMmVolumetric) "area of the filament to convert the volume to a length."; constexpr double mm_input = 33.0; - EXPECT_EQ(gcode.mmToE(mm_input), mm_input * filament_area) << "Since the input mm is linear but the E output must be volumetric, we need to multiply by the cross-sectional area to convert " - "length to volume."; + EXPECT_EQ(gcode.mmToE(mm_input), mm_input * filament_area) + << "Since the input mm is linear but the E output must be volumetric, we need to multiply by the cross-sectional area to convert " + "length to volume."; constexpr double e_input = 100.0; EXPECT_EQ(gcode.eToMm3(e_input, 0), e_input) << "Since the E is volumetric and mm3 is also volumetric, the output needs to be the same."; @@ -431,8 +440,9 @@ TEST_F(GCodeExportTest, EVsMmLinear) } constexpr double mm3_input = 33.0; - EXPECT_EQ(gcode.mm3ToE(mm3_input), mm3_input / filament_area) << "Since the input mm3 is volumetric but the E output must be linear, we need to divide by the cross-sectional area to convert " - "volume to length."; + EXPECT_EQ(gcode.mm3ToE(mm3_input), mm3_input / filament_area) + << "Since the input mm3 is volumetric but the E output must be linear, we need to divide by the cross-sectional area to convert " + "volume to length."; constexpr double e_input = 100.0; EXPECT_EQ(gcode.eToMm3(e_input, 0), e_input * filament_area) << "Since the input E is linear but the output must be volumetric, we " @@ -492,7 +502,7 @@ TEST_F(GCodeExportTest, WriteZHopStartCustomSpeed) 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; - constexpr Velocity speed { 4.0 }; // 240 mm/min. + constexpr Velocity speed{ 4.0 }; // 240 mm/min. gcode.writeZhopStart(hop_height, speed); EXPECT_EQ(std::string("G1 F240 Z5\n"), output.str()) << "Custom provided speed should be used."; } @@ -520,7 +530,7 @@ TEST_F(GCodeExportTest, WriteZHopEndCustomSpeed) 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; - constexpr Velocity speed { 4.0 }; // 240 mm/min. + constexpr Velocity speed{ 4.0 }; // 240 mm/min. gcode.writeZhopEnd(speed); EXPECT_EQ(std::string("G1 F240 Z2\n"), output.str()) << "Custom provided speed should be used."; } From 7bc776566b8272796e04d5a363d8c5f4d75950b7 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 23 Oct 2023 18:12:59 +0200 Subject: [PATCH 635/656] Extruder is explicitely set for evry part of raft. CURA-11205 --- src/FffGcodeWriter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 777bf12978..ec0e28c326 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -725,6 +725,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) gcode_layer.setIsInside(true); current_extruder_nr = interface_extruder_nr; + gcode_layer.setExtruder(current_extruder_nr); Application::getInstance().communication->sendLayerComplete(layer_nr, z, interface_layer_height); @@ -829,7 +830,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) // make sure that we are using the correct extruder to print raft current_extruder_nr = surface_extruder_nr; - + gcode_layer.setExtruder(current_extruder_nr); Application::getInstance().communication->sendLayerComplete(layer_nr, z, surface_layer_height); std::vector raft_outline_paths; From f11ed7c34c52cea64c804cada44b4d8533ed2caf Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 24 Oct 2023 16:23:07 +0200 Subject: [PATCH 636/656] Base curve magnitude is now a float CURA-10783 --- src/PrimeTower.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 7374d836a7..3883822adc 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -120,7 +120,7 @@ void PrimeTower::generatePaths_denseInfill() const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; const coord_t base_height = std::max(scene.settings.get("prime_tower_base_height"), has_raft ? layer_height : 0); - const int base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); + const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); const coord_t line_width = scene.extruders[extruder_order.front()].settings.get("prime_tower_line_width"); prime_moves.resize(extruder_count); From e78f42c19bb46e314432f3e074dbdfa0e7f03976 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 24 Oct 2023 22:14:11 +0200 Subject: [PATCH 637/656] Fix slice in fractional support The code was attempting to access an index that was out of bounds of the array. Add guards to prevent this access. CURA-11041 --- src/support.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support.cpp b/src/support.cpp index a0b049fe04..09a8fb1de3 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1809,7 +1809,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh Polygons roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); support_layers[layer_idx].support_roof.add(roofs); - if (layer_idx > 0) + if (layer_idx > 0 && layer_idx < support_layers.size() - 1) { support_layers[layer_idx].support_fractional_roof.add(roofs.difference(support_layers[layer_idx + 1].support_roof)); } From 131cae1f4a49d0ec4086bbae90659492b62be671 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 24 Oct 2023 20:14:53 +0000 Subject: [PATCH 638/656] Applied clang-format. --- src/sliceDataStorage.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 61c6ff1689..b178075a26 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -705,10 +705,17 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(const Polygons& exclude_po } } -void SupportLayer::fillInfillParts(const LayerIndex layer_nr, const std::vector& support_fill_per_layer, const coord_t support_line_width, const coord_t wall_line_count, const coord_t grow_layer_above /*has default 0*/, const bool unionAll /*has default false*/) +void SupportLayer::fillInfillParts( + const LayerIndex layer_nr, + const std::vector& support_fill_per_layer, + const coord_t support_line_width, + const coord_t wall_line_count, + const coord_t grow_layer_above /*has default 0*/, + const bool unionAll /*has default false*/) { const Polygons& support_this_layer = support_fill_per_layer[layer_nr]; - const Polygons& support_layer_above = (layer_nr + 1) >= support_fill_per_layer.size() || layer_nr <= 0 ? Polygons() : support_fill_per_layer[layer_nr + 1].offset(grow_layer_above); + const Polygons& support_layer_above + = (layer_nr + 1) >= support_fill_per_layer.size() || layer_nr <= 0 ? Polygons() : support_fill_per_layer[layer_nr + 1].offset(grow_layer_above); const auto all_support_areas_in_layer = { support_this_layer.difference(support_layer_above), support_this_layer.intersection(support_layer_above) }; bool use_fractional_config = true; for (auto& support_areas : all_support_areas_in_layer) From 3493ae63097e8fe9a0f4664a2df07818633c704a Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 25 Oct 2023 10:13:21 +0200 Subject: [PATCH 639/656] Set version to 5.5.0 Contributes to CURA-11218 --- conanfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 383f5631f9..7dc2e8b0c8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-beta.2" + self.version = "5.5.0" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/0.1.0") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 1609f0e97096d2877d299ce040c33e317b3e5fe7 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 25 Oct 2023 15:35:36 +0200 Subject: [PATCH 640/656] Fix possible crash --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 70a6152eb7..254c40d06a 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2079,7 +2079,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // Prevent the final travel(s) from resetting to the 'previous' layer height. gcode.setZ(final_travel_z); } - for (unsigned int point_idx = 0; point_idx < path.points.size() - 1; point_idx++) + for (size_t point_idx = 0; point_idx + 1 < path.points.size(); point_idx++) { gcode.writeTravel(path.points[point_idx], speed); } From 98fc849b147d9603e3310a7511168dcc6dc9f176 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 25 Oct 2023 22:55:51 +0200 Subject: [PATCH 641/656] (From code review.) Small refactors and add documentation. done as part of CURA-10407 --- include/GCodePathConfig.h | 2 +- include/LayerPlan.h | 7 ++++--- include/SupportInfillPart.h | 2 +- include/pathPlanning/GCodePath.h | 2 +- src/LayerPlan.cpp | 12 ++++++------ 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 1f0da38d60..4531b650aa 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -18,7 +18,7 @@ namespace cura */ struct GCodePathConfig { - coord_t z_offset{}; + coord_t z_offset{}; // wall_toolpaths; //!< Any walls go here, not in the areas, where they could be combined vertically (don't combine walls). Binned by inset_idx. coord_t custom_line_distance; - bool use_fractional_config; + bool use_fractional_config; //!< Request to use the configuration used to fill a partial layer height here, instead of the normal full layer height configuration. SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0); diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index ef59a8747d..639e67bb85 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -29,7 +29,7 @@ namespace cura */ struct GCodePath { - coord_t z_offset{}; + coord_t z_offset{}; // mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; SpaceFillType space_fill_type{}; //!< The type of space filling of which this path is a part diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 254c40d06a..0da4344f6a 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -38,8 +38,8 @@ constexpr int MINIMUM_SQUARED_LINE_LENGTH = MINIMUM_LINE_LENGTH * MINIMUM_LINE_L GCodePath* LayerPlan::getLatestPathWithConfig( const GCodePathConfig& config, - const coord_t z_offset, const SpaceFillType space_fill_type, + const coord_t z_offset, const Ratio flow, const Ratio width_factor, const bool spiralize, @@ -329,14 +329,14 @@ std::optional> LayerPlan::getFirstTravelDestinationState( return ret; } -GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract, const coord_t z_offset) +GCodePath& LayerPlan::addTravel(const Point& p, const bool force_retract, const coord_t z_offset) { const GCodePathConfig& travel_config = configs_storage.travel_config_per_extruder[getExtruder()]; const RetractionConfig& retraction_config = current_mesh ? current_mesh->retraction_wipe_config.retraction_config : storage.retraction_wipe_config_per_extruder[getExtruder()].retraction_config; - GCodePath* path = getLatestPathWithConfig(travel_config, z_offset, SpaceFillType::None); + GCodePath* path = getLatestPathWithConfig(travel_config, SpaceFillType::None, z_offset); bool combed = false; @@ -481,7 +481,7 @@ GCodePath& LayerPlan::addTravel(const Point p, const bool force_retract, const c return ret; } -GCodePath& LayerPlan::addTravel_simple(const Point p, GCodePath* path) +GCodePath& LayerPlan::addTravel_simple(const Point& p, GCodePath* path) { bool is_first_travel_of_layer = ! static_cast(last_planned_position); if (is_first_travel_of_layer) @@ -491,7 +491,7 @@ GCodePath& LayerPlan::addTravel_simple(const Point p, GCodePath* path) } if (path == nullptr) { - path = getLatestPathWithConfig(configs_storage.travel_config_per_extruder[getExtruder()], 0, SpaceFillType::None); + path = getLatestPathWithConfig(configs_storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None); } path->points.push_back(p); last_planned_position = p; @@ -518,7 +518,7 @@ void LayerPlan::addExtrusionMove( const Ratio speed_factor, const double fan_speed) { - GCodePath* path = getLatestPathWithConfig(config, config.z_offset, space_fill_type, flow, width_factor, spiralize, speed_factor); + GCodePath* path = getLatestPathWithConfig(config, space_fill_type, config.z_offset, flow, width_factor, spiralize, speed_factor); path->points.push_back(p); path->setFanSpeed(fan_speed); if (! static_cast(first_extrusion_acc_jerk)) From 78fcc88b5518e42750e8fcb0567d3fdf37f4ae7d Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 25 Oct 2023 23:21:40 +0200 Subject: [PATCH 642/656] Correct for support-gap equal to exact layer-height multiple. part of CURA-10407 --- src/FffGcodeWriter.cpp | 4 ++-- src/support.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9ea700bb36..58424f79fb 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2289,7 +2289,7 @@ bool FffGcodeWriter::processInsets( if (mesh_group_settings.get("support_enable")) { const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Previously '... +1', but now there is an extra fractional layer on top. + const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + (z_distance_top % layer_height == 0) ? 1 : 0; const int support_layer_nr = gcode_layer.getLayerNr() - z_distance_top_layers; if (support_layer_nr > 0) @@ -2693,7 +2693,7 @@ void FffGcodeWriter::processTopBottom( { const coord_t layer_height = mesh_config.inset0_config.getLayerThickness(); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Previously '... +1', but now there is an extra fractional layer on top. + const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + (z_distance_top % layer_height == 0) ? 1 : 0; support_layer_nr = layer_nr - z_distance_top_layers; } diff --git a/src/support.cpp b/src/support.cpp index 09a8fb1de3..4f57ce44ee 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -785,7 +785,7 @@ void AreaSupport::generateOverhangAreasForMesh(SliceDataStorage& storage, SliceM // Don't generate overhang areas if the Z distance is higher than the objects we're generating support for. const coord_t layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Previously '... +1', but now there is an extra fractional layer on top. + const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + (z_distance_top % layer_height == 0) ? 1 : 0; if (z_distance_top_layers + 1 > storage.print_layer_count) { return; @@ -1050,7 +1050,7 @@ void AreaSupport::generateSupportAreasForMesh( // early out const coord_t layer_thickness = mesh_group_settings.get("layer_height"); const coord_t z_distance_top = ((mesh.settings.get("support_roof_enable")) ? roof_settings : infill_settings).get("support_top_distance"); - const size_t layer_z_distance_top = round_up_divide(z_distance_top, layer_thickness); // Previously '... +1', but now there is an extra fractional layer on top. + const size_t layer_z_distance_top = round_up_divide(z_distance_top, layer_thickness) + (z_distance_top % layer_thickness == 0) ? 1 : 0; if (layer_z_distance_top + 1 > layer_count) { return; From 54edc02f5e7458738a7a712eee91cd4aa11d61ba Mon Sep 17 00:00:00 2001 From: rburema Date: Wed, 25 Oct 2023 21:22:30 +0000 Subject: [PATCH 643/656] Applied clang-format. --- include/FffGcodeWriter.h | 6 +++--- include/SupportInfillPart.h | 4 ++-- include/pathPlanning/GCodePath.h | 6 +++--- include/settings/PathConfigStorage.h | 4 ++-- src/LayerPlan.cpp | 18 ++++++++--------- src/TreeSupport.cpp | 28 +++++++++++++------------- src/TreeSupportTipGenerator.cpp | 22 ++++++++++---------- src/support.cpp | 30 ++++++++++++++-------------- 8 files changed, 59 insertions(+), 59 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index e46f2b471c..7e5908e136 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -4,6 +4,9 @@ #ifndef GCODE_WRITER_H #define GCODE_WRITER_H +#include +#include + #include "FanSpeedLayerTime.h" #include "LayerPlanBuffer.h" #include "gcodeExport.h" @@ -12,9 +15,6 @@ #include "utils/ExtrusionLine.h" //Processing variable-width paths. #include "utils/NoCopy.h" -#include -#include - namespace cura { diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 70de71aecc..1767847cb7 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -4,12 +4,12 @@ #ifndef SUPPORT_INFILL_PART_H #define SUPPORT_INFILL_PART_H +#include + #include "utils/AABB.h" #include "utils/ExtrusionLine.h" #include "utils/polygon.h" -#include - namespace cura { diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 639e67bb85..da4efd30aa 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -4,6 +4,9 @@ #ifndef PATH_PLANNING_G_CODE_PATH_H #define PATH_PLANNING_G_CODE_PATH_H +#include +#include + #include "GCodePathConfig.h" #include "SpaceFillType.h" #include "TimeMaterialEstimates.h" @@ -11,9 +14,6 @@ #include "sliceDataStorage.h" #include "utils/IntPoint.h" -#include -#include - namespace cura { diff --git a/include/settings/PathConfigStorage.h b/include/settings/PathConfigStorage.h index 4ba5bf2837..feafea23bf 100644 --- a/include/settings/PathConfigStorage.h +++ b/include/settings/PathConfigStorage.h @@ -4,14 +4,14 @@ #ifndef SETTINGS_PATH_CONFIGS_H #define SETTINGS_PATH_CONFIGS_H +#include + #include "GCodePathConfig.h" #include "pathPlanning/SpeedDerivatives.h" #include "settings/MeshPathConfigs.h" #include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" -#include - namespace cura { diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 0da4344f6a..52305a9521 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -3,6 +3,15 @@ #include "LayerPlan.h" +#include +#include +#include +#include + +#include +#include +#include + #include "Application.h" //To communicate layer view data. #include "ExtruderTrain.h" #include "PathOrderMonotonic.h" //Monotonic ordering of skin lines. @@ -20,15 +29,6 @@ #include "utils/polygonUtils.h" #include "utils/section_type.h" -#include -#include -#include - -#include -#include -#include -#include - namespace cura { diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 9ce458d7f7..bf876ec582 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -3,6 +3,20 @@ #include "TreeSupport.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + #include "Application.h" //To get settings. #include "TreeSupportTipGenerator.h" #include "TreeSupportUtils.h" @@ -18,20 +32,6 @@ #include "utils/polygonUtils.h" //For moveInside. #include "utils/section_type.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - namespace cura { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index d362052739..094a819544 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -3,6 +3,17 @@ #include "TreeSupportTipGenerator.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include + #include "Application.h" //To get settings. #include "TreeSupportUtils.h" #include "infill/SierpinskiFillProvider.h" @@ -13,17 +24,6 @@ #include "utils/math.h" //For round_up_divide and PI. #include "utils/polygonUtils.h" //For moveInside. -#include -#include -#include -#include -#include - -#include -#include -#include -#include - namespace cura { diff --git a/src/support.cpp b/src/support.cpp index 4f57ce44ee..1a641c208e 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -3,6 +3,21 @@ #include "support.h" +#include // sqrt, round +#include +#include // ifstream.good() +#include // pair + +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "Application.h" //To get settings. #include "BoostInterface.hpp" #include "ExtruderTrain.h" @@ -24,21 +39,6 @@ #include "utils/math.h" #include "utils/views/get.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // sqrt, round -#include -#include // ifstream.good() -#include // pair - namespace cura { From 4a09451702b7c130583719067da49b6981fbf3ef Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 26 Oct 2023 14:35:54 +0200 Subject: [PATCH 644/656] Really correct support z-gap for all heights now hopefully. part of CURA-10407 --- src/FffGcodeWriter.cpp | 4 ++-- src/support.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 58424f79fb..4605960d39 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2289,7 +2289,7 @@ bool FffGcodeWriter::processInsets( if (mesh_group_settings.get("support_enable")) { const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + (z_distance_top % layer_height == 0) ? 1 : 0; + const size_t z_distance_top_layers = (z_distance_top / layer_height) + 1; const int support_layer_nr = gcode_layer.getLayerNr() - z_distance_top_layers; if (support_layer_nr > 0) @@ -2693,7 +2693,7 @@ void FffGcodeWriter::processTopBottom( { const coord_t layer_height = mesh_config.inset0_config.getLayerThickness(); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + (z_distance_top % layer_height == 0) ? 1 : 0; + const size_t z_distance_top_layers = (z_distance_top / layer_height) + 1; support_layer_nr = layer_nr - z_distance_top_layers; } diff --git a/src/support.cpp b/src/support.cpp index 1a641c208e..6bae947fa2 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -785,7 +785,7 @@ void AreaSupport::generateOverhangAreasForMesh(SliceDataStorage& storage, SliceM // Don't generate overhang areas if the Z distance is higher than the objects we're generating support for. const coord_t layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); - const size_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height) + (z_distance_top % layer_height == 0) ? 1 : 0; + const size_t z_distance_top_layers = (z_distance_top / layer_height) + 1; if (z_distance_top_layers + 1 > storage.print_layer_count) { return; @@ -1050,7 +1050,7 @@ void AreaSupport::generateSupportAreasForMesh( // early out const coord_t layer_thickness = mesh_group_settings.get("layer_height"); const coord_t z_distance_top = ((mesh.settings.get("support_roof_enable")) ? roof_settings : infill_settings).get("support_top_distance"); - const size_t layer_z_distance_top = round_up_divide(z_distance_top, layer_thickness) + (z_distance_top % layer_thickness == 0) ? 1 : 0; + const size_t layer_z_distance_top = (z_distance_top / layer_thickness) + 1; if (layer_z_distance_top + 1 > layer_count) { return; From c35eebd528636b5e5d2214afd5868a18560d2186 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 27 Oct 2023 12:25:45 +0200 Subject: [PATCH 645/656] Handle specific prime tower raft line spacing CURA-11233 --- src/FffGcodeWriter.cpp | 129 ++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index d35b38d169..44c98715dd 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -611,6 +611,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const size_t wall_line_count = base_settings.get("raft_base_wall_count"); const coord_t small_area_width = 0; // A raft never has a small region due to the large horizontal expansion. const coord_t line_spacing = base_settings.get("raft_base_line_spacing"); + const coord_t line_spacing_prime_tower = base_settings.get("prime_tower_raft_base_line_spacing"); const Point& infill_origin = Point(); constexpr bool skip_stitching = false; constexpr bool connected_zigzags = false; @@ -621,66 +622,86 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const coord_t max_resolution = base_settings.get("meshfix_maximum_resolution"); const coord_t max_deviation = base_settings.get("meshfix_maximum_deviation"); - Polygons raft_outline_path = storage.raftOutline; + struct ParameterizedRaftPath + { + coord_t line_spacing; + Polygons outline; + }; + + std::vector raft_outline_paths; + raft_outline_paths.emplace_back(ParameterizedRaftPath{ line_spacing, storage.raftOutline }); if (storage.primeTower.enabled) { - // Base layer is shared with prime tower base - raft_outline_path = raft_outline_path.unionPolygons(storage.primeTower.getOuterPoly(layer_nr)); + const Polygons& raft_outline_prime_tower = storage.primeTower.getOuterPoly(layer_nr); + if (line_spacing_prime_tower == line_spacing) + { + // Base layer is shared with prime tower base + raft_outline_paths.front().outline = raft_outline_paths.front().outline.unionPolygons(raft_outline_prime_tower); + } + else + { + // Prime tower has a different line spacing, print them separately + raft_outline_paths.front().outline = raft_outline_paths.front().outline.difference(raft_outline_prime_tower); + raft_outline_paths.emplace_back(ParameterizedRaftPath{ line_spacing_prime_tower, raft_outline_prime_tower }); + } } - Infill infill_comp( - EFillMethod::LINES, - zig_zaggify_infill, - connect_polygons, - raft_outline_path, - gcode_layer.configs_storage.raft_base_config.getLineWidth(), - line_spacing, - fill_overlap, - infill_multiplier, - fill_angle, - z, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - std::vector raft_paths; - infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); - if (! raft_paths.empty()) + for (const ParameterizedRaftPath& raft_outline_path : raft_outline_paths) { - const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); - InsetOrderOptimizer wall_orderer( - *this, - storage, - gcode_layer, - base_settings, - base_extruder_nr, - config, - config, - config, - config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - base_extruder_nr, - base_extruder_nr, - z_seam_config, - raft_paths); - wall_orderer.addToLayer(); - } - gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); + Infill infill_comp( + EFillMethod::LINES, + zig_zaggify_infill, + connect_polygons, + raft_outline_path.outline, + gcode_layer.configs_storage.raft_base_config.getLineWidth(), + raft_outline_path.line_spacing, + fill_overlap, + infill_multiplier, + fill_angle, + z, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + std::vector raft_paths; + infill_comp.generate(raft_paths, raft_polygons, raftLines, base_settings, layer_nr, SectionType::ADHESION); + if (! raft_paths.empty()) + { + const GCodePathConfig& config = gcode_layer.configs_storage.raft_base_config; + const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + base_settings, + base_extruder_nr, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + base_extruder_nr, + base_extruder_nr, + z_seam_config, + raft_paths); + wall_orderer.addToLayer(); + } + gcode_layer.addLinesByOptimizer(raftLines, gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines); - raft_polygons.clear(); - raftLines.clear(); + raft_polygons.clear(); + raftLines.clear(); + } layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); From ba8d5c2eeecbf84ab0ef86b24eb938c1b5f6973c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 27 Oct 2023 15:25:27 +0200 Subject: [PATCH 646/656] Better management of extruder change during raft printing CURA-10783 --- src/FffGcodeWriter.cpp | 48 ++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index d35b38d169..3a39aed4a1 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -556,6 +556,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const size_t base_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr; const size_t interface_extruder_nr = mesh_group_settings.get("raft_interface_extruder_nr").extruder_nr; const size_t surface_extruder_nr = mesh_group_settings.get("raft_surface_extruder_nr").extruder_nr; + const size_t prime_tower_extruder_nr = storage.primeTower.extruder_order.front(); coord_t z = 0; const LayerIndex initial_raft_layer_nr = -Raft::getTotalExtraLayers(); @@ -597,8 +598,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) = *new LayerPlan(storage, layer_nr, z, layer_height, base_extruder_nr, fan_speed_layer_time_settings_per_extruder_raft_base, comb_offset, line_width, avoid_distance); gcode_layer.setIsInside(true); - gcode_layer.setExtruder(base_extruder_nr); - Application::getInstance().communication->sendLayerComplete(layer_nr, z, layer_height); Polygons raftLines; @@ -696,6 +695,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (LayerIndex raft_interface_layer = 1; static_cast(raft_interface_layer) <= num_interface_layers; ++raft_interface_layer) { // raft interface layer + bool prime_tower_added_on_this_layer = ! storage.primeTower.enabled; const LayerIndex layer_nr = initial_raft_layer_nr + raft_interface_layer; z += interface_layer_height; @@ -719,10 +719,19 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) comb_offset, interface_line_width, interface_avoid_distance); - gcode_layer.setIsInside(true); - current_extruder_nr = interface_extruder_nr; - gcode_layer.setExtruder(current_extruder_nr); + if (! prime_tower_added_on_this_layer && current_extruder_nr == prime_tower_extruder_nr) + { + addPrimeTower(storage, gcode_layer, current_extruder_nr); + prime_tower_added_on_this_layer = true; + } + + gcode_layer.setIsInside(true); + if (interface_extruder_nr != current_extruder_nr) + { + setExtruder_addPrime(storage, gcode_layer, interface_extruder_nr); + current_extruder_nr = interface_extruder_nr; + } Application::getInstance().communication->sendLayerComplete(layer_nr, z, interface_layer_height); @@ -784,7 +793,11 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_polygons.clear(); raft_lines.clear(); - setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); + if (! prime_tower_added_on_this_layer) + { + setExtruder_addPrime(storage, gcode_layer, prime_tower_extruder_nr); + current_extruder_nr = prime_tower_extruder_nr; + } layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); @@ -800,6 +813,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (LayerIndex raft_surface_layer = 1; static_cast(raft_surface_layer) <= num_surface_layers; raft_surface_layer++) { // raft surface layers + bool prime_tower_added_on_this_layer = ! storage.primeTower.enabled; const LayerIndex layer_nr = initial_raft_layer_nr + 1 + num_interface_layers + raft_surface_layer - 1; // +1: 1 base layer z += surface_layer_height; @@ -823,11 +837,21 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) comb_offset, surface_line_width, surface_avoid_distance); + + if (! prime_tower_added_on_this_layer && current_extruder_nr == prime_tower_extruder_nr) + { + addPrimeTower(storage, gcode_layer, current_extruder_nr); + prime_tower_added_on_this_layer = true; + } + gcode_layer.setIsInside(true); // make sure that we are using the correct extruder to print raft - current_extruder_nr = surface_extruder_nr; - gcode_layer.setExtruder(current_extruder_nr); + if (current_extruder_nr != surface_extruder_nr) + { + setExtruder_addPrime(storage, gcode_layer, surface_extruder_nr); + current_extruder_nr = surface_extruder_nr; + } Application::getInstance().communication->sendLayerComplete(layer_nr, z, surface_layer_height); Polygons raft_outline_path; @@ -889,7 +913,11 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_polygons.clear(); raft_lines.clear(); - setExtruder_addPrime(storage, gcode_layer, storage.primeTower.extruder_order.front()); + if (! prime_tower_added_on_this_layer) + { + setExtruder_addPrime(storage, gcode_layer, prime_tower_extruder_nr); + current_extruder_nr = prime_tower_extruder_nr; + } layer_plan_buffer.handle(gcode_layer, gcode); } @@ -3620,8 +3648,6 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, LayerPlan& gcode_layer, const size_t extruder_nr) const { - const size_t outermost_prime_tower_extruder = storage.primeTower.extruder_order[0]; - const size_t previous_extruder = gcode_layer.getExtruder(); const bool extruder_changed = gcode_layer.setExtruder(extruder_nr); From ea0436eef5093967832cbaec052a19c30cac132c Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 31 Oct 2023 20:24:27 +0100 Subject: [PATCH 647/656] Update conanfile.py --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index b498c3b84d..b3e464843a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.6.0-alpha" + self.version = "5.6.0-beta.1" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) From 13a059ec1b33d7a920a3d41c477659b413511f92 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 2 Nov 2023 16:49:22 +0100 Subject: [PATCH 648/656] Detailed timing logging for layer export CURA-11255 --- include/FffGcodeWriter.h | 11 +++++-- include/LayerPlanBuffer.h | 9 +++--- include/progress/Progress.h | 46 ++++++++++++++------------ include/utils/gettime.h | 61 ++++++++++++++--------------------- src/FffGcodeWriter.cpp | 42 ++++++++++++++++-------- src/MeshGroup.cpp | 15 ++++++--- src/Scene.cpp | 13 +++++--- src/progress/Progress.cpp | 64 ++++++++++++++++++++++--------------- src/slicer.cpp | 18 +++++------ src/utils/gettime.cpp | 24 ++++++++++---- 10 files changed, 175 insertions(+), 128 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 7e5908e136..d63547692e 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -14,6 +14,7 @@ #include "settings/PathConfigStorage.h" //For the MeshPathConfigs subclass. #include "utils/ExtrusionLine.h" //Processing variable-width paths. #include "utils/NoCopy.h" +#include "utils/gettime.h" namespace cura { @@ -25,7 +26,6 @@ class SliceDataStorage; class SliceMeshStorage; class SliceLayer; class SliceLayerPart; -class TimeKeeper; /*! * Secondary stage in Fused Filament Fabrication processing: The generated polygons are used in the gcode generation. @@ -139,6 +139,13 @@ class FffGcodeWriter : public NoCopy void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper); private: + struct ProcessLayerResult + { + LayerPlan* layer_plan; + double total_elapsed_time; + TimeKeeper::RegisteredTimes stages_times; + }; + /*! * \brief Set the FffGcodeWriter::fan_speed_layer_time_settings by * retrieving all settings from the global/per-meshgroup settings. @@ -210,7 +217,7 @@ class FffGcodeWriter : public NoCopy * \param total_layers The total number of layers. * \return The layer plans */ - LayerPlan& processLayer(const SliceDataStorage& storage, LayerIndex layer_nr, const size_t total_layers) const; + ProcessLayerResult processLayer(const SliceDataStorage& storage, LayerIndex layer_nr, const size_t total_layers) const; /*! * This function checks whether prime blob should happen for any extruder on the first layer. diff --git a/include/LayerPlanBuffer.h b/include/LayerPlanBuffer.h index 8898137b87..2b1ce8cefc 100644 --- a/include/LayerPlanBuffer.h +++ b/include/LayerPlanBuffer.h @@ -4,6 +4,9 @@ #ifndef LAYER_PLAN_BUFFER_H #define LAYER_PLAN_BUFFER_H +#include +#include + #include "ExtruderPlan.h" #include "LayerPlan.h" #include "Preheat.h" @@ -11,9 +14,6 @@ #include "settings/Settings.h" #include "settings/types/Duration.h" -#include -#include - namespace cura { @@ -36,7 +36,6 @@ class GCodeExport; class LayerPlanBuffer { friend class LayerPlan; - friend class LayerPlanBuffer; GCodeExport& gcode; Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train. @@ -215,4 +214,4 @@ class LayerPlanBuffer } // namespace cura -#endif // LAYER_PLAN_BUFFER_H \ No newline at end of file +#endif // LAYER_PLAN_BUFFER_H diff --git a/include/progress/Progress.h b/include/progress/Progress.h index 6b9c83d532..4a513d94a2 100644 --- a/include/progress/Progress.h +++ b/include/progress/Progress.h @@ -1,58 +1,62 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef PROGRESS_H #define PROGRESS_H #include +#include "utils/gettime.h" + namespace cura { -class TimeKeeper; +struct LayerIndex; #define N_PROGRESS_STAGES 7 /*! * Class for handling the progress bar and the progress logging. - * + * * The progress bar is based on a single slicing of a rather large model which needs some complex support; * the relative timing of each stage is currently based on that of the slicing of dragon_65_tilted_large.stl */ -class Progress +class Progress { public: /*! - * The stage in the whole slicing process + * The stage in the whole slicing process */ enum class Stage : unsigned int { - START = 0, - SLICING = 1, - PARTS = 2, - INSET_SKIN = 3, - SUPPORT = 4, - EXPORT = 5, - FINISH = 6 + START = 0, + SLICING = 1, + PARTS = 2, + INSET_SKIN = 3, + SUPPORT = 4, + EXPORT = 5, + FINISH = 6 }; + private: - static double times [N_PROGRESS_STAGES]; //!< Time estimates per stage + static double times[N_PROGRESS_STAGES]; //!< Time estimates per stage static std::string names[N_PROGRESS_STAGES]; //!< name of each stage - static double accumulated_times [N_PROGRESS_STAGES]; //!< Time past before each stage + static double accumulated_times[N_PROGRESS_STAGES]; //!< Time past before each stage static double total_timing; //!< An estimate of the total time /*! * Give an estimate between 0 and 1 of how far the process is. - * + * * \param stage The current stage of processing * \param stage_process How far we currently are in the \p stage * \return An estimate of the overall progress. */ static float calcOverallProgress(Stage stage, float stage_progress); + public: static void init(); //!< Initialize some values needed in a fast computation of the progress /*! * Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided). - * + * * \param stage The current stage of processing * \param progress_in_stage Any number giving the progress within the stage * \param progress_in_stage_max The maximal value of \p progress_in_stage @@ -60,13 +64,15 @@ class Progress static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max); /*! * Message the progress stage over the command socket. - * + * * \param stage The current stage * \param timeKeeper The stapwatch keeping track of the timings for each stage (optional) */ static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper); + + static void messageProgressLayer(LayerIndex layer_nr, size_t total_layers, double total_time, const TimeKeeper::RegisteredTimes& stages); }; -} // name space cura -#endif//PROGRESS_H +} // namespace cura +#endif // PROGRESS_H diff --git a/include/utils/gettime.h b/include/utils/gettime.h index 319f5d750c..0f011360ad 100644 --- a/include/utils/gettime.h +++ b/include/utils/gettime.h @@ -4,55 +4,42 @@ #ifndef GETTIME_H #define GETTIME_H -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN 1 -#include -#else -#ifdef USE_CPU_TIME -#include -#endif - -#include -#include -#include -#endif +#include +#include +#include + +#include namespace cura { -static inline double getTime() -{ -#ifdef _WIN32 - return double(GetTickCount()) / 1000.0; -#else // not __WIN32 -#if USE_CPU_TIME // Use cpu usage time if available, otherwise wall clock time - struct rusage usage; -#ifdef DEBUG - int ret = getrusage(RUSAGE_SELF, &usage); - assert(ret == 0); - ((void)ret); -#else - getrusage(RUSAGE_SELF, &usage); -#endif - double user_time = double(usage.ru_utime.tv_sec) + double(usage.ru_utime.tv_usec) / 1000000.0; - double sys_time = double(usage.ru_stime.tv_sec) + double(usage.ru_stime.tv_usec) / 1000000.0; - return user_time + sys_time; -#else // not USE_CPU_TIME - struct timeval tv; - gettimeofday(&tv, nullptr); - return double(tv.tv_sec) + double(tv.tv_usec) / 1000000.0; -#endif // USE_CPU_TIME -#endif // __WIN32 -} class TimeKeeper { +public: + struct RegisteredTime + { + std::string stage; + double duration; + }; + + using RegisteredTimes = std::vector; + private: - double startTime; + spdlog::stopwatch watch; + double start_time; + RegisteredTimes registered_times; public: TimeKeeper(); double restart(); + + void registerTime(const std::string& stage, double threshold = 0.01); + + const RegisteredTimes& getRegisteredTimes() const + { + return registered_times; + } }; } // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index b99161c1a2..a42d12889d 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -7,6 +7,7 @@ #include // numeric_limits #include #include +#include #include #include @@ -168,15 +169,15 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep total_layers, [&storage, total_layers, this](int layer_nr) { - return &processLayer(storage, layer_nr, total_layers); + return std::make_optional(processLayer(storage, layer_nr, total_layers)); }, - [this, total_layers](LayerPlan* gcode_layer) + [this, total_layers](std::optional result_opt) { - Progress::messageProgress(Progress::Stage::EXPORT, std::max(LayerIndex{ 0 }, gcode_layer->getLayerNr()) + 1, total_layers); - layer_plan_buffer.handle(*gcode_layer, gcode); + const ProcessLayerResult& result = result_opt.value(); + Progress::messageProgressLayer(result.layer_plan->getLayerNr(), total_layers, result.total_elapsed_time, result.stages_times); + layer_plan_buffer.handle(*result.layer_plan, gcode); }); - layer_plan_buffer.flush(); Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper); @@ -945,9 +946,11 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) } } -LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIndex layer_nr, const size_t total_layers) const +FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIndex layer_nr, const size_t total_layers) const { spdlog::debug("GcodeWriter processing layer {} of {}", layer_nr, total_layers); + TimeKeeper time_keeper; + spdlog::stopwatch timer_total; const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; coord_t layer_thickness = mesh_group_settings.get("layer_height"); @@ -1032,6 +1035,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn comb_offset_from_outlines, first_outer_wall_line_width, avoid_distance); + time_keeper.registerTime("Init"); if (include_helper_parts) { @@ -1040,11 +1044,15 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn if (storage.skirt_brim[extruder_nr].size() > 0) { processSkirtBrim(storage, gcode_layer, extruder_nr, layer_nr); + time_keeper.registerTime("Skirt/brim"); } // handle shield(s) first in a layer so that chances are higher that the other nozzle is wiped (for the ooze shield) processOozeShield(storage, gcode_layer); + time_keeper.registerTime("Ooze shield"); + processDraftShield(storage, gcode_layer); + time_keeper.registerTime("Draft shield"); } const size_t support_roof_extruder_nr = mesh_group_settings.get("support_roof_extruder_nr").extruder_nr; @@ -1065,10 +1073,12 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn if (extruder_nr != extruder_order.front() || (extruder_order.size() == 1 && layer_nr >= 0) || extruder_nr == 0) { setExtruder_addPrime(storage, gcode_layer, extruder_nr); + time_keeper.registerTime("Prime tower pre"); } if (include_helper_parts && (extruder_nr == support_infill_extruder_nr || extruder_nr == support_roof_extruder_nr || extruder_nr == support_bottom_extruder_nr)) { addSupportToGCode(storage, gcode_layer, extruder_nr); + time_keeper.registerTime("Supports"); } if (layer_nr >= 0) { @@ -1088,6 +1098,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn { addMeshLayerToGCode(storage, mesh, extruder_nr, mesh_config, gcode_layer); } + time_keeper.registerTime(fmt::format("Mesh {}", mesh_idx)); } } // Always print a prime tower before switching extruder. Unless: @@ -1096,12 +1107,17 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn if (extruder_nr != extruder_order.back() && layer_nr >= 0) { setExtruder_addPrime(storage, gcode_layer, extruder_nr); + time_keeper.registerTime("Prime tower post"); } } gcode_layer.applyModifyPlugin(); + time_keeper.registerTime("Modify plugin"); + gcode_layer.applyBackPressureCompensation(); - return gcode_layer; + time_keeper.registerTime("Back pressure comp."); + + return { &gcode_layer, timer_total.elapsed().count(), time_keeper.getRegisteredTimes() }; } bool FffGcodeWriter::getExtruderNeedPrimeBlobDuringFirstLayer(const SliceDataStorage& storage, const size_t extruder_nr) const @@ -2036,8 +2052,8 @@ bool FffGcodeWriter::processSingleLayerInfill( else // So walls_generated must be true. { std::vector* start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; - while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit it. - // Should almost always be very quick. + while (start_paths->empty() || (*start_paths)[0].empty()) // We know for sure (because walls_generated) that one of them is not empty. So randomise until we hit + // it. Should almost always be very quick. { start_paths = &wall_tool_paths[rand() % wall_tool_paths.size()]; } @@ -2169,8 +2185,8 @@ bool FffGcodeWriter::partitionInfillBySkinAbove( } else // this layer is the 1st layer above the layer whose infill we're printing { - // add this layer's skin region without subtracting the overlap but still make a gap between this skin region and what has been accumulated so far - // we do this so that these skin region edges will definitely have infill walls below them + // add this layer's skin region without subtracting the overlap but still make a gap between this skin region and what has been accumulated so + // far we do this so that these skin region edges will definitely have infill walls below them // looking from the side, if the combined regions so far look like this... // @@ -2201,8 +2217,8 @@ bool FffGcodeWriter::partitionInfillBySkinAbove( } } - // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form a - // bump + // the shrink/expand here is to remove regions of infill below skin that are narrower than the width of the infill walls otherwise the infill walls could merge and form + // a bump infill_below_skin = skin_above_combined.intersection(part.infill_area_per_combine_per_density.back().front()).offset(-infill_line_width).offset(infill_line_width); constexpr bool remove_small_holes_from_infill_below_skin = true; diff --git a/src/MeshGroup.cpp b/src/MeshGroup.cpp index 1516e64190..84ef4d4923 100644 --- a/src/MeshGroup.cpp +++ b/src/MeshGroup.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include "MeshGroup.h" + #include #include #include @@ -10,7 +12,6 @@ #include #include -#include "MeshGroup.h" #include "settings/types/Ratio.h" //For the shrinkage percentage and scale factor. #include "utils/FMatrix4x3.h" //To transform the input meshes for shrinkage compensation and to align in command line mode. #include "utils/floatpoint.h" //To accept incoming meshes with floating point vertices. @@ -48,7 +49,8 @@ Point3 MeshGroup::min() const Point3 ret(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); for (const Mesh& mesh : meshes) { - if (mesh.settings.get("infill_mesh") || mesh.settings.get("cutting_mesh") || mesh.settings.get("anti_overhang_mesh")) // Don't count pieces that are not printed. + if (mesh.settings.get("infill_mesh") || mesh.settings.get("cutting_mesh") + || mesh.settings.get("anti_overhang_mesh")) // Don't count pieces that are not printed. { continue; } @@ -69,7 +71,8 @@ Point3 MeshGroup::max() const Point3 ret(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); for (const Mesh& mesh : meshes) { - if (mesh.settings.get("infill_mesh") || mesh.settings.get("cutting_mesh") || mesh.settings.get("anti_overhang_mesh")) // Don't count pieces that are not printed. + if (mesh.settings.get("infill_mesh") || mesh.settings.get("cutting_mesh") + || mesh.settings.get("anti_overhang_mesh")) // Don't count pieces that are not printed. { continue; } @@ -112,7 +115,9 @@ void MeshGroup::finalize() } mesh.translate(mesh_offset + meshgroup_offset); } - scaleFromBottom(settings.get("material_shrinkage_percentage_xy"), settings.get("material_shrinkage_percentage_z")); // Compensate for the shrinkage of the material. + scaleFromBottom( + settings.get("material_shrinkage_percentage_xy"), + settings.get("material_shrinkage_percentage_z")); // Compensate for the shrinkage of the material. for (const auto& [idx, mesh] : meshes | ranges::views::enumerate) { scripta::log(fmt::format("mesh_{}", idx), mesh, SectionType::NA); @@ -285,7 +290,7 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMa if (loadMeshSTL(&mesh, filename, transformation)) // Load it! If successful... { meshgroup->meshes.push_back(mesh); - spdlog::info("loading '{}' took {:3} seconds", filename, load_timer.restart()); + spdlog::info("loading '{}' took {:03.3f} seconds", filename, load_timer.restart()); return true; } } diff --git a/src/Scene.cpp b/src/Scene.cpp index ff4baf9adc..ac50185b6d 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -1,11 +1,12 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include "Scene.h" + #include #include "Application.h" #include "FffProcessor.h" //To start a slice. -#include "Scene.h" #include "communication/Communication.h" //To flush g-code and layer view when we're done. #include "progress/Progress.h" #include "sliceDataStorage.h" @@ -13,7 +14,9 @@ namespace cura { -Scene::Scene(const size_t num_mesh_groups) : mesh_groups(num_mesh_groups), current_mesh_group(mesh_groups.begin()) +Scene::Scene(const size_t num_mesh_groups) + : mesh_groups(num_mesh_groups) + , current_mesh_group(mesh_groups.begin()) { for (MeshGroup& mesh_group : mesh_groups) { @@ -78,7 +81,7 @@ void Scene::processMeshGroup(MeshGroup& mesh_group) if (empty) { Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup - spdlog::info("Total time elapsed {:3}s.", time_keeper_total.restart()); + spdlog::info("Total time elapsed {:03.3f}s", time_keeper_total.restart()); return; } @@ -94,7 +97,7 @@ void Scene::processMeshGroup(MeshGroup& mesh_group) Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup Application::getInstance().communication->flushGCode(); Application::getInstance().communication->sendOptimizedLayerData(); - spdlog::info("Total time elapsed {:3}s.\n", time_keeper_total.restart()); + spdlog::info("Total time elapsed {:03.3f}s\n", time_keeper_total.restart()); } -} // namespace cura \ No newline at end of file +} // namespace cura diff --git a/src/progress/Progress.cpp b/src/progress/Progress.cpp index 62687044c6..fb3331bc02 100644 --- a/src/progress/Progress.cpp +++ b/src/progress/Progress.cpp @@ -1,49 +1,40 @@ // Copyright (c) 2022 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher +#include "progress/Progress.h" + #include #include #include "Application.h" //To get the communication channel to send progress through. #include "communication/Communication.h" //To send progress through the communication channel. -#include "progress/Progress.h" #include "utils/gettime.h" namespace cura { double Progress::times[] = { - 0.0, // START = 0, - 5.269, // SLICING = 1, - 1.533, // PARTS = 2, + 0.0, // START = 0, + 5.269, // SLICING = 1, + 1.533, // PARTS = 2, 71.811, // INSET_SKIN = 3 - 51.009, // SUPPORT = 4, - 154.62, // EXPORT = 5, - 0.1 // FINISH = 6 -}; -std::string Progress::names [] = -{ - "start", - "slice", - "layerparts", - "inset+skin", - "support", - "export", - "process" + 51.009, // SUPPORT = 4, + 154.62, // EXPORT = 5, + 0.1 // FINISH = 6 }; +std::string Progress::names[] = { "start", "slice", "layerparts", "inset+skin", "support", "export", "process" }; -double Progress::accumulated_times [N_PROGRESS_STAGES] = {-1}; +double Progress::accumulated_times[N_PROGRESS_STAGES] = { -1 }; double Progress::total_timing = -1; float Progress::calcOverallProgress(Stage stage, float stage_progress) { assert(stage_progress <= 1.0); assert(stage_progress >= 0.0); - return ( accumulated_times[(int)stage] + stage_progress * times[(int)stage] ) / total_timing; + return (accumulated_times[(int)stage] + stage_progress * times[(int)stage]) / total_timing; } - void Progress::init() { double accumulated_time = 0; @@ -59,8 +50,6 @@ void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int { float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max)); Application::getInstance().communication->sendProgress(percentage); - - // logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage); FIXME: use different sink } void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper) @@ -69,13 +58,13 @@ void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keep { if ((int)stage > 0) { - spdlog::info("Progress: {} accomplished in {:3}s", names[(int)stage - 1], time_keeper->restart()); + spdlog::info("Progress: {} accomplished in {:03.3f}s", names[(int)stage - 1], time_keeper->restart()); } else { time_keeper->restart(); } - + if ((int)stage < (int)Stage::FINISH) { spdlog::info("Starting {}...", names[(int)stage]); @@ -83,4 +72,29 @@ void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keep } } -}// namespace cura \ No newline at end of file +void Progress::messageProgressLayer(LayerIndex layer_nr, size_t total_layers, double total_time, const TimeKeeper::RegisteredTimes& stages) +{ + messageProgress(Stage::EXPORT, std::max(layer_nr.value, LayerIndex::value_type(0)) + 1, total_layers); + + spdlog::info("+---- Layer export [{}] accomplished in {:03.3f}s", layer_nr.value, total_time); + + size_t padding = 0; + auto iterator_max_size = std::max_element( + stages.begin(), + stages.end(), + [](const TimeKeeper::RegisteredTime& time1, const TimeKeeper::RegisteredTime& time2) + { + return time1.stage.size() < time2.stage.size(); + }); + if (iterator_max_size != stages.end()) + { + padding = iterator_max_size->stage.size(); + + for (const TimeKeeper::RegisteredTime& time : stages) + { + spdlog::info("| *{}:{} {:03.3f}s", time.stage, std::string(padding - time.stage.size(), ' '), time.duration); + } + } +} + +} // namespace cura diff --git a/src/slicer.cpp b/src/slicer.cpp index 6b1ac7ea4d..f5127833bc 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -3,6 +3,13 @@ #include "slicer.h" +#include // remove_if +#include +#include + +#include +#include + #include "Application.h" #include "Slice.h" #include "plugins/slots.h" @@ -15,13 +22,6 @@ #include "utils/gettime.h" #include "utils/section_type.h" -#include -#include - -#include // remove_if -#include -#include - namespace cura { @@ -825,11 +825,11 @@ Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_c buildSegments(*mesh, zbbox, slicing_tolerance, layers); - spdlog::info("Slice of mesh took {:3} seconds", slice_timer.restart()); + spdlog::info("Slice of mesh took {:03.3f} seconds", slice_timer.restart()); makePolygons(*i_mesh, slicing_tolerance, layers); scripta::log("sliced_polygons", layers, SectionType::NA); - spdlog::info("Make polygons took {:3} seconds", slice_timer.restart()); + spdlog::info("Make polygons took {:03.3f} seconds", slice_timer.restart()); } void Slicer::buildSegments(const Mesh& mesh, const std::vector>& zbbox, const SlicingTolerance& slicing_tolerance, std::vector& layers) diff --git a/src/utils/gettime.cpp b/src/utils/gettime.cpp index bfd665abaa..a7d8303857 100644 --- a/src/utils/gettime.cpp +++ b/src/utils/gettime.cpp @@ -1,21 +1,31 @@ -//Copyright (c) 2022 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "utils/gettime.h" +#include + namespace cura { - + TimeKeeper::TimeKeeper() { - restart(); } double TimeKeeper::restart() { - double ret = getTime() - startTime; - startTime = getTime(); + double ret = watch.elapsed().count(); + watch.reset(); return ret; } -}//namespace cura \ No newline at end of file +void TimeKeeper::registerTime(const std::string& stage, double threshold) +{ + double duration = restart(); + if (duration >= threshold) + { + registered_times.emplace_back(RegisteredTime{ stage, duration }); + } +} + +} // namespace cura From a58cb53efd3dd441c083218ad27ad073ccc36af2 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 2 Nov 2023 17:56:38 +0100 Subject: [PATCH 649/656] Allow scripta to construct layermap with raft See: https://github.com/Ultimaker/Scripta/pull/7 --- src/slicer.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/slicer.cpp b/src/slicer.cpp index 6b1ac7ea4d..62e8e3d57d 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -819,7 +819,15 @@ Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_c TimeKeeper slice_timer; layers = buildLayersWithHeight(slice_layer_count, slicing_tolerance, initial_layer_thickness, thickness, use_variable_layer_heights, adaptive_layers); - scripta::setAll(layers); + scripta::setAll(layers, + static_cast(mesh->settings.get("adhesion_type")), + mesh->settings.get("raft_surface_layers"), + mesh->settings.get("raft_surface_thickness"), + mesh->settings.get("raft_interface_layers"), + mesh->settings.get("raft_interface_thickness"), + mesh->settings.get("raft_base_thickness"), + mesh->settings.get("raft_airgap"), + mesh->settings.get("layer_0_z_overlap")); std::vector> zbbox = buildZHeightsForFaces(*mesh); From db79053f1166ab383a5beb71c8d9b41c64ccc36c Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Fri, 3 Nov 2023 11:00:24 +0000 Subject: [PATCH 650/656] Applied clang-format. --- src/slicer.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/slicer.cpp b/src/slicer.cpp index 485a1d5ed5..2758d133f0 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -819,15 +819,16 @@ Slicer::Slicer(Mesh* i_mesh, const coord_t thickness, const size_t slice_layer_c TimeKeeper slice_timer; layers = buildLayersWithHeight(slice_layer_count, slicing_tolerance, initial_layer_thickness, thickness, use_variable_layer_heights, adaptive_layers); - scripta::setAll(layers, - static_cast(mesh->settings.get("adhesion_type")), - mesh->settings.get("raft_surface_layers"), - mesh->settings.get("raft_surface_thickness"), - mesh->settings.get("raft_interface_layers"), - mesh->settings.get("raft_interface_thickness"), - mesh->settings.get("raft_base_thickness"), - mesh->settings.get("raft_airgap"), - mesh->settings.get("layer_0_z_overlap")); + scripta::setAll( + layers, + static_cast(mesh->settings.get("adhesion_type")), + mesh->settings.get("raft_surface_layers"), + mesh->settings.get("raft_surface_thickness"), + mesh->settings.get("raft_interface_layers"), + mesh->settings.get("raft_interface_thickness"), + mesh->settings.get("raft_base_thickness"), + mesh->settings.get("raft_airgap"), + mesh->settings.get("layer_0_z_overlap")); std::vector> zbbox = buildZHeightsForFaces(*mesh); From bca565bdfe97ebc874989f362c24bb3f879dc2cd Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 7 Nov 2023 10:54:47 +0100 Subject: [PATCH 651/656] Time report improvements CURA-11255 --- include/progress/Progress.h | 14 ++++++++++- src/progress/Progress.cpp | 50 +++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/include/progress/Progress.h b/include/progress/Progress.h index 4a513d94a2..619632a1f0 100644 --- a/include/progress/Progress.h +++ b/include/progress/Progress.h @@ -43,6 +43,7 @@ class Progress static std::string names[N_PROGRESS_STAGES]; //!< name of each stage static double accumulated_times[N_PROGRESS_STAGES]; //!< Time past before each stage static double total_timing; //!< An estimate of the total time + static std::optional first_skipped_layer; //!< The index of the layer for which we skipped time reporting /*! * Give an estimate between 0 and 1 of how far the process is. * @@ -62,6 +63,7 @@ class Progress * \param progress_in_stage_max The maximal value of \p progress_in_stage */ static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max); + /*! * Message the progress stage over the command socket. * @@ -70,7 +72,17 @@ class Progress */ static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper); - static void messageProgressLayer(LayerIndex layer_nr, size_t total_layers, double total_time, const TimeKeeper::RegisteredTimes& stages); + /*! + * Message the layer progress over the command socket and into logging output. + * + * \param layer_nr The processed layer number + * \param total_layers The total number of layers to be processed + * \param total_time The total layer processing time, in seconds + * \param stage The detailed stages time reporting for this layer + * \param skip_threshold The time threshold under which we consider that the full layer time reporting should be skipped + * because it is not relevant + */ + static void messageProgressLayer(LayerIndex layer_nr, size_t total_layers, double total_time, const TimeKeeper::RegisteredTimes& stages, double skip_threshold = 0.1); }; diff --git a/src/progress/Progress.cpp b/src/progress/Progress.cpp index fb3331bc02..85194808b0 100644 --- a/src/progress/Progress.cpp +++ b/src/progress/Progress.cpp @@ -5,6 +5,7 @@ #include +#include #include #include "Application.h" //To get the communication channel to send progress through. @@ -27,6 +28,7 @@ std::string Progress::names[] = { "start", "slice", "layerparts", "inset+skin", double Progress::accumulated_times[N_PROGRESS_STAGES] = { -1 }; double Progress::total_timing = -1; +std::optional Progress::first_skipped_layer{}; float Progress::calcOverallProgress(Stage stage, float stage_progress) { @@ -72,27 +74,43 @@ void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keep } } -void Progress::messageProgressLayer(LayerIndex layer_nr, size_t total_layers, double total_time, const TimeKeeper::RegisteredTimes& stages) +void Progress::messageProgressLayer(LayerIndex layer_nr, size_t total_layers, double total_time, const TimeKeeper::RegisteredTimes& stages, double skip_threshold) { - messageProgress(Stage::EXPORT, std::max(layer_nr.value, LayerIndex::value_type(0)) + 1, total_layers); - - spdlog::info("+---- Layer export [{}] accomplished in {:03.3f}s", layer_nr.value, total_time); - - size_t padding = 0; - auto iterator_max_size = std::max_element( - stages.begin(), - stages.end(), - [](const TimeKeeper::RegisteredTime& time1, const TimeKeeper::RegisteredTime& time2) + if (total_time < skip_threshold) + { + if (! first_skipped_layer) { - return time1.stage.size() < time2.stage.size(); - }); - if (iterator_max_size != stages.end()) + first_skipped_layer = layer_nr; + } + } + else { - padding = iterator_max_size->stage.size(); + if (first_skipped_layer) + { + spdlog::info("Skipped time reporting for layers [{}...{}]", first_skipped_layer.value().value, layer_nr.value); + first_skipped_layer.reset(); + } + + messageProgress(Stage::EXPORT, std::max(layer_nr.value, LayerIndex::value_type(0)) + 1, total_layers); - for (const TimeKeeper::RegisteredTime& time : stages) + spdlog::info("┌ Layer export [{}] accomplished in {:03.3f}s", layer_nr.value, total_time); + + size_t padding = 0; + auto iterator_max_size = std::max_element( + stages.begin(), + stages.end(), + [](const TimeKeeper::RegisteredTime& time1, const TimeKeeper::RegisteredTime& time2) + { + return time1.stage.size() < time2.stage.size(); + }); + if (iterator_max_size != stages.end()) { - spdlog::info("| *{}:{} {:03.3f}s", time.stage, std::string(padding - time.stage.size(), ' '), time.duration); + padding = iterator_max_size->stage.size(); + + for (auto [index, time] : stages | ranges::view::enumerate) + { + spdlog::info("{}── {}:{} {:03.3f}s", index < stages.size() - 1 ? "├" : "└", time.stage, std::string(padding - time.stage.size(), ' '), time.duration); + } } } } From c07a0a170867b3c90dcb88c6800927d1afcce041 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 7 Nov 2023 12:32:17 +0100 Subject: [PATCH 652/656] Fix unit test --- tests/integration/SlicePhaseTest.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index 34e46dc809..75d66899b8 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -1,6 +1,10 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#include + +#include + #include "Application.h" // To set up a slice with settings. #include "Slice.h" // To set up a scene to slice. #include "slicer.h" // Starts the slicing phase that we want to test. @@ -8,8 +12,6 @@ #include "utils/FMatrix4x3.h" // To load STL files. #include "utils/polygon.h" // Creating polygons to compare to sliced layers. #include "utils/polygonUtils.h" // Comparing similarity of polygons. -#include -#include namespace cura { @@ -35,6 +37,13 @@ class SlicePhaseTest : public testing::Test scene.settings.add("slicing_tolerance", "middle"); scene.settings.add("layer_height_0", "0.2"); scene.settings.add("layer_height", "0.1"); + scene.settings.add("layer_0_z_overlap", "0.0"); + scene.settings.add("raft_airgap", "0.0"); + scene.settings.add("raft_base_thickness", "0.2"); + scene.settings.add("raft_interface_thickness", "0.2"); + scene.settings.add("raft_interface_layers", "1"); + scene.settings.add("raft_surface_thickness", "0.2"); + scene.settings.add("raft_surface_layers", "1"); scene.settings.add("magic_mesh_surface_mode", "normal"); scene.settings.add("meshfix_extensive_stitching", "false"); scene.settings.add("meshfix_keep_open_polygons", "false"); @@ -51,6 +60,7 @@ class SlicePhaseTest : public testing::Test scene.settings.add("anti_overhang_mesh", "false"); scene.settings.add("cutting_mesh", "false"); scene.settings.add("infill_mesh", "false"); + scene.settings.add("adhesion_type", "none"); } }; @@ -121,7 +131,8 @@ TEST_F(SlicePhaseTest, Cylinder1000) const FMatrix4x3 transformation; // Path to cylinder1000.stl is relative to CMAKE_CURRENT_SOURCE_DIR/tests. - ASSERT_TRUE(loadMeshIntoMeshGroup(&mesh_group, std::filesystem::path(__FILE__).parent_path().append("resources/cylinder1000.stl").string().c_str(), transformation, scene.settings)); + ASSERT_TRUE( + loadMeshIntoMeshGroup(&mesh_group, std::filesystem::path(__FILE__).parent_path().append("resources/cylinder1000.stl").string().c_str(), transformation, scene.settings)); EXPECT_EQ(mesh_group.meshes.size(), 1); Mesh& cylinder_mesh = mesh_group.meshes[0]; @@ -162,4 +173,4 @@ TEST_F(SlicePhaseTest, Cylinder1000) } } -} // namespace cura \ No newline at end of file +} // namespace cura From 3cfce8350ba204f0229a85907ca575e1522089d6 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 8 Nov 2023 10:13:43 +0100 Subject: [PATCH 653/656] Add missing include `optional` --- src/progress/Progress.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/progress/Progress.cpp b/src/progress/Progress.cpp index 85194808b0..d0c3e9203a 100644 --- a/src/progress/Progress.cpp +++ b/src/progress/Progress.cpp @@ -1,9 +1,10 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include "progress/Progress.h" #include +#include #include #include From 138d6cdefcce91b4bc5fa8e379ec322b756c2eb9 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 8 Nov 2023 10:14:31 +0100 Subject: [PATCH 654/656] Use correct namespace for `ranges::views` --- src/progress/Progress.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/progress/Progress.cpp b/src/progress/Progress.cpp index d0c3e9203a..6af743785d 100644 --- a/src/progress/Progress.cpp +++ b/src/progress/Progress.cpp @@ -108,7 +108,7 @@ void Progress::messageProgressLayer(LayerIndex layer_nr, size_t total_layers, do { padding = iterator_max_size->stage.size(); - for (auto [index, time] : stages | ranges::view::enumerate) + for (auto [index, time] : stages | ranges::views::enumerate) { spdlog::info("{}── {}:{} {:03.3f}s", index < stages.size() - 1 ? "├" : "└", time.stage, std::string(padding - time.stage.size(), ' '), time.duration); } From eeb93cfaa3bd7466a4b519206cc3e51423dd13a6 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 8 Nov 2023 10:47:28 +0100 Subject: [PATCH 655/656] Refactor Progress class and improve type safety This refactor cleans up the Progress class and enhances type safety. It replaces raw C-style arrays with std::array, which guarantees type safety and prevents out-of-bound access. The type of several variables and methods has also been changed from int and float to size_t and double to increase precision. --- include/progress/Progress.h | 26 +++++++++++++++++------- src/progress/Progress.cpp | 40 +++++++++++++------------------------ 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/include/progress/Progress.h b/include/progress/Progress.h index 619632a1f0..ae829867bb 100644 --- a/include/progress/Progress.h +++ b/include/progress/Progress.h @@ -1,10 +1,13 @@ -// Copyright (c) 2018 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2023 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef PROGRESS_H #define PROGRESS_H +#include #include +#include +#include #include "utils/gettime.h" @@ -13,7 +16,7 @@ namespace cura struct LayerIndex; -#define N_PROGRESS_STAGES 7 +static constexpr size_t N_PROGRESS_STAGES = 7; /*! * Class for handling the progress bar and the progress logging. @@ -39,9 +42,18 @@ class Progress }; private: - static double times[N_PROGRESS_STAGES]; //!< Time estimates per stage - static std::string names[N_PROGRESS_STAGES]; //!< name of each stage - static double accumulated_times[N_PROGRESS_STAGES]; //!< Time past before each stage + static constexpr std::array times{ + 0.0, // START = 0, + 5.269, // SLICING = 1, + 1.533, // PARTS = 2, + 71.811, // INSET_SKIN = 3 + 51.009, // SUPPORT = 4, + 154.62, // EXPORT = 5, + 0.1 // FINISH = 6 + }; + + static constexpr std::array names{ "start", "slice", "layerparts", "inset+skin", "support", "export", "process" }; + static std::array accumulated_times; //!< Time past before each stage static double total_timing; //!< An estimate of the total time static std::optional first_skipped_layer; //!< The index of the layer for which we skipped time reporting /*! @@ -51,7 +63,7 @@ class Progress * \param stage_process How far we currently are in the \p stage * \return An estimate of the overall progress. */ - static float calcOverallProgress(Stage stage, float stage_progress); + static double calcOverallProgress(Stage stage, double stage_progress); public: static void init(); //!< Initialize some values needed in a fast computation of the progress diff --git a/src/progress/Progress.cpp b/src/progress/Progress.cpp index 6af743785d..be6a688813 100644 --- a/src/progress/Progress.cpp +++ b/src/progress/Progress.cpp @@ -15,62 +15,50 @@ namespace cura { - -double Progress::times[] = { - 0.0, // START = 0, - 5.269, // SLICING = 1, - 1.533, // PARTS = 2, - 71.811, // INSET_SKIN = 3 - 51.009, // SUPPORT = 4, - 154.62, // EXPORT = 5, - 0.1 // FINISH = 6 -}; -std::string Progress::names[] = { "start", "slice", "layerparts", "inset+skin", "support", "export", "process" }; - -double Progress::accumulated_times[N_PROGRESS_STAGES] = { -1 }; +std::array Progress::accumulated_times = { -1 }; double Progress::total_timing = -1; std::optional Progress::first_skipped_layer{}; -float Progress::calcOverallProgress(Stage stage, float stage_progress) +double Progress::calcOverallProgress(Stage stage, double stage_progress) { assert(stage_progress <= 1.0); assert(stage_progress >= 0.0); - return (accumulated_times[(int)stage] + stage_progress * times[(int)stage]) / total_timing; + return (accumulated_times.at(static_cast(stage)) + stage_progress * times.at(static_cast(stage))) / total_timing; } void Progress::init() { double accumulated_time = 0; - for (int stage = 0; stage < N_PROGRESS_STAGES; stage++) + for (size_t stage = 0; stage < N_PROGRESS_STAGES; stage++) { - accumulated_times[(int)stage] = accumulated_time; - accumulated_time += times[(int)stage]; + accumulated_times.at(static_cast(stage)) = accumulated_time; + accumulated_time += times.at(static_cast(stage)); } total_timing = accumulated_time; } void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max) { - float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max)); - Application::getInstance().communication->sendProgress(percentage); + double percentage = calcOverallProgress(stage, static_cast(progress_in_stage / static_cast(progress_in_stage_max))); + Application::getInstance().communication->sendProgress(static_cast(percentage)); } void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper) { - if (time_keeper) + if (time_keeper != nullptr) { - if ((int)stage > 0) + if (static_cast(stage) > 0) { - spdlog::info("Progress: {} accomplished in {:03.3f}s", names[(int)stage - 1], time_keeper->restart()); + spdlog::info("Progress: {} accomplished in {:03.3f}s", names.at(static_cast(stage) - 1), time_keeper->restart()); } else { time_keeper->restart(); } - if ((int)stage < (int)Stage::FINISH) + if (static_cast(stage) < static_cast(Stage::FINISH)) { - spdlog::info("Starting {}...", names[(int)stage]); + spdlog::info("Starting {}...", names.at(static_cast(stage))); } } } @@ -108,7 +96,7 @@ void Progress::messageProgressLayer(LayerIndex layer_nr, size_t total_layers, do { padding = iterator_max_size->stage.size(); - for (auto [index, time] : stages | ranges::views::enumerate) + for (const auto& [index, time] : stages | ranges::views::enumerate) { spdlog::info("{}── {}:{} {:03.3f}s", index < stages.size() - 1 ? "├" : "└", time.stage, std::string(padding - time.stage.size(), ' '), time.duration); } From 0c3e9d0c492d15068f8afe9aa2743e5e068e7cbd Mon Sep 17 00:00:00 2001 From: jellespijker Date: Wed, 8 Nov 2023 11:22:57 +0000 Subject: [PATCH 656/656] Applied clang-format. --- include/progress/Progress.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/progress/Progress.h b/include/progress/Progress.h index ae829867bb..c1e20cc6c7 100644 --- a/include/progress/Progress.h +++ b/include/progress/Progress.h @@ -5,9 +5,9 @@ #define PROGRESS_H #include +#include #include #include -#include #include "utils/gettime.h"