/* See LICENSE file for copyright and license details. */
#include "libtracebitmap.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
struct data {
size_t height;
size_t width;
size_t y;
size_t x;
int beginning;
const char ***plot;
};
static int
new_component(int negative, void *user_data)
{
struct data *data = user_data;
data->beginning = 1;
printf("%s", negative ? "-" : "+");
fflush(stdout);
return 0;
}
static const char *
mix(const char *a, const char *b)
{
static const struct {
const char *text;
uint16_t bits;
} symbols[] = {
{" ", 0x0000}, {"┼", 0x1111},
{"╵", 0x1000}, {"┬", 0x0111},
{"╶", 0x0100}, {"┤", 0x1011},
{"╷", 0x0010}, {"┴", 0x1101},
{"╴", 0x0001}, {"├", 0x1110},
{"│", 0x1010}, {"─", 0x0101},
{"└", 0x1100}, {"┐", 0x0011},
{"┘", 0x1001}, {"┌", 0x0110}
};
uint16_t bits;
size_t i, j;
for (i = 0; strcmp(a, symbols[i].text); i++);
for (j = 0; strcmp(b, symbols[j].text); j++);
bits = symbols[i].bits | symbols[j].bits;
for (i = 0; bits != symbols[i].bits; i++);
return symbols[i].text;
}
static int
new_stop(size_t y, size_t x, void *user_data)
{
struct data *data = user_data;
size_t y0, y1, y2;
size_t x0, x1, x2;
printf(" (%zu,%zu)", y, x);
fflush(stdout);
if (data->beginning) {
data->beginning = 0;
data->y = y;
data->x = x;
} else {
y0 = MIN(data->y, y);
x0 = MIN(data->x, x);
y2 = MAX(data->y, y);
x2 = MAX(data->x, x);
if (y0 == y2) {
y1 = y0;
for (x1 = x0; x1 < x2; x1++) {
data->plot[y1 * 2][x1 * 2 + 0] = mix(data->plot[y1 * 2][x1 * 2 + 0], "╶");
data->plot[y1 * 2][x1 * 2 + 1] = mix(data->plot[y1 * 2][x1 * 2 + 1], "─");
data->plot[y1 * 2][x1 * 2 + 2] = mix(data->plot[y1 * 2][x1 * 2 + 2], "╴");
}
} else {
x1 = x0;
for (y1 = y0; y1 < y2; y1++) {
data->plot[y1 * 2 + 0][x1 * 2] = mix(data->plot[y1 * 2 + 0][x1 * 2], "╷");
data->plot[y1 * 2 + 1][x1 * 2] = mix(data->plot[y1 * 2 + 1][x1 * 2], "│");
data->plot[y1 * 2 + 2][x1 * 2] = mix(data->plot[y1 * 2 + 2][x1 * 2], "╵");
}
}
data->y = y;
data->x = x;
}
return 0;
}
static int
component_finished(void *user_data)
{
(void) user_data;
printf("\n");
fflush(stdout);
return 0;
}
int
main(void)
{
struct libtracebitmap_bitmap bitmap;
size_t size = 0;
size_t len = 0;
ssize_t rd;
size_t width, i, j;
size_t y, x;
int r;
const char ***plot;
struct data data;
bitmap.image = NULL;
for (;;) {
if (len == size) {
size += 1024;
bitmap.image = realloc(bitmap.image, size);
if (!bitmap.image) {
perror("realloc");
exit(1);
}
}
rd = read(STDIN_FILENO, &bitmap.image[len], size - len);
if (rd <= 0) {
if (!rd)
break;
perror("read");
exit(1);
}
len += (size_t)rd;
}
bitmap.height = 0;
bitmap.width = 0;
width = 0;
for (i = j = 0; j < len; j++) {
if ((char)bitmap.image[j] == '\n') {
if (!bitmap.height++) {
bitmap.width = width;
} else {
if (bitmap.width != width) {
fprintf(stderr, "Invalid input: each line must have the same length.\n");
exit(1);
}
}
width = 0;
} else if ((char)bitmap.image[j] == '.') {
bitmap.image[i++] = LIBTRACEBITMAP_INK_OFF;
width += 1;
} else if ((char)bitmap.image[j] == 'x') {
bitmap.image[i++] = LIBTRACEBITMAP_INK_ON;
width += 1;
} else {
fprintf(stderr, "Invalid input: may only contain '.', 'x', and <newline> characters.\n");
exit(1);
}
}
if (width) {
fprintf(stderr, "Invalid input: must end with <newline> character.\n");
exit(1);
}
plot = calloc(bitmap.height * 2 + 1, sizeof(*plot));
for (y = 0; y < bitmap.height * 2 + 1; y++) {
plot[y] = calloc(bitmap.width * 2 + 1, sizeof(**plot));
for (x = 0; x < bitmap.width * 2 + 1; x++)
plot[y][x] = " ";
}
for (y = 0, i = 0; y < bitmap.height; y++)
for (x = 0; x < bitmap.width; x++, i++)
plot[y * 2 + 1][x * 2 + 1] = (bitmap.image[i] == LIBTRACEBITMAP_INK_ON ? "x" : ".");
data.height = bitmap.height;
data.width = bitmap.width;
data.plot = plot;
r = libtracebitmap_trace(&bitmap, new_component, new_stop, component_finished, &data);
if (r)
exit(r);
for (y = 0; y < bitmap.height * 2 + 1; y++) {
for (x = 0; x < bitmap.width * 2 + 1; x++)
printf("%s", plot[y][x]);
printf("\n");
free(plot[y]);
}
free(plot);
free(bitmap.image);
return 0;
}