Skip to content

Commit

Permalink
semi: working on linear move scheduler.
Browse files Browse the repository at this point in the history
Signed-off-by: Bartłomiej Burdukiewicz <[email protected]>
  • Loading branch information
dev-0x7C6 committed Jun 14, 2021
1 parent 8f3db7e commit 2a7728d
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 37 deletions.
19 changes: 6 additions & 13 deletions src/gcode-generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ auto code(const instruction::move &move) -> const char * {

auto convert(const float v) -> std::string {
std::string ret;
ret.resize(std::snprintf(nullptr, 0, "%.3f", v) + 1, 0);
std::snprintf(ret.data(), ret.size(), "%.3f", v);
ret.resize(std::snprintf(nullptr, 0, "%.3f", v), 0);
std::snprintf(ret.data(), ret.size() + 1, "%.3f", v);
return ret;
}

Expand All @@ -44,17 +44,10 @@ std::string gcode::generator::grbl::operator()(const instruction::move v) const
return value;
};

if (v.x)
ret += " X" + convert(calc(v.x.value()));

if (v.y)
ret += " Y" + convert(calc(v.y.value()));

if (v.feedrate)
ret += " F" + std::to_string(v.feedrate.value());

if (v.pwr)
ret += " S" + std::to_string(v.pwr.value().duty);
ret += " X" + convert(calc(v.x));
ret += " Y" + convert(calc(v.y));
ret += " F" + std::to_string(v.feedrate);
ret += " S" + std::to_string(v.pwr.duty);

return ret;
}
Expand Down
32 changes: 26 additions & 6 deletions src/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ struct dwell {
};

struct power {
constexpr power() = default;
constexpr power() noexcept = default;
constexpr power(const i32 duty) noexcept
: duty(duty) {}
constexpr power(const power &) noexcept = default;
constexpr power(power &&) noexcept = default;
constexpr power &operator=(const power &) noexcept = default;
constexpr power &operator=(power &&) noexcept = default;

i32 duty{};

constexpr bool operator<=>(const power &rhs) const noexcept = default;
};

