WIP G-code find & replace: Unit tests and some bug fixes.
This commit is contained in:
parent
2da3d80a62
commit
aa62868ccb
@ -5,29 +5,47 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
GCodeFindReplace::GCodeFindReplace(const PrintConfig &print_config)
|
// Similar to https://npp-user-manual.org/docs/searching/#extended-search-mode
|
||||||
|
const void unescape_extended_search_mode(std::string &s)
|
||||||
{
|
{
|
||||||
const std::vector<std::string> &subst = print_config.gcode_substitutions.values;
|
boost::replace_all(s, "\\n", "\n"); // Line Feed control character LF (ASCII 0x0A)
|
||||||
|
boost::replace_all(s, "\\r", "\r"); // Carriage Return control character CR (ASCII 0x0D)
|
||||||
|
boost::replace_all(s, "\\t", "\t"); // TAB control character (ASCII 0x09)
|
||||||
|
boost::replace_all(s, "\\0", "\0x00"); // NUL control character (ASCII 0x00)
|
||||||
|
boost::replace_all(s, "\\\\", "\\"); // Backslash character (ASCII 0x5C)
|
||||||
|
|
||||||
if ((subst.size() % 3) != 0)
|
// Notepad++ also supports:
|
||||||
|
// \o: the octal representation of a byte, made of 3 digits in the 0-7 range
|
||||||
|
// \d: the decimal representation of a byte, made of 3 digits in the 0-9 range
|
||||||
|
// \x: the hexadecimal representation of a byte, made of 2 digits in the 0-9, A-F/a-f range.
|
||||||
|
// \u: The hexadecimal representation of a two-byte character, made of 4 digits in the 0-9, A-F/a-f range.
|
||||||
|
}
|
||||||
|
|
||||||
|
GCodeFindReplace::GCodeFindReplace(const std::vector<std::string> &gcode_substitutions)
|
||||||
|
{
|
||||||
|
if ((gcode_substitutions.size() % 3) != 0)
|
||||||
throw RuntimeError("Invalid length of gcode_substitutions parameter");
|
throw RuntimeError("Invalid length of gcode_substitutions parameter");
|
||||||
|
|
||||||
m_substitutions.reserve(subst.size() / 3);
|
m_substitutions.reserve(gcode_substitutions.size() / 3);
|
||||||
for (size_t i = 0; i < subst.size(); i += 3) {
|
for (size_t i = 0; i < gcode_substitutions.size(); i += 3) {
|
||||||
Substitution out;
|
Substitution out;
|
||||||
try {
|
try {
|
||||||
out.plain_pattern = subst[i];
|
out.plain_pattern = gcode_substitutions[i];
|
||||||
out.format = subst[i + 1];
|
out.format = gcode_substitutions[i + 1];
|
||||||
const std::string ¶ms = subst[i + 2];
|
const std::string ¶ms = gcode_substitutions[i + 2];
|
||||||
out.regexp = strchr(params.c_str(), 'r') != nullptr || strchr(params.c_str(), 'R') != nullptr;
|
out.regexp = strchr(params.c_str(), 'r') != nullptr || strchr(params.c_str(), 'R') != nullptr;
|
||||||
out.case_insensitive = strchr(params.c_str(), 'i') != nullptr || strchr(params.c_str(), 'I') != nullptr;
|
out.case_insensitive = strchr(params.c_str(), 'i') != nullptr || strchr(params.c_str(), 'I') != nullptr;
|
||||||
out.whole_word = strchr(params.c_str(), 'w') != nullptr || strchr(params.c_str(), 'W') != nullptr;
|
out.whole_word = strchr(params.c_str(), 'w') != nullptr || strchr(params.c_str(), 'W') != nullptr;
|
||||||
if (out.regexp)
|
if (out.regexp) {
|
||||||
out.regexp_pattern.assign(
|
out.regexp_pattern.assign(
|
||||||
out.whole_word ?
|
out.whole_word ?
|
||||||
std::string("\b") + out.plain_pattern + "\b" :
|
std::string("\\b") + out.plain_pattern + "\\b" :
|
||||||
out.plain_pattern,
|
out.plain_pattern,
|
||||||
(out.case_insensitive ? boost::regex::icase : 0) | boost::regex::optimize);
|
(out.case_insensitive ? boost::regex::icase : 0) | boost::regex::optimize);
|
||||||
|
} else {
|
||||||
|
unescape_extended_search_mode(out.plain_pattern);
|
||||||
|
unescape_extended_search_mode(out.format);
|
||||||
|
}
|
||||||
} catch (const std::exception &ex) {
|
} catch (const std::exception &ex) {
|
||||||
throw RuntimeError(std::string("Invalid gcode_substitutions parameter, failed to compile regular expression: ") + ex.what());
|
throw RuntimeError(std::string("Invalid gcode_substitutions parameter, failed to compile regular expression: ") + ex.what());
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ namespace Slic3r {
|
|||||||
|
|
||||||
class GCodeFindReplace {
|
class GCodeFindReplace {
|
||||||
public:
|
public:
|
||||||
GCodeFindReplace(const PrintConfig &print_config);
|
GCodeFindReplace(const PrintConfig &print_config) : GCodeFindReplace(print_config.gcode_substitutions.values) {}
|
||||||
|
GCodeFindReplace(const std::vector<std::string> &gcode_substitutions);
|
||||||
|
|
||||||
|
|
||||||
std::string process_layer(const std::string &gcode);
|
std::string process_layer(const std::string &gcode);
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_fill.cpp
|
test_fill.cpp
|
||||||
test_flow.cpp
|
test_flow.cpp
|
||||||
test_gcode.cpp
|
test_gcode.cpp
|
||||||
|
test_gcodefindreplace.cpp
|
||||||
test_gcodewriter.cpp
|
test_gcodewriter.cpp
|
||||||
test_model.cpp
|
test_model.cpp
|
||||||
test_print.cpp
|
test_print.cpp
|
||||||
|
218
tests/fff_print/test_gcodefindreplace.cpp
Normal file
218
tests/fff_print/test_gcodefindreplace.cpp
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "libslic3r/GCode/FindReplace.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
SCENARIO("Find/Replace with plain text", "[GCodeFindReplace]") {
|
||||||
|
GIVEN("G-code") {
|
||||||
|
const std::string gcode =
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
"G1 Z1; move up\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n";
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move up", "move down", "" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", case insensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move up", "move down", "i" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move UP\" with \"move down\", case insensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move UP", "move down", "i" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move UP", "move down", "" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whole word
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move up", "move down", "w" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move u\" with \"move down\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move u", "move down", "w" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
WHEN("Replace \"ove up\" with \"move down\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move u", "move down", "w" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-line replace
|
||||||
|
WHEN("Replace \"move up\\nG1 X0 \" with \"move down\\nG0 X1 \"") {
|
||||||
|
GCodeFindReplace find_replace({ "move up\\nG1 X0 ", "move down\\nG0 X1 ", "" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G0 X1 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
// Multi-line replace, whole word.
|
||||||
|
WHEN("Replace \"move up\\nG1 X0\" with \"move down\\nG0 X1\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move up\\nG1 X0", "move down\\nG0 X1", "w" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G0 X1 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
// Multi-line replace, whole word, fails.
|
||||||
|
WHEN("Replace \"move up\\nG1 X\" with \"move down\\nG0 X\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move up\\nG1 X", "move down\\nG0 X", "w" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GIVEN("G-code with decimals") {
|
||||||
|
const std::string gcode =
|
||||||
|
"G1 Z0.123; home\n"
|
||||||
|
"G1 Z1.21; move up\n"
|
||||||
|
"G1 X0 Y.33 Z.431 E1.2; perimeter\n";
|
||||||
|
WHEN("Regular expression NOT processed in non-regex mode") {
|
||||||
|
GCodeFindReplace find_replace({ "( [XYZEF]-?)\\.([0-9]+)", "\\10.\\2", "" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("Find/Replace with regexp", "[GCodeFindReplace]") {
|
||||||
|
GIVEN("G-code") {
|
||||||
|
const std::string gcode =
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
"G1 Z1; move up\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n";
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move up", "move down", "r" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", case insensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move up", "move down", "ri" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move UP\" with \"move down\", case insensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move UP", "move down", "ri" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||||
|
GCodeFindReplace find_replace({ "move UP", "move down", "r" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whole word
|
||||||
|
WHEN("Replace \"move up\" with \"move down\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move up", "move down", "rw" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G1 X0 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
WHEN("Replace \"move u\" with \"move down\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move u", "move down", "rw" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
WHEN("Replace \"ove up\" with \"move down\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move u", "move down", "rw" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-line replace
|
||||||
|
WHEN("Replace \"move up\\nG1 X0 \" with \"move down\\nG0 X1 \"") {
|
||||||
|
GCodeFindReplace find_replace({ "move up\\nG1 X0 ", "move down\\nG0 X1 ", "r" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G0 X1 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
// Multi-line replace, whole word.
|
||||||
|
WHEN("Replace \"move up\\nG1 X0\" with \"move down\\nG0 X1\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move up\\nG1 X0", "move down\\nG0 X1", "rw" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0; home\n"
|
||||||
|
// substituted
|
||||||
|
"G1 Z1; move down\n"
|
||||||
|
"G0 X1 Y1 Z1; perimeter\n"
|
||||||
|
"G1 X13 Y32 Z1; infill\n"
|
||||||
|
"G1 X13 Y32 Z1; wipe\n");
|
||||||
|
}
|
||||||
|
// Multi-line replace, whole word, fails.
|
||||||
|
WHEN("Replace \"move up\\nG1 X\" with \"move down\\nG0 X\", whole word") {
|
||||||
|
GCodeFindReplace find_replace({ "move up\\nG1 X", "move down\\nG0 X", "rw" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GIVEN("G-code with decimals") {
|
||||||
|
const std::string gcode =
|
||||||
|
"G1 Z0.123; home\n"
|
||||||
|
"G1 Z1.21; move up\n"
|
||||||
|
"G1 X0 Y.33 Z.431 E1.2; perimeter\n";
|
||||||
|
WHEN("Missing zeros before dot filled in") {
|
||||||
|
GCodeFindReplace find_replace({ "( [XYZEF]-?)\\.([0-9]+)", "\\10.\\2", "r" });
|
||||||
|
REQUIRE(find_replace.process_layer(gcode) ==
|
||||||
|
"G1 Z0.123; home\n"
|
||||||
|
"G1 Z1.21; move up\n"
|
||||||
|
"G1 X0 Y0.33 Z0.431 E1.2; perimeter\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user