Итак, пятница уже не первый час шагает по глобусу, поэтому держите свеженький выстрел в мозги ;)
Недавнее обсуждение «тыкательного принтера», естественно, не может не будить в пытливых умах вопрос, как бы повысить его скорость печати? Не избежал этой участи и я. Физически всё просто — надо поменьше отрывать тяжёлую ручку от бумаги и рисовать как можно более длинными штрихами. Но как разбить произвольное изображение на штрихи?
Разумеется, решение для искусственно самоограниченной задачи, когда ручка движется строго по горизонтали и бумага после каждого прохода подаётся на один диаметр шарика ручки — элементарное. Берём RLE и Флойда-Стейнберга, за 15 минут пишем этот код:
#define SQUARE(x) ((x)*(x))
#define MAXERROR 256 //for RLE
static unsigned char Grayscale8Bit[HEIGHT][WIDTH], Dithered8Bit[HEIGHT][WIDTH];
static signed short AdditionalError[2][WIDTH];
тут мы читаем из файла Grayscale8Bit, этот код я приводить не буду
memset (AdditionalError, 0, 2*WIDTH*sizeof(short)); //Even/odd lines buffer
for (int y=0; y<HEIGHT; y++)
{
int RLEError=0;
int PenColor = 255*(Grayscale8Bit[y][0]>127); //Pen color can be either 0 or 255
for (int x=0; x<WIDTH; x++)
{
int PixelValue = (int)Grayscale8Bit[y][x] + AdditionalError[y&1][x]; //Exact pixel value plus Floyd-Steinberg error from the prev. line
RLEError += SQUARE (PixelValue - PenColor); //To avoid missing contrast details such as thin vertical lines, RLE error counted as square.
if (RLEError > SQUARE (MAXERROR))
{
PenColor = 255-PenColor; //Inverse pen position (up/down)
RLEError = SQUARE (PixelValue - PenColor); //Begin counting new RLE error immediately
}
Dithered8Bit[y][x]=PenColor; //Put proper color into the output array
AdditionalError[!(y&1)][x] = (PixelValue - PenColor)/2; //Put remaining error into next line buffer, not exactly Floyd-Steinberg but sort of.
if (x) AdditionalError[!(y&1)][x-1] = (PixelValue - PenColor)/4;
if (x<WIDTH-1) AdditionalError[!(y&1)][x+1] = (PixelValue - PenColor)/4;
}
}
тут мы пишем в файл Dithered8Bit, этот код тоже у каждого свой получится
Код без каких-либо капризов, отладки и подбора параметров сразу выдаёт результат:

Ну то есть задача в её куцем виде — совсем детская. Там не то что думать не пришлось, даже ошибиться негде было. Но и результат тоже, мягко говоря, так себе.
Ну а теперь вот вам по случаю пятницы головоломка: как полностью реализовать потенциал не одной, а двух степеней свободы нашего привода, да ещё с учётом того, что скорость протяжки бумаги и скорость вошканья каретки в общем случае друг другу не равны, а проходить ручкой по одному месту больше одного-двух раз — нежелательно, бумага не чугунная. Мучайтесь и ломайте головы над возможными алгоритмами такого вот обхода растра ;)
Спойлер, но вы его сразу не читайте, чтобы не сбиться со своих мыслей: я бы, наверное, обошёл сначала изолинии крупных элементов, разбивая пространство между ними на более или менее густые штриховки, а потом уже прикинул бы ошибку и добавил-убавил штрихи сообразно мелким деталям. Перо, идущее вдоль изолиний — в общем случае довольно хорошая идея, когда надо не убить разборчивость изображения, а то даже ещё и усилить её. Но, правда, это касается только фотореалистичных изображений, а в задаче-то у нас произвольные.