struct move {
Expand All @@ -47,18 +54,31 @@ struct move {
, y(y)
, scale(type){};

constexpr move(etype type, std::optional<int> feedrate = {}, std::optional<power> pwr = {}) noexcept
constexpr move(etype type, int feedrate = {}, power pwr = {}) noexcept
: feedrate(feedrate)
, type(type)
, pwr(pwr) {}

constexpr move(const move &) noexcept = default;
constexpr move(move &&) noexcept = default;

std::optional<float> x;
std::optional<float> y;
std::optional<power> pwr;
std::optional<int> feedrate;
constexpr move &operator=(const move &) noexcept = default;
constexpr move &operator=(move &&) noexcept = default;

constexpr auto is_next_move_adaptive(const move &rhs) {
bool is_adaptive{true};
is_adaptive &= (y == rhs.y);
is_adaptive &= (pwr == rhs.pwr);
is_adaptive &= (feedrate == rhs.feedrate);
is_adaptive &= (scale == rhs.scale);
is_adaptive &= (type == rhs.type);
return is_adaptive;
}

float x{};
float y{};
power pwr{0};
int feedrate{1000};
escale scale{escale::dpi};
etype type{etype::precise};
};
Expand Down
83 changes: 67 additions & 16 deletions src/semi-gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,51 @@ i32 semi::calculate::power(const int color, const options &opts) noexcept {
return pwr;
}

class x_move_scheduler {
public:
void insert(const instruction::move &rhs, const semi::options &opts) {
auto engraving_move = [&](const instruction::move &rhs) -> std::optional<std::pair<instruction::move, instruction::move>> {
if (rhs.pwr == 0)
return std::nullopt;

instruction::move s = rhs;
instruction::move e = rhs;
s.pwr = 0;
s.feedrate = opts.speed.rapid;
s.type = instruction::move::etype::rapid;
e.x += 1;
return std::make_pair(s, e);
};

if (!m_move_pair) {
m_move_pair = engraving_move(rhs);
} else {
if (m_move_pair->second.is_next_move_adaptive(rhs)) {
m_move_pair->second = rhs;
} else {
m_moves.push_back(m_move_pair->first);
m_moves.push_back(m_move_pair->second);
m_move_pair = engraving_move(rhs);
}
}
}

auto finish() {
if (m_move_pair) {
m_moves.push_back(m_move_pair->first);
m_moves.push_back(m_move_pair->second);
m_move_pair->second.pwr = 0;
m_moves.push_back(m_move_pair->second);
}

return std::move(m_moves);
}

private:
std::optional<std::pair<instruction::move, instruction::move>> m_move_pair;
std::vector<instruction::move> m_moves;
};

semi::gcodes semi::generator::from_image(const QImage &img, semi::options opts, progress_t &progress) {
raii_progress progress_raii(progress);
auto ret = initialization();
Expand All @@ -58,7 +103,7 @@ semi::gcodes semi::generator::from_image(const QImage &img, semi::options opts,
const std::optional<float> y,
const u16 pwr,
const instruction::move::etype type,
std::optional<int> feedrate = {}) {
int feedrate = {}) {
const auto [x_offset, y_offset] = offsets;
instruction::move move(type, feedrate, instruction::power(pwr));

Expand All @@ -70,15 +115,18 @@ semi::gcodes semi::generator::from_image(const QImage &img, semi::options opts,
encode(std::move(move));
};

encode(instruction::move(instruction::move::etype::rapid, opts.speed.rapid));
encode(instruction::move(instruction::move::etype::precise, opts.speed.precise));
auto offsets{center_offset(img, opts)};
const auto [x_offset, y_offset] = offsets;

auto line_count{0};

for (auto y = 0; y < img.height(); ++y) {
gcode_move({}, y, 0, instruction::move::etype::precise);

for (auto repeat = 0u; repeat <= opts.repeat_line_count; ++repeat) {
gcode_move({}, y, 0, instruction::move::etype::rapid);
auto schedule_power_off{false};
auto schedule_power_off{true};
x_move_scheduler sched;

for (auto x = 0; x < img.width(); ++x) {
const auto px = ((line_count % 2) == 0) ? x : img.width() - x - 1;
const auto pwr = semi::calculate::power(img.pixel(px, y), opts);
Expand All @@ -99,22 +147,25 @@ semi::gcodes semi::generator::from_image(const QImage &img, semi::options opts,
}

if (strategy::lines == opts.strat) {
if (pwr != 0) {
if (schedule_power_off)
gcode_move(px, {}, pwr, instruction::move::etype::precise);
else
gcode_move(px, {}, 0, instruction::move::etype::rapid);
schedule_power_off = true;
}

if (pwr == 0 && schedule_power_off)
schedule_power_off = false;
const auto type = (pwr == 0) ? instruction::move::etype::rapid :
instruction::move::etype::precise;
const auto feedrate = (pwr == 0) ? opts.speed.rapid :
opts.speed.precise;
instruction::move m(type, feedrate, pwr);
m.x = px - x_offset;
m.y = y - y_offset;

sched.insert(m, opts);
}
}

progress = divide(y, img.height());
for (auto &&moves : sched.finish())
ret.emplace_back(std::move(moves));

line_count++;
}

progress = divide(y, img.height());
}

move_gcodes(finalization(), ret);
Expand Down
4 changes: 2 additions & 2 deletions src/semi-gcode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ enum strategy {
};

struct feedrate {
std::optional<float> rapid;
std::optional<float> precise;
float rapid{2000};
float precise{1000};
};

struct options {
Expand Down
81 changes: 81 additions & 0 deletions src/ut/instruction-tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,87 @@ TEST(calculations, power) {
EXPECT_DOUBLE_EQ(semi::calculate::power(QColor(Qt::black).rgb(), opts), 1);
}

TEST(semi_move_instruction, adaptive) {
instruction::move begin;
instruction::move end;

auto is_adaptive = [&]() { return begin.is_next_move_adaptive(end); };

EXPECT_TRUE(is_adaptive());

begin.feedrate = 3000;
begin.pwr = 3000;
end.feedrate = 3000;
end.pwr = 3000;

EXPECT_TRUE(is_adaptive());

begin.feedrate = 3000;
begin.pwr = 3000;
end.feedrate = 3001;
end.pwr = 3000;

EXPECT_FALSE(is_adaptive());

begin.feedrate = 3000;
begin.pwr = 3000;
end.feedrate = 3000;
end.pwr = 3001;

EXPECT_FALSE(is_adaptive());
}

TEST(from_image, three_points) {
semi::options opts;
opts.strat = semi::strategy::lines;
progress_t progress;
QPixmap data(100, 1);
data.fill(Qt::white);
QImage line = data.toImage();
line.setPixelColor(0, 0, Qt::black);
line.setPixelColor(50, 0, Qt::black);
line.setPixelColor(99, 0, Qt::black);
auto ret = semi::generator::from_image(line, opts, progress);

gcode::generator::grbl gen;
auto cnt = std::count_if(ret.begin(), ret.end(), [&](auto &&v) {
if (std::holds_alternative<instruction::move>(v)) {
auto move = std::get<instruction::move>(v);
std::cout << std::visit(gen, v) << std::endl;
return move.pwr.duty == 255;
}

return false;
});

EXPECT_EQ(cnt, 3);
}

TEST(from_image, linear_gradient) {
semi::options opts;
opts.strat = semi::strategy::lines;
progress_t progress;
QPixmap data(256, 1);
data.fill(Qt::white);
QImage line = data.toImage();
for (int i = 0; i < 256; ++i)
line.setPixelColor(i, 0, QColor::fromRgb(i, i, i));
auto ret = semi::generator::from_image(line, opts, progress);

gcode::generator::grbl gen;
auto cnt = std::count_if(ret.begin(), ret.end(), [&](auto &&v) {
if (std::holds_alternative<instruction::move>(v)) {
auto move = std::get<instruction::move>(v);
std::cout << std::visit(gen, v) << std::endl;
return move.pwr.duty != 0;
}

return false;
});

EXPECT_EQ(cnt, 255);
}

TEST(semi_generator, from_image) {
auto progress = progress_t{};
const auto image = create_image();
Expand Down

0 comments on commit 2a7728d

Please sign in to comment.