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: platno.hpp, platno.cpp, main.cpp.

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

Reprezentacija: Platno ima sirina, visina i piksel — matricu fiksnog kapaciteta char piksel[MAX_VISINA][MAX_SIRINA], gde je karakter na koloni x, redu y dat sa piksel[y][x]; koriste se samo pozicije u opsegu 0 <= y < visina, 0 <= x < sirina.

Operacije:

Ulaz

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

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

Izlaz

Na standardni izlaz ispisati platno pri obradi upita p (jedan red platna po liniji), ili broj različitih boja 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

platno.hpp

#ifndef PLATNO_HPP
#define PLATNO_HPP

namespace matf
{
  const int MAX_SIRINA = 32;
  const int MAX_VISINA = 32;

  struct Platno {
    int sirina;
    int visina;
    char piksel[MAX_VISINA][MAX_SIRINA];   // piksel[y][x], y u [0, visina), x u [0, sirina)
  };

  // preduslov: sirina <= MAX_SIRINA, visina <= MAX_VISINA
  Platno napravi_platno(int sirina, int visina, char boja);

  void postavi_piksel(Platno& p, int x, int y, char boja);
  void nacrtaj_horizontalnu_liniju(Platno& p, int y, int x1, int x2, char boja);
  void nacrtaj_vertikalnu_liniju(Platno& p, int x, int y1, int y2, char boja);
  void popuni_pravougaonik(Platno& p, int x, int y, int w, int h, char boja);

  // osa == 'H' za horizontalno, osa == 'V' za vertikalno preslikavanje
  void preslikaj(Platno& p, char osa);
  // rotira platno za 90 stepeni udesno; sirina i visina se razmenjuju
  void rotiraj90(Platno& p);

  int broj_razlicitih_boja(const Platno& p);

  void ispisi_platno(const Platno& p);

} // namespace matf

#endif // PLATNO_HPP

platno.cpp

#include "platno.hpp"

#include <algorithm>
#include <cassert>
#include <cctype>
#include <iostream>

namespace matf
{

  Platno napravi_platno(int sirina, int visina, char boja)
  {
    assert(sirina <= MAX_SIRINA && visina <= MAX_VISINA);

    Platno p;
    p.sirina = sirina;
    p.visina = visina;
    for (int y = 0; y < visina; ++y)
      for (int x = 0; x < sirina; ++x)
        p.piksel[y][x] = boja;
    return p;
  }

  void postavi_piksel(Platno& p, int x, int y, char boja)
  {
    p.piksel[y][x] = boja;
  }

  void nacrtaj_horizontalnu_liniju(Platno& p, int y, int x1, int x2, char boja)
  {
    for (int x = x1; x <= x2; ++x)
      p.piksel[y][x] = boja;
  }

  void nacrtaj_vertikalnu_liniju(Platno& p, int x, int y1, int y2, char boja)
  {
    for (int y = y1; y <= y2; ++y)
      p.piksel[y][x] = boja;
  }

  void popuni_pravougaonik(Platno& p, int x, int y, int w, int h, char boja)
  {
    for (int yy = y; yy < y + h; ++yy)
      for (int xx = x; xx < x + w; ++xx)
        p.piksel[yy][xx] = boja;
  }

  void preslikaj(Platno& p, char osa)
  {
    if (osa == 'H') {
      for (int y = 0; y < p.visina; ++y)
        std::reverse(p.piksel[y], p.piksel[y] + p.sirina);
    } else if (osa == 'V') {
      for (int y = 0; y < p.visina / 2; ++y)
        std::swap_ranges(p.piksel[y], p.piksel[y] + p.sirina, p.piksel[p.visina - 1 - y]);
    }
  }

  void rotiraj90(Platno& p)
  {
    Platno novo;
    novo.sirina = p.visina;
    novo.visina = p.sirina;

    for (int y = 0; y < novo.visina; ++y)
      for (int x = 0; x < novo.sirina; ++x)
        novo.piksel[y][x] = p.piksel[p.visina - 1 - x][y];

    p = novo;
  }

  int broj_razlicitih_boja(const Platno& p)
  {
    bool vidjeno[256] = {};
    for (int y = 0; y < p.visina; ++y)
      for (int x = 0; x < p.sirina; ++x)
        if (std::isprint(static_cast<unsigned char>(p.piksel[y][x])))
          vidjeno[static_cast<unsigned char>(p.piksel[y][x])] = true;

    int broj = 0;
    for (bool b : vidjeno)
      if (b)
        ++broj;
    return broj;
  }

  void ispisi_platno(const Platno& p)
  {
    for (int y = 0; y < p.visina; ++y) {
      for (int x = 0; x < p.sirina; ++x)
        std::cout << p.piksel[y][x];
      std::cout << std::endl;
    }
  }

} // namespace matf

main.cpp

#include "platno.hpp"

#include <iostream>

int main()
{
  int q;
  std::cin >> q;

  matf::Platno p;

  while (q--) {
    char op;
    std::cin >> op;

    if (op == 'c') {
      int w, h;
      char boja;
      std::cin >> w >> h >> boja;
      p = matf::napravi_platno(w, h, boja);
    } else if (op == 's') {
      int x, y;
      char boja;
      std::cin >> x >> y >> boja;
      matf::postavi_piksel(p, x, y, boja);
    } else if (op == 'h') {
      int y, x1, x2;
      char boja;
      std::cin >> y >> x1 >> x2 >> boja;
      matf::nacrtaj_horizontalnu_liniju(p, y, x1, x2, boja);
    } else if (op == 'v') {
      int x, y1, y2;
      char boja;
      std::cin >> x >> y1 >> y2 >> boja;
      matf::nacrtaj_vertikalnu_liniju(p, x, y1, y2, boja);
    } else if (op == 'r') {
      int x, y, w, h;
      char boja;
      std::cin >> x >> y >> w >> h >> boja;
      matf::popuni_pravougaonik(p, x, y, w, h, boja);
    } else if (op == 'm') {
      char osa;
      std::cin >> osa;
      matf::preslikaj(p, osa);
    } else if (op == 't') {
      matf::rotiraj90(p);
    } else if (op == 'p') {
      matf::ispisi_platno(p);
    } else if (op == 'n') {
      std::cout << matf::broj_razlicitih_boja(p) << std::endl;
    }
  }

  return 0;
}