Mini ASCII Art Studio

Napraviti mini editor za ASCII art na malom platnu. Platno je pravougaona mreža znakova (boja). Treba omogućiti osnovne operacije crtanja i transformacija.

Zadatak rešiti u više fajlova: main.c, canvas.h, canvas.c.

Boja se predstavlja jednim ASCII karakterom char, koji je moguće ispisati (npr. A, B, #, .).

Platno Canvas treba da podržava sledeće funkcionalnosti:

Ulaz

Sa standardnog ulaza se učitava broj upita $q$ ($1 \leq q \leq 200$). Zatim se učitava $q$ upita oblika:

Svi indeksi su nula-bazirani. Pretpostaviti da su dimenzije i koordinate su u granicama platna i da neće biti nevalidnih upita. Platno neće biti rezolucije veće od $32 \times 32$.

Izlaz

Na standardni izlaz ispisati platno pri obradi upita p, ili vrednosti pri obradi upita n.

Primer

Ulaz

10
c 5 4 .
h 1 0 4 A
v 0 0 3 B
s 4 3 C
p
n
m H
p
t
p

Izlaz

B....
BAAAA
B....
B...C
4
....B
AAAAB
....B
C...B
C.A.
..A.
..A.
..A.
BBBB

Rešenje

canvas.h

#ifndef CANVAS_H
#define CANVAS_H

#define MAX_W 32
#define MAX_H 32

typedef struct Canvas {
    int w;
    int h;
    char pix[MAX_H][MAX_W];
} Canvas;

// Better to use pointers for passing and returning large structs (see later lectures)

Canvas canvas_new(int w, int h, char fill);
Canvas canvas_set(Canvas c, int x, int y, char col);
Canvas canvas_hline(Canvas c, int y, int x1, int x2, char col);
Canvas canvas_vline(Canvas c, int x, int y1, int y2, char col);
Canvas canvas_fill_rect(Canvas c, int x, int y, int w, int h, char col);
Canvas canvas_mirror(Canvas c, char axis);
Canvas canvas_rotate90(Canvas c);

int    canvas_distinct_colors(Canvas c);

void   canvas_print(Canvas c);

#endif // CANVAS_H

canvas.c

#include "canvas.h"
#include <stdio.h>
#include <ctype.h>

Canvas canvas_new(int w, int h, char fill)
{
    Canvas c;

    c.w = w; c.h = h;
    for (int y = 0; y < c.h; y++) {
        for (int x = 0; x < c.w; x++) {
            c.pix[y][x] = fill;
        }
    }

    return c;
}

Canvas canvas_set(Canvas c, int x, int y, char col)
{
    c.pix[y][x] = col;

    return c;
}

Canvas canvas_hline(Canvas c, int y, int x1, int x2, char col)
{
    for (int x = x1; x <= x2; x++) {
        c.pix[y][x] = col;
    }

    return c;
}

Canvas canvas_vline(Canvas c, int x, int y1, int y2, char col)
{
    for (int y = y1; y <= y2; y++) {
        c.pix[y][x] = col;
    }

    return c;
}

Canvas canvas_fill_rect(Canvas c, int x, int y, int w, int h, char col)
{
    int x2 = x + w - 1;
    int y2 = y + h - 1;

    for (int yy = y; yy <= y2; yy++) {
        for (int xx = x; xx <= x2; xx++) {
            c.pix[yy][xx] = col;
        }
    }

    return c;
}

Canvas canvas_mirror(Canvas c, char axis)
{
    if (axis == 'H') {
        for (int y = 0; y < c.h; y++) {
            for (int x = 0; x < c.w / 2; x++) {
                // new(x,y) = old(w-1-x,y)
                char tmp = c.pix[y][x];
                c.pix[y][x] = c.pix[y][c.w - 1 - x];
                c.pix[y][c.w - 1 - x] = tmp;
            }
        }
    } else if (axis == 'V') {
        for (int y = 0; y < c.h / 2; y++) {
            for (int x = 0; x < c.w; x++) {
                // new(x,y) = old(x,h-1-y)
                char tmp = c.pix[y][x];
                c.pix[y][x] = c.pix[c.h - 1 - y][x];
                c.pix[c.h - 1 - y][x] = tmp;
            }
        }
    }
    
    return c;
}

Canvas canvas_rotate90(Canvas c)
{
    Canvas out = c; // copy to initialize
    out.w = c.h;
    out.h = c.w;

    for (int y = 0; y < out.h; y++) {
        for (int x = 0; x < out.w; x++) {
            // new(x,y) = old(y, old_h-1-x)
            int ox = y;
            int oy = c.h - 1 - x;
            if (ox >= 0 && ox < c.w && oy >= 0 && oy < c.h) {
                out.pix[y][x] = c.pix[oy][ox];
            }
        }
    }

    return out;
}

int canvas_distinct_colors(Canvas c)
{
    int used[256] = { 0 };
    
    for (int y = 0; y < c.h; y++) {
        for (int x = 0; x < c.w; x++) {
            if (isprint(c.pix[y][x])) {
                used[(int) c.pix[y][x]] = 1;
            }
        }
    }

    int count = 0;
    for (int i = 0; i < 256; i++) {
        count += used[i];
    }

    return count;
}

void canvas_print(Canvas c)
{
    for (int y = 0; y < c.h; y++) {
        for (int x = 0; x < c.w; x++) {
            putchar(c.pix[y][x]);
        }
        putchar('\n');
    }
}

main.c

#include <stdio.h>
#include "canvas.h"

int main(void)
{
    int q;
    scanf("%d", &q);

    Canvas cv;

    while (q--) {
        char op;
        scanf(" %c", &op);
        if (op == 'c') {
            int w, h; char b;
            scanf("%d %d %c", &w, &h, &b);
            cv = canvas_new(w, h, b);
        } else if (op == 's') {
            int x, y; char b;
            scanf("%d %d %c", &x, &y, &b);
            cv = canvas_set(cv, x, y, b);
        } else if (op == 'h') {
            int y, x1, x2; char b;
            scanf("%d %d %d %c", &y, &x1, &x2, &b);
            cv = canvas_hline(cv, y, x1, x2, b);
        } else if (op == 'v') {
            int x, y1, y2; char b;
            scanf("%d %d %d %c", &x, &y1, &y2, &b);
            cv = canvas_vline(cv, x, y1, y2, b);
        } else if (op == 'r') {
            int x, y, w, h; char b;
            scanf("%d %d %d %d %c", &x, &y, &w, &h, &b);
            cv = canvas_fill_rect(cv, x, y, w, h, b);
        } else if (op == 'm') {
            char axis;
            scanf(" %c", &axis);
            cv = canvas_mirror(cv, axis);
        } else if (op == 't') {
            cv = canvas_rotate90(cv);
        } else if (op == 'p') {
            canvas_print(cv);
        } else if (op == 'n') {
            printf("%d\n", canvas_distinct_colors(cv));
        }
    }

    return 0;
}

Makefile

CC = gcc
CFLAGS = -Wall -Wextra -pedantic

.PHONY: all clean

all: main

main: main.o canvas.o
	$(CC) $(CFLAGS) -o main main.o canvas.o

main.o: main.c canvas.h
	$(CC) $(CFLAGS) -c main.c -o main.o

canvas.o: canvas.c canvas.h
	$(CC) $(CFLAGS) -c canvas.c -o canvas.o

clean:
	rm -f main.o canvas.o color.o main