diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 486474a5f5..91943e6dcc 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -21,6 +21,7 @@ on: - main - 'CURA-*' - 'PP-*' + - 'NP-*' - '[0-9]+.[0-9]+' permissions: diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index 76060d3063..0acc07d220 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -14,6 +14,7 @@ on: - main - 'CURA-*' - 'PP-*' + - 'NP-*' - '[0-9].[0-9]*' - '[0-9].[0-9][0-9]*' tags: diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 141bc61989..29c83817de 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -17,6 +17,7 @@ on: - main - 'CURA-*' - 'PP-*' + - 'NP-*' - '[0-9]+.[0-9]+' permissions: diff --git a/.github/workflows/stress_benchmark.yml b/.github/workflows/stress_benchmark.yml index 3911302b5d..f09bb61a8b 100644 --- a/.github/workflows/stress_benchmark.yml +++ b/.github/workflows/stress_benchmark.yml @@ -20,6 +20,7 @@ on: - main - 'CURA-*' - 'PP-*' + - 'NP-*' - '[0-9]+.[0-9]+' permissions: diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 66e7bc0f82..b8c85c284d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -15,6 +15,7 @@ on: - main - 'CURA-*' - 'PP-*' + - 'NP-*' - '[0-9]+.[0-9]+' pull_request: diff --git a/CMakeLists.txt b/CMakeLists.txt index 62dab2e03d..8a5abfbc30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,21 +17,23 @@ option(ENABLE_SENTRY "Send crash data via Sentry" 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) +option(ENABLE_THREADING "Enable threading support" ON) -# Generate the plugin types -find_package(protobuf REQUIRED) -find_package(asio-grpc REQUIRED) -find_package(gRPC REQUIRED) -find_package(curaengine_grpc_definitions REQUIRED) -option(OLDER_APPLE_CLANG "Apple Clang <= 13 used" OFF) +if (${ENABLE_ARCUS} OR ${ENABLE_PLUGINS}) + find_package(protobuf REQUIRED) +endif () -MESSAGE(STATUS "Compiling with plugins support: ${ENABLE_PLUGINS}") +MESSAGE(STATUS "Building with plugins support: ${ENABLE_PLUGINS}") if (${ENABLE_PLUGINS}) + find_package(asio-grpc REQUIRED) + find_package(gRPC REQUIRED) + find_package(curaengine_grpc_definitions REQUIRED) + find_package(semver REQUIRED) MESSAGE(STATUS "Plugin secure remotes allowed: ${ENABLE_REMOTE_PLUGINS}") endif () -if (ENABLE_ARCUS) - message(STATUS "Building with Arcus") +message(STATUS "Building with Arcus: ${ENABLE_ARCUS}") +if (${ENABLE_ARCUS}) find_package(arcus REQUIRED) protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto) endif () @@ -151,10 +153,12 @@ set(engine_SRCS # Except main.cpp. src/utils/ToolpathVisualizer.cpp src/utils/VoronoiUtils.cpp src/utils/VoxelUtils.cpp - ) +) add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) -use_threads(_CuraEngine) +if (ENABLE_THREADING) + use_threads(_CuraEngine) +endif () target_include_directories(_CuraEngine PUBLIC @@ -162,7 +166,7 @@ target_include_directories(_CuraEngine $ PRIVATE $ # Include Cura.pb.h - ) +) target_compile_definitions(_CuraEngine PUBLIC @@ -180,7 +184,7 @@ target_compile_definitions(_CuraEngine $<$:ASSERT_INSANE_OUTPUT> $<$:USE_CPU_TIME> $<$:DEBUG> - ) +) enable_sanitizers(_CuraEngine) @@ -189,7 +193,7 @@ if (${EXTENSIVE_WARNINGS}) endif () if (ENABLE_ARCUS) - target_link_libraries(_CuraEngine PUBLIC arcus::arcus ) + target_link_libraries(_CuraEngine PUBLIC arcus::arcus) endif () find_package(clipper REQUIRED) @@ -200,7 +204,6 @@ find_package(spdlog REQUIRED) find_package(fmt REQUIRED) find_package(range-v3 REQUIRED) find_package(scripta REQUIRED) -find_package(semver REQUIRED) if (ENABLE_SENTRY) find_package(sentry REQUIRED) @@ -220,13 +223,13 @@ target_link_libraries(_CuraEngine stb::stb boost::boost scripta::scripta - semver::semver - curaengine_grpc_definitions::curaengine_grpc_definitions - asio-grpc::asio-grpc - grpc::grpc - protobuf::libprotobuf - $<$:sentry::sentry> - $<$:GTest::gtest>) + $<$:semver::semver> + $<$:curaengine_grpc_definitions::curaengine_grpc_definitions> + $<$:asio-grpc::asio-grpc> + $<$:grpc::grpc> + $<$:protobuf::libprotobuf> + $<$:sentry::sentry> + $<$:GTest::gtest>) target_compile_definitions(_CuraEngine PRIVATE $<$:SENTRY_URL=\"${SENTRY_URL}\"> @@ -242,7 +245,7 @@ else () SET(CMAKE_RC_COMPILER_INIT windres) SET(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o " - ) + ) endif () add_executable(CuraEngine src/main.cpp ${RES_FILES}) # ..., but don't forget the glitter! if (ENABLE_SENTRY) @@ -250,10 +253,18 @@ else () endif () endif (NOT WIN32) -use_threads(CuraEngine) +if (ENABLE_THREADING) + use_threads(CuraEngine) +endif () + +if (CMAKE_CXX_PLATFORM_ID STREQUAL "emscripten") + message(STATUS "Building for Emscripten") + target_link_options(_CuraEngine PUBLIC -Wno-unused-command-line-argument -sINVOKE_RUN=0 -sEXPORT_NAME=CuraEngine -sEXPORTED_RUNTIME_METHODS=[callMain,FS] -sFORCE_FILESYSTEM=1 -sALLOW_MEMORY_GROWTH=1 -sEXPORT_ES6=1 -sMODULARIZE=1 -sSINGLE_FILE=1 -sENVIRONMENT=worker -sERROR_ON_UNDEFINED_SYMBOLS=0 -lembind --embind-emit-tsd CuraEngine.d.ts) +endif () + target_link_libraries(CuraEngine PRIVATE _CuraEngine - $<$:sentry::sentry> + $<$:sentry::sentry> ) target_compile_definitions(CuraEngine PRIVATE $<$:SENTRY_URL=\"${SENTRY_URL}\"> @@ -280,10 +291,10 @@ if (ENABLE_TESTING OR ENABLE_BENCHMARKS) GTest::gtest GTest::gmock clipper::clipper - curaengine_grpc_definitions::curaengine_grpc_definitions - asio-grpc::asio-grpc - grpc::grpc - protobuf::libprotobuf) + $<$: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 () @@ -293,7 +304,7 @@ if (ENABLE_BENCHMARKS) add_subdirectory(benchmark) if (NOT WIN32) add_subdirectory(stress_benchmark) - endif() + endif () endif () if (ENABLE_TESTING) diff --git a/Cura.proto b/Cura.proto index f1e261bece..3fc57b7385 100644 --- a/Cura.proto +++ b/Cura.proto @@ -12,6 +12,7 @@ message ObjectList // 0 ... 99: Broadcasts // 100 ... 199: Modify // 200 ... 299: Generate +// IMPORTANT: If you add a slot ID also update the SlotID enum in include/plugins/slots.h enum SlotID { SETTINGS_BROADCAST = 0; SIMPLIFY_MODIFY = 100; diff --git a/benchmark/simplify_benchmark.h b/benchmark/simplify_benchmark.h index d3726bce65..88360af005 100644 --- a/benchmark/simplify_benchmark.h +++ b/benchmark/simplify_benchmark.h @@ -1,11 +1,10 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2024 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 "../tests/ReadTestPolygons.h" -#include "plugins/slots.h" #include "utils/Simplify.h" #include "utils/channel.h" @@ -13,7 +12,11 @@ #include #include + +#ifdef ENABLE_PLUGINS +#include "plugins/slots.h" #include +#endif namespace cura { @@ -58,6 +61,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_local)(benchmark::State& st) BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_local); +#ifdef ENABLE_PLUGINS BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State& st) { for (auto _ : st) @@ -71,6 +75,7 @@ BENCHMARK_DEFINE_F(SimplifyTestFixture, simplify_slot_noplugin)(benchmark::State } BENCHMARK_REGISTER_F(SimplifyTestFixture, simplify_slot_noplugin); +#endif } // namespace cura #endif // CURAENGINE_BENCHMARK_SIMPLIFY_BENCHMARK_H diff --git a/conandata.yml b/conandata.yml index a020e5417a..14cf72dc44 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1,5 +1,7 @@ version: "5.7.0-alpha.1" requirements: + - "scripta/0.1.0@ultimaker/testing" +requirements_arcus: - "arcus/5.3.1" +requirements_plugins: - "curaengine_grpc_definitions/(latest)@ultimaker/testing" - - "scripta/0.1.0@ultimaker/testing" diff --git a/conanfile.py b/conanfile.py index 191131c272..d20378d7e7 100644 --- a/conanfile.py +++ b/conanfile.py @@ -74,7 +74,8 @@ def config_options(self): def configure(self): self.options["boost"].header_only = True self.options["clipper"].shared = True - self.options["protobuf"].shared = False + if self.options.enable_arcus or self.options.enable_plugins: + self.options["protobuf"].shared = False if self.options.enable_arcus: self.options["arcus"].shared = True if self.settings.os == "Linux": @@ -103,15 +104,20 @@ def build_requirements(self): def requirements(self): for req in self.conan_data["requirements"]: - if "arcus" in req and not self.options.enable_arcus: - continue self.requires(req) + if self.options.enable_arcus: + for req in self.conan_data["requirements_arcus"]: + self.requires(req) if self.options.get_safe("enable_sentry", False): self.requires("sentry-native/0.6.5") + if self.options.enable_plugins: + self.requires("neargye-semver/0.3.0") + self.requires("asio-grpc/2.6.0") + self.requires("grpc/1.50.1") + for req in self.conan_data["requirements_plugins"]: + self.requires(req) if self.options.enable_arcus or self.options.enable_plugins: - self.requires("protobuf/3.21.9") - self.requires("asio-grpc/2.6.0") - self.requires("grpc/1.50.1") + self.requires("protobuf/3.21.12") self.requires("clipper/6.4.2@ultimaker/stable") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") @@ -119,7 +125,6 @@ def requirements(self): self.requires("spdlog/1.12.0") self.requires("fmt/10.1.1") self.requires("range-v3/0.12.0") - self.requires("neargye-semver/0.3.0") self.requires("zlib/1.2.12") self.requires("openssl/3.2.0") @@ -134,6 +139,7 @@ def generate(self): 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" + tc.variables["ENABLE_THREADING"] = not (self.settings.arch == "wasm" and self.settings.os == "Emscripten") if self.options.get_safe("enable_sentry", False): tc.variables["ENABLE_SENTRY"] = True tc.variables["SENTRY_URL"] = self.conf.get("user.curaengine:sentry_url", "", check_type=str) @@ -206,8 +212,15 @@ def build(self): self.run(f"sentry-cli --auth-token {os.environ['SENTRY_TOKEN']} releases finalize -o {sentry_org} -p {sentry_project} {self.version}") def package(self): - ext = ".exe" if self.settings.os == "Windows" else "" + match self.setting.os: + case "Windows": + ext = ".exe" + case "Emscripten": + ext = ".js" + case other: + ext = "" copy(self, f"CuraEngine{ext}", src=self.build_folder, dst=path.join(self.package_folder, "bin")) + copy(self, f"*.d.ts", src=self.build_folder, dst=path.join(self.package_folder, "bin")) copy(self, f"_CuraEngine.*", src=self.build_folder, dst=path.join(self.package_folder, "lib")) copy(self, "LICENSE*", src=self.source_folder, dst=path.join(self.package_folder, "license")) diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index ac7c1ccda7..11b86e8e65 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -1,9 +1,10 @@ -// Copyright (c) 2018-2022 Ultimaker B.V. -// CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher #ifndef COMMANDLINE_H #define COMMANDLINE_H +#include #include //Loading JSON documents to get settings from them. #include //To store the command line arguments. #include @@ -151,6 +152,12 @@ class CommandLine : public Communication void sliceNext() override; private: +#ifdef __EMSCRIPTEN__ + std::string progressHandler; +#endif + + std::unordered_set search_directories_; + /* * \brief The command line arguments that the application was called with. */ @@ -161,12 +168,6 @@ class CommandLine : public Communication */ unsigned int last_shown_progress_; - /* - * \brief Get the default search directories to search for definition files. - * \return The default search directories to search for definition files. - */ - std::unordered_set defaultSearchDirectories(); - /* * \brief Load a JSON file and store the settings inside it. * \param json_filename The location of the JSON file to load settings from. @@ -177,7 +178,7 @@ class CommandLine : public Communication * 1, the file could not be opened. If it's 2, there was a syntax error in * the file. */ - int loadJSON(const std::string& json_filename, Settings& settings, bool force_read_parent = false, bool force_read_nondefault = false); + int loadJSON(const std::filesystem::path& json_filename, Settings& settings, bool force_read_parent = false, bool force_read_nondefault = false); /* * \brief Load a JSON document and store the settings inside it. @@ -190,7 +191,7 @@ class CommandLine : public Communication */ int loadJSON( const rapidjson::Document& document, - const std::unordered_set& search_directories, + const std::unordered_set& search_directories, Settings& settings, bool force_read_parent = false, bool force_read_nondefault = false); @@ -211,7 +212,7 @@ class CommandLine : public Communication * \param search_directories The directories to search in. * \return The first definition file that matches the definition ID. */ - const std::string findDefinitionFile(const std::string& definition_id, const std::unordered_set& search_directories); + static std::string findDefinitionFile(const std::string& definition_id, const std::unordered_set& search_directories); }; } // namespace cura diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 01870b2056..9af4e7cbae 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -1,8 +1,17 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #ifndef PLUGINS_CONVERTERS_H #define PLUGINS_CONVERTERS_H +#ifdef ENABLE_PLUGINS + +#include +#include +#include + +#include +#include +#include #include "Cura.pb.h" #include "WallToolPaths.h" @@ -26,14 +35,6 @@ #include "settings/types/LayerIndex.h" #include "utils/polygon.h" -#include -#include -#include - -#include -#include -#include - namespace cura::plugins { @@ -128,5 +129,5 @@ struct gcode_paths_modify_response : public details::converter #include #include @@ -237,4 +238,59 @@ using slots = plugins::details::SingletonRegistry::min(), + SlotID_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits::max() +}; +} // namespace plugins::v0 + +namespace slots +{ +namespace details +{ +struct Slots +{ + template + constexpr auto modify(auto&& data, auto&&... args) noexcept + { + return std::forward(data); + } + + template + constexpr auto broadcast(auto&&... args) noexcept + { + } + + constexpr auto connect(auto&&... args) noexcept + { + } +}; +} // namespace details + +constexpr details::Slots instance() noexcept +{ + return {}; +} +} // namespace slots + +} // namespace cura + + +#endif // ENABLE_PLUGINS + #endif // PLUGINS_SLOTS_H diff --git a/include/utils/channel.h b/include/utils/channel.h index 4ff915ffe7..fb3574d169 100644 --- a/include/utils/channel.h +++ b/include/utils/channel.h @@ -1,9 +1,10 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #ifndef CHANNEL_H #define CHANNEL_H +#ifdef ENABLE_PLUGINS #include #include #include @@ -26,4 +27,5 @@ std::shared_ptr createChannel(const ChannelSetupConfiguration& co } // namespace cura::utils +#endif // ENABLE_PLUGINS #endif // CHANNEL_H diff --git a/include/utils/views/split_paths.h b/include/utils/views/split_paths.h new file mode 100644 index 0000000000..926150555a --- /dev/null +++ b/include/utils/views/split_paths.h @@ -0,0 +1,33 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef CURAENGINE_SPLIT_PATHS_H +#define CURAENGINE_SPLIT_PATHS_H + +#include + +#include +#include +#include + +namespace cura::views +{ +namespace details +{ +#ifdef _WIN32 +constexpr auto path_sep = ";"; +#else +constexpr auto path_sep = ':'; +#endif +} // namespace details + +inline static constexpr auto split_paths = ranges::views::split(details::path_sep) + | ranges::views::transform( + [](auto&& rng) + { + return std::string_view(&*rng.begin(), ranges::distance(rng)); + }); + +} // namespace cura::views + +#endif // CURAENGINE_SPLIT_PATHS_H diff --git a/src/Application.cpp b/src/Application.cpp index db6d7b4c2c..897b4c89eb 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -131,6 +131,7 @@ void Application::printHelp() const fmt::print(" -v\n\tIncrease the verbose level (show log messages).\n"); fmt::print(" -m\n\tSet the desired number of threads.\n"); fmt::print(" -p\n\tLog progress information.\n"); + fmt::print(" -d Add definition search paths seperated by a `:` (Unix) or `;` (Windows)\n"); fmt::print(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n"); fmt::print(" -s =\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n"); fmt::print(" -l \n\tLoad an STL model. \n"); @@ -152,7 +153,7 @@ void Application::printLicense() const { fmt::print("\n"); fmt::print("Cura_SteamEngine version {}\n", CURA_ENGINE_VERSION); - fmt::print("Copyright (C) 2023 Ultimaker\n"); + fmt::print("Copyright (C) 2024 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"); diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 95359abae4..f6cd567431 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -1,18 +1,22 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include "communication/CommandLine.h" +#include // error number when trying to read file #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 //Loading JSON documents to get settings from them. -#include #include +#include +#include +#include #include +#include +#include #include #include "Application.h" //To get the extruders for material estimates. @@ -20,14 +24,24 @@ #include "FffProcessor.h" //To start a slice and get time estimates. #include "Slice.h" #include "utils/Matrix4x3D.h" //For the mesh_rotation_matrix setting. +#include "utils/format/filesystem_path.h" +#include "utils/views/split_paths.h" + +#ifdef __EMSCRIPTEN__ +#include +#endif namespace cura { CommandLine::CommandLine(const std::vector& arguments) - : arguments_(arguments) - , last_shown_progress_(0) + : arguments_{ arguments } + , last_shown_progress_{ 0 } { + if (auto search_paths = spdlog::details::os::getenv("CURA_ENGINE_SEARCH_PATH"); ! search_paths.empty()) + { + search_directories_ = search_paths | views::split_paths | ranges::to>(); + }; } // These are not applicable to command line slicing. @@ -94,7 +108,7 @@ void CommandLine::sendPrintTimeMaterialEstimates() const sum = 0.0; for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice_->scene.extruders.size(); extruder_nr++) { - sum += FffProcessor::getInstance()->getTotalFilamentUsed(extruder_nr); + sum += FffProcessor::getInstance()->getTotalFilamentUsed(static_cast(extruder_nr)); } } @@ -106,6 +120,12 @@ void CommandLine::sendProgress(double progress) const return; } // TODO: Do we want to print a progress bar? We'd need a better solution to not have that progress bar be ruined by any logging. +#ifdef __EMSCRIPTEN__ + // Call progress handler with progress + char js[100]; + std::sprintf(js, "globalThis[\"%s\"](%f)", progressHandler.c_str(), progress); + emscripten_run_script(js); +#endif } void CommandLine::sliceNext() @@ -116,7 +136,7 @@ void CommandLine::sliceNext() size_t num_mesh_groups = 1; for (size_t argument_index = 2; argument_index < arguments_.size(); argument_index++) { - if (arguments_[argument_index].find("--next") == 0) // Starts with "--next". + if (arguments_[argument_index].starts_with("--next")) // Starts with "--next". { num_mesh_groups++; } @@ -130,7 +150,7 @@ void CommandLine::sliceNext() slice.scene.extruders.reserve(arguments_.size() >> 1); // Allocate enough memory to prevent moves. slice.scene.extruders.emplace_back(0, &slice.scene.settings); // Always have one extruder. - ExtruderTrain* last_extruder = &slice.scene.extruders[0]; + ExtruderTrain* last_extruder = slice.scene.extruders.data(); bool force_read_parent = false; bool force_read_nondefault = false; @@ -142,7 +162,7 @@ void CommandLine::sliceNext() { if (argument[1] == '-') // Starts with "--". { - if (argument.find("--next") == 0) // Starts with "--next". + if (argument.starts_with("--next")) // Starts with "--next". { try { @@ -161,23 +181,32 @@ void CommandLine::sliceNext() exit(1); } } - else if (argument.find("--force-read-parent") == 0 || argument.find("--force_read_parent") == 0) + else if (argument.starts_with("--force-read-parent") || argument.starts_with("--force_read_parent")) { spdlog::info("From this point on, force the parser to read values of non-leaf settings, instead of skipping over them as is proper."); force_read_parent = true; } - else if (argument.find("--force-read-nondefault") == 0 || argument.find("--force_read_nondefault") == 0) + else if (argument.starts_with("--force-read-nondefault") || argument.starts_with("--force_read_nondefault")) { 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) + else if (argument.starts_with("--end-force-read") || argument.starts_with("--end_force_read")) { spdlog::info("From this point on, reset all force-XXX values to false (don't 'force read ___' anymore)."); force_read_parent = false; force_read_nondefault = false; } +#ifdef __EMSCRIPTEN__ + else if (argument.find("--progress") == 0) + { + // Store progress handler name + argument_index++; + argument = arguments_[argument_index]; + progressHandler = argument; + } +#endif else { spdlog::error("Unknown option: {}", argument); @@ -204,6 +233,18 @@ void CommandLine::sliceNext() // enableProgressLogging(); FIXME: how to handle progress logging? Is this still relevant? break; } + case 'd': + { + argument_index++; + if (argument_index >= arguments_.size()) + { + spdlog::error("Missing definition search paths"); + exit(1); + } + argument = arguments_[argument_index]; + search_directories_ = argument | views::split_paths | ranges::to>(); + break; + } case 'j': { argument_index++; @@ -213,7 +254,7 @@ void CommandLine::sliceNext() exit(1); } argument = arguments_[argument_index]; - if (loadJSON(argument, *last_settings, force_read_parent, force_read_nondefault)) + if (loadJSON(std::filesystem::path{ argument }, *last_settings, force_read_parent, force_read_nondefault) != 0) { spdlog::error("Failed to load JSON file: {}", argument); exit(1); @@ -222,7 +263,7 @@ void CommandLine::sliceNext() // If this was the global stack, create extruders for the machine_extruder_count setting. if (last_settings == &slice.scene.settings) { - const size_t extruder_count = slice.scene.settings.get("machine_extruder_count"); + const auto extruder_count = slice.scene.settings.get("machine_extruder_count"); while (slice.scene.extruders.size() < extruder_count) { slice.scene.extruders.emplace_back(slice.scene.extruders.size(), &slice.scene.settings); @@ -257,7 +298,7 @@ void CommandLine::sliceNext() } argument = arguments_[argument_index]; - const Matrix4x3D transformation = last_settings->get("mesh_rotation_matrix"); // The transformation applied to the model when loaded. + const auto transformation = last_settings->get("mesh_rotation_matrix"); // The transformation applied to the model when loaded. if (! loadMeshIntoMeshGroup(&slice.scene.mesh_groups[mesh_group_index], argument.c_str(), transformation, last_extruder->settings_)) { @@ -302,7 +343,7 @@ void CommandLine::sliceNext() exit(1); } argument = arguments_[argument_index]; - const size_t value_position = argument.find("="); + const size_t value_position = argument.find('='); std::string key = argument.substr(0, value_position); if (value_position == std::string::npos) { @@ -319,7 +360,6 @@ void CommandLine::sliceNext() Application::getInstance().printCall(); Application::getInstance().printHelp(); exit(1); - break; } } } @@ -360,60 +400,33 @@ void CommandLine::sliceNext() FffProcessor::getInstance()->finalize(); } -int CommandLine::loadJSON(const std::string& json_filename, Settings& settings, bool force_read_parent, bool force_read_nondefault) +int CommandLine::loadJSON(const std::filesystem::path& json_filename, Settings& settings, bool force_read_parent, bool force_read_nondefault) { - FILE* file = fopen(json_filename.c_str(), "rb"); + std::ifstream file(json_filename, std::ios::binary); if (! file) { spdlog::error("Couldn't open JSON file: {}", json_filename); return 1; } + std::vector read_buffer(std::istreambuf_iterator(file), {}); + rapidjson::MemoryStream memory_stream(read_buffer.data(), read_buffer.size()); + rapidjson::Document json_document; - char read_buffer[4096]; - rapidjson::FileReadStream reader_stream(file, read_buffer, sizeof(read_buffer)); - json_document.ParseStream(reader_stream); - fclose(file); + json_document.ParseStream(memory_stream); if (json_document.HasParseError()) { spdlog::error("Error parsing JSON (offset {}): {}", json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError())); return 2; } - std::unordered_set search_directories = defaultSearchDirectories(); // For finding the inheriting JSON files. - std::string directory = std::filesystem::path(json_filename).parent_path().string(); - search_directories.insert(directory); - - return loadJSON(json_document, search_directories, settings, force_read_parent, force_read_nondefault); -} - -std::unordered_set CommandLine::defaultSearchDirectories() -{ - std::unordered_set result; - - char* search_path_env = getenv("CURA_ENGINE_SEARCH_PATH"); - if (search_path_env) - { -#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) - char delims[] = ":"; // Colon for Unix. -#else - char delims[] = ";"; // Semicolon for Windows. -#endif - char paths[128 * 1024]; // Maximum length of environment variable. - strcpy(paths, search_path_env); // Necessary because strtok actually modifies the original string, and we don't want to modify the environment variable itself. - char* path = strtok(paths, delims); - while (path != nullptr) - { - result.insert(path); - path = strtok(nullptr, delims); // Continue searching in last call to strtok. - } - } - return result; + search_directories_.insert(std::filesystem::path(json_filename).parent_path()); + return loadJSON(json_document, search_directories_, settings, force_read_parent, force_read_nondefault); } int CommandLine::loadJSON( const rapidjson::Document& document, - const std::unordered_set& search_directories, + const std::unordered_set& search_directories, Settings& settings, bool force_read_parent, bool force_read_nondefault) @@ -422,13 +435,13 @@ int CommandLine::loadJSON( if (document.HasMember("inherits") && document["inherits"].IsString()) { std::string parent_file = findDefinitionFile(document["inherits"].GetString(), search_directories); - if (parent_file == "") + if (parent_file.empty()) { spdlog::error("Inherited JSON file: {} not found.", document["inherits"].GetString()); return 1; } - int error_code = loadJSON(parent_file, settings, force_read_parent, force_read_nondefault); // Head-recursively load the settings file that we inherit from. - if (error_code) + // Head-recursively load the settings file that we inherit from. + if (const auto error_code = loadJSON(parent_file, settings, force_read_parent, force_read_nondefault); error_code != 0) { return error_code; } @@ -546,12 +559,16 @@ void CommandLine::loadJSONSettings(const rapidjson::Value& element, Settings& se } } - if (! (setting_object.HasMember("default_value") || (force_read_nondefault && setting_object.HasMember("value") && ! settings.has(name)))) + if (! setting_object.HasMember("default_value") && (! force_read_nondefault || ! setting_object.HasMember("value") || settings.has(name))) { if (! setting_object.HasMember("children")) { // Setting has no child-settings, so must be leaf, but also holds no (default) value?! - spdlog::warn("JSON setting {} has no [default_]value!", name); + spdlog::warn("JSON setting '{}' has no [default_]value!", name); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + setting_object.Accept(writer); + spdlog::debug("JSON setting '{}': '{}'", name, buffer.GetString()); } continue; } @@ -569,19 +586,17 @@ void CommandLine::loadJSONSettings(const rapidjson::Value& element, Settings& se } } -const std::string CommandLine::findDefinitionFile(const std::string& definition_id, const std::unordered_set& search_directories) +std::string CommandLine::findDefinitionFile(const std::string& definition_id, const std::unordered_set& search_directories) { - for (const std::string& search_directory : search_directories) + for (const auto& search_directory : search_directories) { - const std::string candidate = search_directory + std::string("/") + definition_id + std::string(".def.json"); - const std::ifstream ifile(candidate.c_str()); // Check whether the file exists and is readable by opening it. - if (ifile) + if (auto candidate = search_directory / (definition_id + ".def.json"); std::filesystem::exists(candidate)) { return candidate; } } spdlog::error("Couldn't find definition file with ID: {}", definition_id); - return std::string(""); + return {}; } } // namespace cura diff --git a/src/infill.cpp b/src/infill.cpp index e746846c02..c085307622 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include "infill.h" @@ -317,6 +317,7 @@ void Infill::_generate( break; case EFillMethod::PLUGIN: { +#ifdef ENABLE_PLUGINS // FIXME: I don't like this conditional block outside of the plugin scope. auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate( inner_contour_, mesh ? mesh->settings.get("infill_pattern") : settings.get("infill_pattern"), @@ -324,6 +325,7 @@ void Infill::_generate( toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); result_polygons.add(generated_result_polygons_); result_lines.add(generated_result_lines_); +#endif break; } default: diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 40988a2f7b..134fec86dd 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -1,6 +1,7 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#ifdef ENABLE_PLUGINS #include "plugins/converters.h" @@ -481,3 +482,5 @@ gcode_paths_modify_response::native_value_type return paths; } } // namespace cura::plugins + +#endif // ENABLE_PLUGINS \ No newline at end of file diff --git a/src/slicer.cpp b/src/slicer.cpp index d13908458f..5c7dcfe11e 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -784,12 +784,7 @@ 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 = slots::instance().modify( - 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.removeDegenerateVerts(); // remove verts connected to overlapping line segments // Clean up polylines for Surface Mode printing diff --git a/src/utils/channel.cpp b/src/utils/channel.cpp index 45d1711b1b..3b181afa4f 100644 --- a/src/utils/channel.cpp +++ b/src/utils/channel.cpp @@ -1,5 +1,6 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher +#ifdef ENABLE_PLUGINS #include "utils/channel.h" @@ -45,3 +46,5 @@ std::shared_ptr createChannel(const ChannelSetupConfiguration& co } } // namespace cura::utils + +#endif // ENABLE_PLUGINS \ No newline at end of file