diff --git a/CMakeLists.txt b/CMakeLists.txt index 5858a1ab0c..d99b58a178 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(engine_SRCS # Except main.cpp. src/communication/ArcusCommunication.cpp src/communication/ArcusCommunicationPrivate.cpp src/communication/CommandLine.cpp + src/communication/EmscriptenCommunication.cpp src/communication/Listener.cpp src/infill/ImageBasedDensityProvider.cpp @@ -271,7 +272,28 @@ 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) + target_link_options(_CuraEngine + PUBLIC + "SHELL:-sINVOKE_RUN=0" + "SHELL:-sEXPORT_NAME=CuraEngine" + "SHELL:-sEXPORTED_RUNTIME_METHODS=[callMain,FS]" + "SHELL:-sFORCE_FILESYSTEM=1" + "SHELL:-sALLOW_MEMORY_GROWTH=1" + "SHELL:-sEXPORT_ES6=1" + "SHELL:-sMODULARIZE=1" + "SHELL:-sSINGLE_FILE=1" + "SHELL:-sENVIRONMENT=web" + "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" + "SHELL:-sWASM_BIGINT=1" + "SHELL:-sSTACK_SIZE=196608" + $<$:SHELL:-sASSERTIONS=2> + $<$:SHELL:-sSAFE_HEAP=1> + $<$:SHELL:-sSTACK_OVERFLOW_CHECK=2> + $<$:SHELL:-g3> + $<$:SHELL:-gsource-map> + "SHELL:-lembind" + "SHELL:--embind-emit-tsd CuraEngine.d.ts" + ) endif () target_link_libraries(CuraEngine PRIVATE diff --git a/include/Application.h b/include/Application.h index 205766f62f..15a9b94bb2 100644 --- a/include/Application.h +++ b/include/Application.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "utils/NoCopy.h" @@ -38,14 +39,14 @@ class Application : NoCopy * can assume that it is safe to access this without checking whether it is * initialised. */ - Communication* communication_ = nullptr; + std::shared_ptr communication_; /* * \brief The slice that is currently ongoing. * * If no slice has started yet, this will be a nullptr. */ - Slice* current_slice_ = nullptr; + std::shared_ptr current_slice_; /*! * \brief ThreadPool with lifetime tied to Application diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index ed6b1cbbb2..395151d331 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -27,6 +27,8 @@ using container_setting_map = std::unordered_map; class CommandLine : public Communication { public: + CommandLine() = default; + /* * \brief Construct a new communicator that interprets the command line to * start a slice. @@ -141,18 +143,15 @@ class CommandLine : public Communication */ void sliceNext() override; -private: -#ifdef __EMSCRIPTEN__ - std::string progressHandler; -#endif - - std::vector search_directories_; - +protected: /* * \brief The command line arguments that the application was called with. */ std::vector arguments_; +private: + std::vector search_directories_; + /* * The last progress update that we output to stdcerr. */ diff --git a/include/communication/EmscriptenCommunication.h b/include/communication/EmscriptenCommunication.h new file mode 100644 index 0000000000..5144b96dc6 --- /dev/null +++ b/include/communication/EmscriptenCommunication.h @@ -0,0 +1,65 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef EMSCRIPTENCOMMUNICATION_H +#define EMSCRIPTENCOMMUNICATION_H +#ifdef __EMSCRIPTEN__ + +#include "communication/CommandLine.h" + +namespace cura +{ + +/** + * \class EmscriptenCommunication + * \brief A class for handling communication in an Emscripten environment. + * + * This class extends the CommandLine class and provides specific implementations + * for sending progress and handling slice information in an Emscripten environment. + */ +class EmscriptenCommunication : public CommandLine +{ +private: + std::string progress_handler_; ///< Handler for progress messages. + std::string gcode_header_handler_; ///< Handler for getting the GCode handler. + std::string slice_info_handler_; ///< Handler for slice information messages. + + /** + * \brief Creates a message containing slice information. + * \return A string containing the slice information message. + */ + [[nodiscard]] static std::string createSliceInfoMessage(); + +public: + /** + * \brief Constructor for EmscriptenCommunication. + * \param arguments A vector of strings containing the command line arguments. + */ + EmscriptenCommunication(const std::vector& arguments); + + /** + * \brief Sends the progress of the current operation. + * \param progress A double representing the progress percentage. + */ + void sendProgress(double progress) const override; + + /** + * \brief Sends GcodeHeader + */ + void sendGCodePrefix(const std::string& prefix) const override; + + /** + * \brief Initiates the slicing of the next item. + */ + void sliceNext() override; + + bool isSequential() const override + { + return false; + } +}; + +} // namespace cura + +#endif // __EMSCRIPTEN__ +#endif // EMSCRIPTENCOMMUNICATION_H diff --git a/include/utils/math.h b/include/utils/math.h index bb911f67b7..f1ad772c72 100644 --- a/include/utils/math.h +++ b/include/utils/math.h @@ -13,37 +13,97 @@ namespace cura { +/** + * @brief Returns the square of a value. + * + * @tparam T A multipliable type (arithmetic types such as int, float, double, etc.) + * @param a The value to be squared. + * @return T The square of the input value. + */ template [[nodiscard]] T square(const T& a) { return a * a; } -[[nodiscard]] inline int64_t round_divide_signed(const int64_t dividend, const int64_t divisor) //!< Return dividend divided by divisor rounded to the nearest integer +/** + * @brief Returns the quotient of the division of two signed integers, rounded to the nearest integer. + * + * @param dividend The numerator. + * @param divisor The denominator (must not be zero). + * @return int64_t The result of the division rounded to the nearest integer. + * @throws std::invalid_argument If the divisor is zero. + */ +[[nodiscard]] inline int64_t round_divide_signed(const int64_t dividend, const int64_t divisor) { - if ((dividend < 0) ^ (divisor < 0)) // Either the numerator or the denominator is negative, so the result must be negative. + if ((dividend < 0) ^ (divisor < 0)) { - return (dividend - divisor / 2) / divisor; // Flip the .5 offset to do proper rounding in the negatives too. + return (dividend - divisor / 2) / divisor; } return (dividend + divisor / 2) / divisor; } -[[nodiscard]] inline uint64_t ceil_divide_signed(const int64_t dividend, const int64_t divisor) //!< Return dividend divided by divisor rounded up towards positive infinity. +/** + * @brief Returns the quotient of the division of two signed integers, rounded up towards positive infinity. + * + * @param dividend The numerator. + * @param divisor The denominator (must not be zero). + * @return int64_t The result of the division rounded up. + * @throws std::invalid_argument If the divisor is zero. + */ +[[nodiscard]] inline int64_t ceil_divide_signed(const int64_t dividend, const int64_t divisor) { - return static_cast((dividend / divisor) + (dividend * divisor > 0 ? 1 : 0)); + int64_t quotient = dividend / divisor; + int64_t remainder = dividend % divisor; + + // Round up if there's a remainder and the signs of dividend and divisor are the same + if (remainder != 0 && ((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0))) + { + quotient += 1; + } + + return quotient; } -[[nodiscard]] inline uint64_t floor_divide_signed(const int64_t dividend, const int64_t divisor) //!< Return dividend divided by divisor rounded down towards negative infinity. +/** + * @brief Returns the quotient of the division of two signed integers, rounded down towards negative infinity. + * + * @param dividend The numerator. + * @param divisor The denominator (must not be zero). + * @return int64_t The result of the division rounded down. + * @throws std::invalid_argument If the divisor is zero. + */ +[[nodiscard]] inline int64_t floor_divide_signed(const int64_t dividend, const int64_t divisor) { - return static_cast((dividend / divisor) + (dividend * divisor > 0 ? 0 : -1)); + const int64_t quotient = dividend / divisor; + const int64_t remainder = dividend % divisor; + if (remainder != 0 && ((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0))) + { + return quotient - 1; + } + return quotient; } -[[nodiscard]] inline uint64_t round_divide(const uint64_t dividend, const uint64_t divisor) //!< Return dividend divided by divisor rounded to the nearest integer +/** + * @brief Returns the quotient of the division of two unsigned integers, rounded to the nearest integer. + * + * @param dividend The numerator. + * @param divisor The denominator (must not be zero). + * @return uint64_t The result of the division rounded to the nearest integer. + */ +[[nodiscard]] inline uint64_t round_divide(const uint64_t dividend, const uint64_t divisor) { return (dividend + divisor / 2) / divisor; } -[[nodiscard]] inline uint64_t round_up_divide(const uint64_t dividend, const uint64_t divisor) //!< Return dividend divided by divisor rounded to the nearest integer +/** + * @brief Returns the quotient of the division of two unsigned integers, rounded up towards positive infinity. + * + * @param dividend The numerator. + * @param divisor The denominator (must not be zero). + * @return uint64_t The result of the division rounded up. + */ +[[nodiscard]] inline uint64_t round_up_divide(const uint64_t dividend, const uint64_t divisor) { return (dividend + divisor - 1) / divisor; } diff --git a/include/utils/string.h b/include/utils/string.h index 7468077279..f21bd66524 100644 --- a/include/utils/string.h +++ b/include/utils/string.h @@ -9,6 +9,9 @@ #include #include // ostringstream +#include +#include +#include #include namespace cura @@ -27,6 +30,30 @@ static inline int stringcasecompare(const char* a, const char* b) return *a - *b; } +// Convert string to base64 string. +// This function is useful to forward string through javascript even if they contain any special strings +// +[[maybe_unused]] static std::string convertTobase64(const std::string& input) +{ + using namespace boost::archive::iterators; + // prepare the stream to hold the encoded data + std::stringstream output; + + // encode data + typedef base64_from_binary> base64_enc; + std::copy(base64_enc(input.begin()), base64_enc(input.end()), ostream_iterator(output)); + + // Retrieve the encoded string + std::string output_encoded = output.str(); + + // ensure padding if needed + size_t num = (3 - input.length() % 3) % 3; + for (size_t i = 0; i < num; i++) + { + output_encoded.push_back('='); + } + return output_encoded; +} /*! * Efficient conversion of micron integer type to millimeter string. * diff --git a/src/Application.cpp b/src/Application.cpp index a2a271f617..319c55e544 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -18,8 +18,10 @@ #include #include +#include "Slice.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 "communication/EmscriptenCommunication.h" // To use Emscripten to slice stuff. #include "progress/Progress.h" #include "utils/ThreadPool.h" #include "utils/string.h" //For stringcasecompare. @@ -45,7 +47,6 @@ Application::Application() Application::~Application() { - delete communication_; delete thread_pool_; } @@ -100,7 +101,7 @@ void Application::connect() } } - ArcusCommunication* arcus_communication = new ArcusCommunication(); + auto arcus_communication = std::make_shared(); arcus_communication->connect(ip, port); communication_ = arcus_communication; } @@ -214,8 +215,11 @@ void Application::slice() { arguments.emplace_back(argv_[argument_index]); } - - communication_ = new CommandLine(arguments); +#ifdef __EMSCRIPTEN__ + communication_ = std::make_shared(arguments); +#else + communication_ = std::make_shared(arguments); +#endif } void Application::run(const size_t argc, char** argv) diff --git a/src/ConicalOverhang.cpp b/src/ConicalOverhang.cpp index 699d9697d7..ab4c60873d 100644 --- a/src/ConicalOverhang.cpp +++ b/src/ConicalOverhang.cpp @@ -23,7 +23,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 = std::llround(tan_angle * static_cast(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 = LayerIndex(slicer->layers.size()) - 2; layer_nr >= 0; layer_nr--) { SlicerLayer& layer = slicer->layers[static_cast(layer_nr)]; SlicerLayer& layer_above = slicer->layers[static_cast(layer_nr) + 1ul]; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index ee853ca551..9a7f26a410 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -575,7 +575,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const size_t surface_extruder_nr = mesh_group_settings.get("raft_surface_extruder_nr").extruder_nr_; coord_t z = 0; - const LayerIndex initial_raft_layer_nr = -Raft::getTotalExtraLayers(); + const LayerIndex initial_raft_layer_nr = -LayerIndex(Raft::getTotalExtraLayers()); const Settings& interface_settings = mesh_group_settings.get("raft_interface_extruder_nr").settings_; const size_t num_interface_layers = interface_settings.get("raft_interface_layers"); const Settings& surface_settings = mesh_group_settings.get("raft_surface_extruder_nr").settings_; @@ -764,7 +764,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) z += raft_interface_z_offset; - for (LayerIndex raft_interface_layer = 1; static_cast(raft_interface_layer) <= num_interface_layers; ++raft_interface_layer) + for (LayerIndex raft_interface_layer = 1; raft_interface_layer <= LayerIndex(num_interface_layers); ++raft_interface_layer) { // raft interface layer const LayerIndex layer_nr = initial_raft_layer_nr + raft_interface_layer; z += interface_layer_height; @@ -925,7 +925,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) z += raft_surface_z_offset; - for (LayerIndex raft_surface_layer = 1; static_cast(raft_surface_layer) <= num_surface_layers; raft_surface_layer++) + for (LayerIndex raft_surface_layer = 1; raft_surface_layer <= LayerIndex(num_surface_layers); raft_surface_layer++) { // raft surface layers const LayerIndex layer_nr = initial_raft_layer_nr + 1 + num_interface_layers + raft_surface_layer - 1; // +1: 1 base layer z += surface_layer_height; @@ -1538,7 +1538,7 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor extruder_order_per_layer.init(true, storage.print_layer_count); const std::vector extruders_used = storage.getExtrudersUsed(); - for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr < static_cast(storage.print_layer_count); layer_nr++) + for (LayerIndex layer_nr = -LayerIndex(Raft::getTotalExtraLayers()); layer_nr < LayerIndex(storage.print_layer_count); layer_nr++) { std::vector extruder_order = getUsedExtrudersOnLayer(storage, last_extruder, layer_nr, extruders_used); extruder_order_per_layer.push_back(extruder_order); @@ -1557,7 +1557,7 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor void FffGcodeWriter::calculatePrimeLayerPerExtruder(const SliceDataStorage& storage) { - LayerIndex first_print_layer = -Raft::getTotalExtraLayers(); + LayerIndex first_print_layer = -LayerIndex(Raft::getTotalExtraLayers()); for (size_t extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; ++extruder_nr) { if (getExtruderNeedPrimeBlobDuringFirstLayer(storage, extruder_nr)) @@ -1567,7 +1567,7 @@ void FffGcodeWriter::calculatePrimeLayerPerExtruder(const SliceDataStorage& stor } } - for (LayerIndex layer_nr = first_print_layer; layer_nr < static_cast(storage.print_layer_count); ++layer_nr) + for (LayerIndex layer_nr = first_print_layer; layer_nr < LayerIndex(storage.print_layer_count); ++layer_nr) { const std::vector used_extruders = storage.getExtrudersUsed(layer_nr); for (size_t extruder_nr = 0; extruder_nr < used_extruders.size(); ++extruder_nr) @@ -1591,7 +1591,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( assert(static_cast(extruder_count) > 0); std::vector ret; std::vector extruder_is_used_on_this_layer = storage.getExtrudersUsed(layer_nr); - const LayerIndex raft_base_layer_nr = -Raft::getTotalExtraLayers(); + const LayerIndex raft_base_layer_nr = -LayerIndex(Raft::getTotalExtraLayers()); Raft::LayerType layer_type = Raft::getLayerType(layer_nr); if (layer_type == Raft::RaftBase) @@ -1827,8 +1827,7 @@ void FffGcodeWriter::addMeshPartToGCode( 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() < LayerIndex(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) @@ -2929,27 +2928,25 @@ bool FffGcodeWriter::processInsets( bool spiralize = false; if (Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("magic_spiralize")) { - 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 - || (layer_nr >= static_cast(initial_bottom_layers) && part.spiral_wall.empty())) // The rest of the layers in spiralize mode are using the spiral wall + const auto initial_bottom_layers = LayerIndex(mesh.settings.get("initial_bottom_layers")); + const auto layer_nr = gcode_layer.getLayerNr(); + if ((layer_nr < initial_bottom_layers && part.wall_toolpaths.empty()) // The bottom layers in spiralize mode are generated using the variable width paths + || (layer_nr >= initial_bottom_layers && part.spiral_wall.empty())) // The rest of the layers in spiralize mode are using the spiral wall { // nothing to do return false; } - if (gcode_layer.getLayerNr() >= static_cast(initial_bottom_layers)) + if (gcode_layer.getLayerNr() >= initial_bottom_layers) { 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() == 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 // start this first wall at the same vertex the spiral starts const Polygon& spiral_inset = part.spiral_wall[0]; - const size_t spiral_start_vertex = storage.spiralize_seam_vertex_indices[initial_bottom_layers]; + const size_t spiral_start_vertex = storage.spiralize_seam_vertex_indices[static_cast(initial_bottom_layers.value)]; if (spiral_start_vertex < spiral_inset.size()) { gcode_layer.addTravel(spiral_inset[spiral_start_vertex]); diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 10a1336995..0a0351e460 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -557,7 +557,7 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const siz coord_t surface_line_width = mesh.settings.get("wall_line_width_0"); mesh.layer_nr_max_filled_layer = -1; - for (LayerIndex layer_idx = 0; layer_idx < static_cast(mesh.layers.size()); layer_idx++) + for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(mesh.layers.size()); layer_idx++) { SliceLayer& layer = mesh.layers[layer_idx]; @@ -585,7 +585,7 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const siz break; // all previous meshes have been processed } SliceMeshStorage& other_mesh = *storage.meshes[other_mesh_idx]; - if (layer_idx >= static_cast(other_mesh.layers.size())) + if (layer_idx >= LayerIndex(other_mesh.layers.size())) { // there can be no interaction between the infill mesh and this other non-infill mesh continue; } @@ -875,7 +875,7 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage } for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { - for (LayerIndex layer_nr = static_cast(mesh.layers.size()) - 1; layer_nr > max_print_height_per_extruder[extruder_nr]; layer_nr--) + for (LayerIndex layer_nr = LayerIndex(mesh.layers.size()) - 1; layer_nr > max_print_height_per_extruder[extruder_nr]; layer_nr--) { if (mesh.getExtruderIsUsed(extruder_nr, layer_nr)) { @@ -1097,7 +1097,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) return false; }; - for (LayerIndex layer_nr = start_layer_nr; layer_nr < mesh.layers.size(); layer_nr++) + for (LayerIndex layer_nr = start_layer_nr; layer_nr < LayerIndex(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 abfab67ae7..92d921e0c4 100644 --- a/src/InterlockingGenerator.cpp +++ b/src/InterlockingGenerator.cpp @@ -222,10 +222,10 @@ void InterlockingGenerator::addBoundaryCells(const std::vector& layers, c 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. + const auto 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 (LayerIndex layer_nr = 0; layer_nr < max_layer_count; layer_nr++) + for (LayerIndex layer_nr = 0; layer_nr < LayerIndex(max_layer_count); layer_nr++) { Shape& layer_region = layer_regions[static_cast(layer_nr)]; for (Slicer* mesh : { &mesh_a_, &mesh_b_ }) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 029fb038c5..35b18b5ed9 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2236,7 +2236,7 @@ void LayerPlan::processFanSpeedAndMinimalLayerTime(Point2LL starting_position) void LayerPlan::writeGCode(GCodeExport& gcode) { - Communication* communication = Application::getInstance().communication_; + auto communication = Application::getInstance().communication_; communication->setLayerForSend(layer_nr_); communication->sendCurrentPosition(gcode.getPosition()); gcode.setLayerNr(layer_nr_); diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index e6c14098a5..14867fa962 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -471,7 +471,7 @@ void ArcusCommunication::sliceNext() // Handle the main Slice message. const cura::proto::Slice* slice_message = dynamic_cast(message.get()); // See if the message is of the message type Slice. Returns nullptr otherwise. - if (! slice_message) + if (slice_message == nullptr) { return; } @@ -504,15 +504,15 @@ void ArcusCommunication::sliceNext() } #endif // ENABLE_PLUGINS - Slice slice(slice_message->object_lists().size()); - Application::getInstance().current_slice_ = &slice; + auto slice = std::make_shared(slice_message->object_lists().size()); + Application::getInstance().current_slice_ = slice; private_data->readGlobalSettingsMessage(slice_message->global_settings()); private_data->readExtruderSettingsMessage(slice_message->extruders()); // Broadcast the settings to the plugins slots::instance().broadcast(*slice_message); - const size_t extruder_count = slice.scene.extruders.size(); + 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). for (const cura::proto::SettingExtruder& setting_extruder : slice_message->limit_to_extruder()) @@ -523,8 +523,8 @@ void ArcusCommunication::sliceNext() // If it's -1 it should be ignored as per the spec. Let's also ignore it if it's beyond range. continue; } - ExtruderTrain& extruder = slice.scene.extruders[setting_extruder.extruder()]; - slice.scene.limit_to_extruder.emplace(setting_extruder.name(), &extruder); + ExtruderTrain& extruder = slice->scene.extruders[setting_extruder.extruder()]; + slice->scene.limit_to_extruder.emplace(setting_extruder.name(), &extruder); } // Load all mesh groups, meshes and their settings. @@ -535,9 +535,9 @@ void ArcusCommunication::sliceNext() } spdlog::debug("Done reading Slice message."); - if (! slice.scene.mesh_groups.empty()) + if (! slice->scene.mesh_groups.empty()) { - slice.compute(); + slice->compute(); FffProcessor::getInstance()->finalize(); flushGCode(); sendPrintTimeMaterialEstimates(); diff --git a/src/communication/ArcusCommunicationPrivate.cpp b/src/communication/ArcusCommunicationPrivate.cpp index 81101dc9b2..44621918ca 100644 --- a/src/communication/ArcusCommunicationPrivate.cpp +++ b/src/communication/ArcusCommunicationPrivate.cpp @@ -47,7 +47,7 @@ std::shared_ptr ArcusCommunication::Private::getOptimized void ArcusCommunication::Private::readGlobalSettingsMessage(const proto::SettingList& global_settings_message) { - Slice* slice = Application::getInstance().current_slice_; + auto slice = Application::getInstance().current_slice_; for (const cura::proto::Setting& setting_message : global_settings_message.settings()) { slice->scene.settings.add(setting_message.name(), setting_message.value()); @@ -57,7 +57,7 @@ void ArcusCommunication::Private::readGlobalSettingsMessage(const proto::Setting void ArcusCommunication::Private::readExtruderSettingsMessage(const google::protobuf::RepeatedPtrField& extruder_messages) { // Make sure we have enough extruders added currently. - Slice* slice = Application::getInstance().current_slice_; + auto slice = Application::getInstance().current_slice_; const size_t extruder_count = slice->scene.settings.get("machine_extruder_count"); for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 38a8f16734..e5f8b4340a 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -32,10 +32,6 @@ #include "utils/format/filesystem_path.h" #include "utils/views/split_paths.h" -#ifdef __EMSCRIPTEN__ -#include -#endif - namespace cura { @@ -118,13 +114,6 @@ 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() @@ -140,16 +129,16 @@ void CommandLine::sliceNext() num_mesh_groups++; } } - Slice slice(num_mesh_groups); - Application::getInstance().current_slice_ = &slice; + Application::getInstance().current_slice_ = std::make_shared(num_mesh_groups); + auto slice = Application::getInstance().current_slice_; size_t mesh_group_index = 0; - Settings* last_settings = &slice.scene.settings; + Settings* last_settings = &slice->scene.settings; - 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.data(); + 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.data(); bool force_read_parent = false; bool force_read_nondefault = false; @@ -169,7 +158,7 @@ void CommandLine::sliceNext() mesh_group_index++; FffProcessor::getInstance()->time_keeper.restart(); - last_settings = &slice.scene.mesh_groups[mesh_group_index].settings; + last_settings = &slice->scene.mesh_groups[mesh_group_index].settings; } catch (...) { @@ -197,15 +186,12 @@ void CommandLine::sliceNext() force_read_parent = false; force_read_nondefault = false; } -#ifdef __EMSCRIPTEN__ - else if (argument.find("--progress") == 0) + else if (argument.starts_with("--progress_cb") || argument.starts_with("--slice_info_cb") || argument.starts_with("--gcode_header_cb")) { - // Store progress handler name + // Unused in command line slicing, but used in EmscriptenCommunication. argument_index++; argument = arguments_[argument_index]; - progressHandler = argument; } -#endif else { spdlog::error("Unknown option: {}", argument); @@ -260,12 +246,12 @@ void CommandLine::sliceNext() } // If this was the global stack, create extruders for the machine_extruder_count setting. - if (last_settings == &slice.scene.settings) + if (last_settings == &slice->scene.settings) { - const auto extruder_count = slice.scene.settings.get("machine_extruder_count"); - while (slice.scene.extruders.size() < 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); + slice->scene.extruders.emplace_back(slice->scene.extruders.size(), &slice->scene.settings); } } // If this was an extruder stack, make sure that the extruder_nr setting is correct. @@ -278,13 +264,13 @@ void CommandLine::sliceNext() case 'e': { size_t extruder_nr = stoul(argument.substr(2)); - while (slice.scene.extruders.size() <= extruder_nr) // Make sure we have enough extruders up to the extruder_nr that the user wanted. + while (slice->scene.extruders.size() <= extruder_nr) // Make sure we have enough extruders up to the extruder_nr that the user wanted. { - slice.scene.extruders.emplace_back(extruder_nr, &slice.scene.settings); + slice->scene.extruders.emplace_back(extruder_nr, &slice->scene.settings); } - last_settings = &slice.scene.extruders[extruder_nr].settings_; + last_settings = &slice->scene.extruders[extruder_nr].settings_; last_settings->add("extruder_nr", argument.substr(2)); - last_extruder = &slice.scene.extruders[extruder_nr]; + last_extruder = &slice->scene.extruders[extruder_nr]; break; } case 'l': @@ -299,14 +285,14 @@ void CommandLine::sliceNext() 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_)) + if (! loadMeshIntoMeshGroup(&slice->scene.mesh_groups[mesh_group_index], argument.c_str(), transformation, last_extruder->settings_)) { spdlog::error("Failed to load model: {}. (error number {})", argument, errno); exit(1); } else { - last_settings = &slice.scene.mesh_groups[mesh_group_index].meshes.back().settings_; + last_settings = &slice->scene.mesh_groups[mesh_group_index].meshes.back().settings_; } break; } @@ -328,7 +314,7 @@ void CommandLine::sliceNext() } case 'g': { - last_settings = &slice.scene.mesh_groups[mesh_group_index].settings; + last_settings = &slice->scene.mesh_groups[mesh_group_index].settings; break; } /* ... falls through ... */ @@ -426,49 +412,45 @@ void CommandLine::sliceNext() for (const auto& [setting_key, setting_value] : global_settings) { - slice.scene.settings.add(setting_key, setting_value); + slice->scene.settings.add(setting_key, setting_value); } for (const auto& [key, values] : extruder_settings) { const auto extruder_nr = std::stoi(key.substr(extruder_identifier.size())); - while (slice.scene.extruders.size() <= static_cast(extruder_nr)) + while (slice->scene.extruders.size() <= static_cast(extruder_nr)) { - slice.scene.extruders.emplace_back(extruder_nr, &slice.scene.settings); + slice->scene.extruders.emplace_back(extruder_nr, &slice->scene.settings); } for (const auto& [setting_key, setting_value] : values) { - slice.scene.extruders[extruder_nr].settings_.add(setting_key, setting_value); + slice->scene.extruders[extruder_nr].settings_.add(setting_key, setting_value); } } for (const auto& [key, values] : model_settings) { const auto& model_name = key; - - cura::MeshGroup mesh_group; for (const auto& [setting_key, setting_value] : values) { - mesh_group.settings.add(setting_key, setting_value); + slice->scene.mesh_groups[mesh_group_index].settings.add(setting_key, setting_value); } - const auto transformation = mesh_group.settings.get("mesh_rotation_matrix"); - const auto extruder_nr = mesh_group.settings.get("extruder_nr"); + const auto transformation = slice->scene.mesh_groups[mesh_group_index].settings.get("mesh_rotation_matrix"); + const auto extruder_nr = slice->scene.mesh_groups[mesh_group_index].settings.get("extruder_nr"); - if (! loadMeshIntoMeshGroup(&mesh_group, model_name.c_str(), transformation, slice.scene.extruders[extruder_nr].settings_)) + if (! loadMeshIntoMeshGroup(&slice->scene.mesh_groups[mesh_group_index], model_name.c_str(), transformation, slice->scene.extruders[extruder_nr].settings_)) { spdlog::error("Failed to load model: {}. (error number {})", model_name, errno); exit(1); } - - slice.scene.mesh_groups.push_back(std::move(mesh_group)); } for (const auto& [key, value] : limit_to_extruder) { const auto extruder_nr = std::stoi(value.substr(extruder_identifier.size())); if (extruder_nr >= 0) { - slice.scene.limit_to_extruder[key] = &slice.scene.extruders[extruder_nr]; + slice->scene.limit_to_extruder[key] = &slice->scene.extruders[extruder_nr]; } } @@ -499,11 +481,11 @@ void CommandLine::sliceNext() try { #endif // DEBUG - slice.scene.mesh_groups[mesh_group_index].finalize(); + slice->scene.mesh_groups[mesh_group_index].finalize(); spdlog::info("Loaded from disk in {:3}s\n", FffProcessor::getInstance()->time_keeper.restart()); // Start slicing. - slice.compute(); + slice->compute(); #ifndef DEBUG } catch (...) diff --git a/src/communication/EmscriptenCommunication.cpp b/src/communication/EmscriptenCommunication.cpp new file mode 100644 index 0000000000..8155290ee9 --- /dev/null +++ b/src/communication/EmscriptenCommunication.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifdef __EMSCRIPTEN__ +#include "communication/EmscriptenCommunication.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Application.h" +#include "FffProcessor.h" +#include "Slice.h" +#include "utils/string.h" + +namespace cura +{ + +EmscriptenCommunication::EmscriptenCommunication(const std::vector& arguments) + : CommandLine(arguments) +{ + spdlog::info("Emscripten communication initialized"); + if (auto progress_flag = ranges::find(arguments_, "--progress_cb"); progress_flag != arguments_.end()) + { + progress_handler_ = *ranges::next(progress_flag); + } + if (auto slice_info_flag = ranges::find(arguments_, "--slice_info_cb"); slice_info_flag != arguments_.end()) + { + slice_info_handler_ = *ranges::next(slice_info_flag); + } + if (auto gcode_header_flag = ranges::find(arguments_, "--gcode_header_cb"); gcode_header_flag != arguments_.end()) + { + gcode_header_handler_ = *ranges::next(gcode_header_flag); + } +} + +void EmscriptenCommunication::sendGCodePrefix(const std::string& prefix) const +{ + emscripten_run_script(fmt::format("globalThis[\"{}\"](\"{}\")", gcode_header_handler_, convertTobase64(prefix)).c_str()); +} + +void EmscriptenCommunication::sendProgress(double progress) const +{ + emscripten_run_script(fmt::format("globalThis[\"{}\"]({})", progress_handler_, progress).c_str()); +} + +std::string EmscriptenCommunication::createSliceInfoMessage() +{ + // Construct a string with rapidjson containing the slice information + rapidjson::Document doc; + auto& allocator = doc.GetAllocator(); + doc.SetObject(); + + // Set the slice UUID + rapidjson::Value slice_uuid("slice_uuid", allocator); + rapidjson::Value uuid(Application::getInstance().instance_uuid_.c_str(), allocator); + doc.AddMember(slice_uuid, uuid, allocator); + + // Set the time estimates + rapidjson::Value time_estimates_json(rapidjson::kObjectType); + auto time_estimates = FffProcessor::getInstance()->getTotalPrintTimePerFeature(); + for (const auto& [feature, duration_idx] : std::vector>{ { "infill", PrintFeatureType::Infill }, + { "skin", PrintFeatureType::Skin }, + { "support", PrintFeatureType::Support }, + { "inner_wall", PrintFeatureType::InnerWall }, + { "move_combing", PrintFeatureType::MoveCombing }, + { "move_retraction", PrintFeatureType::MoveRetraction }, + { "outer_wall", PrintFeatureType::OuterWall }, + { "prime_tower", PrintFeatureType::PrimeTower }, + { "skirt_brim", PrintFeatureType::SkirtBrim }, + { "support_infill", PrintFeatureType::SupportInfill }, + { "support_interface", PrintFeatureType::SupportInterface } }) + { + rapidjson::Value feature_time(feature.c_str(), allocator); + rapidjson::Value feature_duration(time_estimates[static_cast(duration_idx)]); + time_estimates_json.AddMember(feature_time, feature_duration, allocator); + } + doc.AddMember("time_estimates", time_estimates_json, allocator); + + // Set the material estimates + rapidjson::Value material_estimates_json(rapidjson::kObjectType); + const Scene& scene = Application::getInstance().current_slice_->scene; + + for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice_->scene.extruders.size(); extruder_nr++) + { + const double value = FffProcessor::getInstance()->getTotalFilamentUsed(static_cast(extruder_nr)); + spdlog::info("Extruder {} used {} [mm] of filament", extruder_nr, value); + rapidjson::Value extruder_id(fmt::format("{}", extruder_nr).c_str(), allocator); + rapidjson::Value extruder_material_estimate(value); + material_estimates_json.AddMember(extruder_id, extruder_material_estimate, allocator); + } + doc.AddMember("material_estimates", material_estimates_json, allocator); + + // Set CuraEngine information + rapidjson::Value slicer_info_json(rapidjson::kObjectType); + rapidjson::Value slicer_version(CURA_ENGINE_VERSION, allocator); + doc.AddMember("slicer_info", slicer_info_json, allocator); + + // Serialize the JSON document to a string + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + doc.Accept(writer); + return buffer.GetString(); +} + +void EmscriptenCommunication::sliceNext() +{ + CommandLine::sliceNext(); + auto slice_info = createSliceInfoMessage(); + emscripten_run_script(fmt::format("globalThis[\"{}\"]({})", slice_info_handler_, slice_info).c_str()); +}; + +} // namespace cura + +#endif // __EMSCRIPTEN__ diff --git a/src/slicer.cpp b/src/slicer.cpp index f66e2d40ec..aa8a4c64e9 100644 --- a/src/slicer.cpp +++ b/src/slicer.cpp @@ -1017,13 +1017,13 @@ void Slicer::makePolygons(Mesh& mesh, SlicingTolerance slicing_tolerance, std::v switch (slicing_tolerance) { case SlicingTolerance::INCLUSIVE: - for (LayerIndex layer_nr = 0; layer_nr + 1 < layers.size(); layer_nr++) + for (size_t 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 (LayerIndex layer_nr = 0; layer_nr + 1 < layers.size(); layer_nr++) + for (size_t 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/tests/FffGcodeWriterTest.cpp b/tests/FffGcodeWriterTest.cpp index 87b20cb64e..a3c52d6509 100644 --- a/tests/FffGcodeWriterTest.cpp +++ b/tests/FffGcodeWriterTest.cpp @@ -54,12 +54,12 @@ class FffGcodeWriterTest : public testing::Test inner_square.back().emplace_back(MM2INT(60), MM2INT(60)); inner_square.back().emplace_back(MM2INT(10), MM2INT(60)); - Application::getInstance().communication_ = new MockCommunication(); + Application::getInstance().communication_ = std::make_shared(); } SliceDataStorage* setUpStorage() { - Application::getInstance().current_slice_ = new Slice(1); + Application::getInstance().current_slice_ = std::make_shared(1); // Define all settings in the mesh group. The extruder train and model settings will fall back on that then. settings = &Application::getInstance().current_slice_->scene.settings; @@ -100,7 +100,7 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) { // SETUP SliceDataStorage* storage = setUpStorage(); - + // Set the fan speed layer time settings (since the LayerPlan constructor copies these). FanSpeedLayerTimeSettings fan_settings; fan_settings.cool_min_layer_time = settings->get("cool_min_layer_time"); @@ -110,7 +110,7 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) fan_settings.cool_fan_speed_max = settings->get("cool_fan_speed_max"); fan_settings.cool_min_speed = settings->get("cool_min_speed"); fan_settings.cool_fan_full_layer = settings->get("cool_fan_full_layer"); - + Mesh mesh(*settings); LayerPlan gcode_layer(*storage, 100, 10000, 100, 0, {fan_settings}, 20, 10, 5000 ); @@ -165,7 +165,7 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) } return false; }; - + // Check the results for (auto poly:inner_square) for (auto point:poly) diff --git a/tests/GCodeExportTest.cpp b/tests/GCodeExportTest.cpp index e2d7ef15e0..aa5d0620b0 100644 --- a/tests/GCodeExportTest.cpp +++ b/tests/GCodeExportTest.cpp @@ -38,7 +38,7 @@ class GCodeExportTest : public testing::Test * Mock away the communication channel where layer data is output by this * class. */ - MockCommunication* mock_communication; + std::shared_ptr mock_communication; void SetUp() override { @@ -68,15 +68,13 @@ class GCodeExportTest : public testing::Test gcode.machine_name_ = "Your favourite 3D printer"; // Set up a scene so that we may request settings. - Application::getInstance().current_slice_ = new Slice(1); - mock_communication = new MockCommunication(); + Application::getInstance().current_slice_ = std::make_shared(1); + mock_communication = std::make_shared(); Application::getInstance().communication_ = mock_communication; } void TearDown() override { - delete Application::getInstance().current_slice_; - delete Application::getInstance().communication_; Application::getInstance().communication_ = nullptr; } }; @@ -224,12 +222,7 @@ class GriffinHeaderTest : public testing::TestWithParam gcode.machine_name_ = "Your favourite 3D printer"; // Set up a scene so that we may request settings. - Application::getInstance().current_slice_ = new Slice(0); - } - - void TearDown() override - { - delete Application::getInstance().current_slice_; + Application::getInstance().current_slice_ = std::make_shared(0); } }; // NOLINTEND(misc-non-private-member-variables-in-classes) diff --git a/tests/LayerPlanTest.cpp b/tests/LayerPlanTest.cpp index da6a783644..112f3d457f 100644 --- a/tests/LayerPlanTest.cpp +++ b/tests/LayerPlanTest.cpp @@ -80,7 +80,7 @@ class LayerPlanTest : public testing::Test SliceDataStorage* setUpStorage() { constexpr size_t num_mesh_groups = 1; - Application::getInstance().current_slice_ = new Slice(num_mesh_groups); + Application::getInstance().current_slice_ = std::make_shared(num_mesh_groups); // Define all settings in the mesh group. The extruder train and model settings will fall back on that then. settings = &Application::getInstance().current_slice_->scene.current_mesh_group->settings; @@ -228,7 +228,6 @@ class LayerPlanTest : public testing::Test void TearDown() override { delete storage; - delete Application::getInstance().current_slice_; } }; diff --git a/tests/arcus/ArcusCommunicationPrivateTest.cpp b/tests/arcus/ArcusCommunicationPrivateTest.cpp index 12652958ca..40b651250a 100644 --- a/tests/arcus/ArcusCommunicationPrivateTest.cpp +++ b/tests/arcus/ArcusCommunicationPrivateTest.cpp @@ -34,15 +34,13 @@ class ArcusCommunicationPrivateTest : public testing::Test { instance = new ArcusCommunication::Private(); instance->socket = new MockSocket(); - Application::getInstance().current_slice_ = new Slice(GK_TEST_NUM_MESH_GROUPS); + Application::getInstance().current_slice_ = std::make_shared(GK_TEST_NUM_MESH_GROUPS); } void TearDown() override { delete instance->socket; delete instance; - - delete Application::getInstance().current_slice_; } /* diff --git a/tests/arcus/ArcusCommunicationTest.cpp b/tests/arcus/ArcusCommunicationTest.cpp index a05b88d98d..3c0eb20d37 100644 --- a/tests/arcus/ArcusCommunicationTest.cpp +++ b/tests/arcus/ArcusCommunicationTest.cpp @@ -14,6 +14,7 @@ #include "geometry/Shape.h" #include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" +#include "utils/string.h" // NOLINTBEGIN(*-magic-numbers) namespace cura @@ -123,15 +124,16 @@ TEST_F(ArcusCommunicationTest, HasSlice) TEST_F(ArcusCommunicationTest, SendGCodePrefix) { - const std::string& prefix = "bladibla"; + const std::string prefix = ";bladiblhjvouyvu\n;iuboua"; + const std::string& encoded_prefix = convertTobase64(prefix); - ac->sendGCodePrefix(prefix); + ac->sendGCodePrefix(encoded_prefix); ac->flushGCode(); EXPECT_GT(socket->sent_messages.size(), 0); bool found_prefix = false; for (const auto& message : socket->sent_messages) { - if (message->DebugString().find(prefix) != std::string::npos) + if (message->DebugString().find(encoded_prefix) != std::string::npos) { found_prefix = true; break; diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index cfb9557024..763873bcd8 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -31,7 +31,7 @@ class SlicePhaseTest : public testing::Test Application::getInstance().startThreadPool(); // Set up a scene so that we may request settings. - Application::getInstance().current_slice_ = new Slice(1); + Application::getInstance().current_slice_ = std::make_shared(1); // And a few settings that we want to default. Scene& scene = Application::getInstance().current_slice_->scene; diff --git a/tests/settings/SettingsTest.cpp b/tests/settings/SettingsTest.cpp index 0c6c98f458..59a60a3528 100644 --- a/tests/settings/SettingsTest.cpp +++ b/tests/settings/SettingsTest.cpp @@ -87,8 +87,8 @@ class Slice; // Forward declaration to save some time compiling. TEST_F(SettingsTest, AddSettingExtruderTrain) { // Add a slice with some extruder trains. - std::shared_ptr current_slice = std::make_shared(0); - Application::getInstance().current_slice_ = current_slice.get(); + auto current_slice = std::make_shared(0); + Application::getInstance().current_slice_ = current_slice; current_slice->scene.extruders.emplace_back(0, nullptr); current_slice->scene.extruders.emplace_back(1, nullptr); current_slice->scene.extruders.emplace_back(2, nullptr); @@ -223,7 +223,7 @@ TEST_F(SettingsTest, OverwriteSetting) TEST_F(SettingsTest, Inheritance) { std::shared_ptr current_slice = std::make_shared(0); - Application::getInstance().current_slice_ = current_slice.get(); + Application::getInstance().current_slice_ = current_slice; const std::string value = "To be frank, I'd have to change my name."; Settings parent; @@ -240,7 +240,7 @@ TEST_F(SettingsTest, Inheritance) TEST_F(SettingsTest, LimitToExtruder) { std::shared_ptr current_slice = std::make_shared(0); - Application::getInstance().current_slice_ = current_slice.get(); + Application::getInstance().current_slice_ = current_slice; current_slice->scene.extruders.emplace_back(0, nullptr); current_slice->scene.extruders.emplace_back(1, nullptr); current_slice->scene.extruders.emplace_back(2, nullptr); diff --git a/tests/utils/MathTest.cpp b/tests/utils/MathTest.cpp new file mode 100644 index 0000000000..71c5e8c8f9 --- /dev/null +++ b/tests/utils/MathTest.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2022 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "utils/math.h" + +#include + +#include + +namespace cura +{ +// NOLINTBEGIN(*-magic-numbers) + +// Test cases for the square function +TEST(MathTest, TestSquare) +{ + EXPECT_EQ(square(2), 4) << "Square of 2 should be 4."; + EXPECT_EQ(square(-3), 9) << "Square of -3 should be 9."; + EXPECT_EQ(square(0), 0) << "Square of 0 should be 0."; + EXPECT_EQ(square(1.5), 2.25) << "Square of 1.5 should be 2.25."; +} + +// Test cases for the round_divide_signed function +TEST(MathTest, TestRoundDivideSigned) +{ + EXPECT_EQ(round_divide_signed(10, 3), 3) << "10 / 3 rounded should be 3."; + EXPECT_EQ(round_divide_signed(10, -3), -3) << "10 / -3 rounded should be -3."; + EXPECT_EQ(round_divide_signed(-10, 3), -3) << "-10 / 3 rounded should be -3."; + EXPECT_EQ(round_divide_signed(-10, -3), 3) << "-10 / -3 rounded should be 3."; +} + +// Test cases for the ceil_divide_signed function +TEST(MathTest, TestCeilDivideSigned) +{ + EXPECT_EQ(ceil_divide_signed(10, 3), 4) << "10 / 3 rounded up should be 4."; + EXPECT_EQ(ceil_divide_signed(10, -3), -3) << "10 / -3 rounded up should be -3."; + EXPECT_EQ(ceil_divide_signed(-10, 3), -3) << "-10 / 3 rounded up should be -3."; + EXPECT_EQ(ceil_divide_signed(-10, -3), 4) << "-10 / -3 rounded up should be 4."; +} + +// Test cases for the floor_divide_signed function +TEST(MathTest, TestFloorDivideSigned) +{ + EXPECT_EQ(floor_divide_signed(10, 3), 3) << "10 / 3 rounded down should be 3."; + EXPECT_EQ(floor_divide_signed(10, -3), -4) << "10 / -3 rounded down should be -4."; + EXPECT_EQ(floor_divide_signed(-10, 3), -4) << "-10 / 3 rounded down should be -4."; + EXPECT_EQ(floor_divide_signed(-10, -3), 3) << "-10 / -3 rounded down should be 3."; +} + +// Test cases for the round_divide function +TEST(MathTest, TestRoundDivide) +{ + EXPECT_EQ(round_divide(10, 3), 3) << "10 / 3 rounded should be 3."; + EXPECT_EQ(round_divide(11, 3), 4) << "11 / 3 rounded should be 4."; + EXPECT_EQ(round_divide(9, 3), 3) << "9 / 3 rounded should be 3."; + +} + +// Test cases for the round_up_divide function +TEST(MathTest, TestRoundUpDivide) +{ + EXPECT_EQ(round_up_divide(10, 3), 4) << "10 / 3 rounded up should be 4."; + EXPECT_EQ(round_up_divide(9, 3), 3) << "9 / 3 rounded up should be 3."; + EXPECT_EQ(round_up_divide(1, 1), 1) << "1 / 1 rounded up should be 1."; +} + +// NOLINTEND(*-magic-numbers) + +} // namespace cura