Serwis Edukacyjny
Nauczycieli
w I-LO w Tarnowie

Do strony głównej I LO w Tarnowie

Materiały dla uczniów liceum

  Wyjście       Spis treści       Poprzedni       Następny  

©2019 mgr Jerzy Wałaszek
I LO w Tarnowie

logo

Autor artykułu: mgr Jerzy Wałaszek

 

 

SDL2

Figury

Rozdziały:
    Instalacja
    Typy danych
    Grafika rastrowa
    Okno
    Kontekst graficzny
    Punkty
    Odcinki
    Figury
    Algorytm Bresenhama
    Wypełnianie figur
    Wypełnianie obszarów
    Wypełnianie obszarów algorytmem Smitha
    Przezroczystość
    Zdarzenia

     Interfejs SDL2 wg nazw
     Interfejs SDL2 wg kategorii
W rozdziale:
Czworokąty
Prostokąty
Trójkąty
Wielokąty foremne
Okręgi i elipsy

 

Czworokąty

Czworokąt (ang. quadrilateral) jest łamaną zamkniętą zbudowaną z 4 odcinków:

 

Figurę taką definiujemy za pomocą 4 punktów wierzchołkowych. Do narysowania czworokąta możemy użyć tablicy struktur SDL_Point oraz funkcji SDL_RenderDrawLines(). Aby łamana została zamknięta, ostatni element tablicy musi być taki sam jak pierwszy, z tego powodu tablica będzie zawierała 5 elementów, a nie 4.

Poniższy program tworzy zadaną liczbę losowych czworokątów, po czym animuje je w okienku.

 

// Czworokąty 1
//-------------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba czworokątów
const int N = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point q[N][5];
  int dx[N],dy[N],cr[N],cg[N],cb[N],i,j;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  for(i = 0; i < N; i++)
  {
    for(j = 0; j < 4; j++)
    {
      q[i][j].x = W_W / 2 - W_W / 4 + (rand() % W_W) / 2;
      q[i][j].y = W_H / 2 - W_H / 4 + (rand() % W_H) / 2;
    }
    q[i][4] = q[i][0]; // Zamykamy łamaną
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,q[i],5);
    }

    // Modyfikujemy współrzędne
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((q[i][j].x + dx[i] < 0) || (q[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((q[i][j].y + dy[i] < 0) || (q[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].y += dy[i];
        q[i][4] = q[i][0];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Równoległobok (ang. parallelogram) jest czworokątem, którego boki są parami równoległe do siebie:

W powyższym równoległoboku zachodzą związki:

Oznacza to, iż równoległość boków wymusza, iż boki naprzeciwległe mają równe długości. Dlatego do zdefiniowania równoległoboku niezbędne są trzy punkty wierzchołkowe. Czwarty punkt nie jest swobodny, lecz zależy od tych trzech i można go wyznaczyć obliczeniowo.

Umówmy się, że 3 punkty definiujące równoległobok będą podane zgodnie z ruchem wskazówek zegara (jak na powyższym rysunku): P1(x1,y1), P2(x2,y2) i P3(x3,y3), Obliczymy współrzędne punktu P4(x4,y4). Wykorzystamy proste związki geometryczne:

 

Na dwóch przeciwległych bokach budujemy trójkąty prostokątne tak, aby boki te były przeciwprostokątnymi, a pozostałe boki trójkątów mają być równoległe do osi OX i OY. Ponieważ boki naprzeciwległe równoległoboku są równe co do długości, to otrzymujemy dla pierwszego trójkąta:

Dla drugiego trójkąta mamy podobnie:

Trójkąty są przystające, zatem:

A stąd otrzymujemy wzory na współrzędne czwartego punktu:

Zmodyfikujemy nieco poprzedni program tak, aby rysował równoległoboki:

 
// Czworokąty 2
//-------------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba równoległoboków
const int N = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point q[N][5];
  int dx[N],dy[N],cr[N],cg[N],cb[N],i,j;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  for(i = 0; i < N; i++)
  {
    // Losujemy trzy punkty
    for(j = 0; j < 3; j++)
    {
      q[i][j].x = W_W / 2 - W_W / 4 + (rand() % W_W) / 2;
      q[i][j].y = W_H / 2 - W_H / 4 + (rand() % W_H) / 2;
    }
    // Czwarty punkt wyliczamy
    q[i][3].x = q[i][0].x - q[i][1].x + q[i][2].x;
    q[i][3].y = q[i][0].y - q[i][1].y + q[i][2].y;
    q[i][4] = q[i][0]; // Zamykamy łamaną
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,q[i],5);
    }

    // Modyfikujemy współrzędne
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((q[i][j].x + dx[i] < 0) || (q[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((q[i][j].y + dy[i] < 0) || (q[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].y += dy[i];
        q[i][4] = q[i][0];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Romb (ang. rhombus) jest równoległobokiem o wszystkich bokach równych co do długości:

Romb, podobnie jak opisany powyżej równoległobok, definiujemy za pomocą 3 kolejnych wierzchołków, a współrzędne czwartego wierzchołka wyliczamy wg wzorów:

W rombie dwa pierwsze punkty P1 i P2 można wybrać dowolnie, jednak punkt P3 nie jest już swobodny, ponieważ musi znajdować się w takiej samej odległości od punktu P2 jak punkt P1, inaczej równoległobok nie miałby boków równych. Wynika z tego, że punkt P3 musi leżeć na obwodzie okręgu o promieniu równym długości boku rombu i o środku w punkcie P2:

Spostrzeżenie to umożliwi nam generowanie rombów. Procedura jest następująca:

Generujemy losowe współrzędne punktów P1 i P2.

Obliczamy odległość od punktu P1 do P2 z twierdzenia Pitagorasa:

Ten drugi wzór nie jest pomyłką: jeśli odwrócimy kolejność składników w różnicy, to zmienimy jej znak na przeciwny. Nie ma to jednak żadnego znaczenia, ponieważ różnica i tak jest podnoszona do kwadratu:

Otrzymamy promień a okręgu, na obwodzie którego należy wyznaczyć losowy punkt. Środek okręgu jest w punkcie P2. Współrzędne punktów na okręgu określamy parametrycznie:

Parametr t wygenerujemy jako pseudolosową liczbę rzeczywistą wg wzoru dla języka C++:

t = 2 * M_PI * (rand() / (double) RAND_MAX);

Wyrażenie w nawiasie daje rzeczywistą wartość pseudolosową w zakresie od 0 do 1. Pomnożone przez 2π da wartość w pożądanym zakresie od 0 do 2π.

Gdy wyznaczymy współrzędne punktu P3, to wyliczamy współrzędne ostatniego wierzchołka rombu z podanego wcześniej wzoru dla równoległoboku:

Poniższy program jest modyfikacją pierwszego i animuje romby:

 
// Czworokąty 3
//-------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba rombów
const int N = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point q[N][5];
  int dx[N],dy[N],cr[N],cg[N],cb[N],i,j;
  double a,t;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  for(i = 0; i < N; i++)
  {
    // Losujemy dwa punkty
    for(j = 0; j < 2; j++)
    {
      q[i][j].x = W_W / 2 - W_W / 6 + (rand() % W_W) / 3;
      q[i][j].y = W_H / 2 - W_H / 6 + (rand() % W_H) / 3;
    }
    // Trzeci i czwarty punkt obliczamy
    a = sqrt((q[i][0].x-q[i][1].x)*(q[i][0].x-q[i][1].x) +
             (q[i][0].y-q[i][1].y)*(q[i][0].y-q[i][1].y));
    t = 2 * M_PI * (rand() / (double) RAND_MAX);
    q[i][2].x = q[i][1].x + a * cos(t);
    q[i][2].y = q[i][1].y + a * sin(t);
    q[i][3].x = q[i][0].x - q[i][1].x + q[i][2].x;
    q[i][3].y = q[i][0].y - q[i][1].y + q[i][2].y;
    q[i][4] = q[i][0]; // Zamykamy łamaną
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,q[i],5);
    }

    // Modyfikujemy współrzędne
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((q[i][j].x + dx[i] < 0) || (q[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((q[i][j].y + dy[i] < 0) || (q[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].y += dy[i];
        q[i][4] = q[i][0];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Prostokąt (ang. rectangle) jest równoległobokiem, w którym sąsiednie boki tworzą ze sobą kąt prosty:

Podobnie jak dla rombu dwa pierwsze punkty mogą być wybrane dowolnie. Punkt P3 musi leżeć na prostej prostopadłej do odcinka wyznaczonego przez P1 i P2 i przechodzącej przez punkt P2:

Problem sprowadza się zatem do wygenerowania punktu P3 na tej prostej prostopadłej i tym się teraz zajmiemy.

Punkt P3 można wygenerować na kilka sposobów. Poniżej podaję opis jednego z nich, w którym wykorzystuje się okrąg.

Najpierw losujemy punkt P2, który będzie środkiem okręgu o promieniu  równym a:

Dowolny punkt na obwodzie okręgu posiada współrzędne:

Kąt α jest katem pomiędzy prostą równoległą do osi OX układu współrzędnych i przechodzącą przez środek okręgu, a drugą prostą przechodzącą przez środek i wybrany punkt P1 na obwodzie okręgu. Aby otrzymać jego współrzędne, wystarczy wylosować kat α w zakresie od 0 do 2π radianów i użyć podanych wzorów. W ten sposób otrzymamy dwa wierzchołki prostokąta. Trzeci wierzchołek leży na prostej prostopadłej do odcinka P1P2 i przechodzącej przez P2. Losujemy zatem promień b, środek nowego okręgu umieszczamy w punkcie P2 i wybieramy na obwodzie punkt P3 o współrzędnych wg wzoru:

Otrzymamy trzeci wierzchołek prostokąta. Czwarty wierzchołek znajdziemy już bez problemu ze wzorów:

 
// Czworokąty 4
//-------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba prostokatów
const int N = 50;

// Kąt prosty w radianach
const double PI2 = M_PI / 2;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point q[N][5];
  int dx[N],dy[N],cr[N],cg[N],cb[N],i,j,x,a,b;
  double alpha;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy P2
      q[i][1].x = W_W / 2 - W_W / 6 + (rand() % W_W) / 3;
      q[i][1].y = W_H / 2 - W_H / 6 + (rand() % W_H) / 3;

    // Losujemy promień a i kąt alpha
    a = rand() % x;
    alpha = 2 * M_PI * rand() / RAND_MAX;

    // Wyliczamy P1
    q[i][0].x = q[i][1].x + a * cos(alpha);
    q[i][0].y = q[i][1].y + a * sin(alpha);

    // Losujemy promień b
    b = rand() % x;

    // Wyliczamy P3
    q[i][2].x = q[i][1].x + b * cos(alpha+PI2);
    q[i][2].y = q[i][1].y + b * sin(alpha+PI2);

    // Wyliczamy P4
    q[i][3].x = q[i][0].x - q[i][1].x + q[i][2].x;
    q[i][3].y = q[i][0].y - q[i][1].y + q[i][2].y;

    // Zamykamy łamaną
    q[i][4] = q[i][0];

    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,q[i],5);
    }

    // Modyfikujemy współrzędne
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((q[i][j].x + dx[i] < 0) || (q[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((q[i][j].y + dy[i] < 0) || (q[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) q[i][j].y += dy[i];
        q[i][4] = q[i][0];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 

 

Prostokąty

Biblioteka SDL2 zawiera specjalną funkcję, która rysuje prostokąt o bokach równoległych do boków ekranu:

SDL_RenderDrawRect(r,rect)

r – wskaźnik struktury SDL_Renderer z kontekstem graficznym

rect – wskaźnik struktury SDL_Rect, która definiuje prostokąt

SDL_Rect struktura definiuje prostokątny obszar na powierzchni graficznej:
struct SDL_Rect
{
  Sint16 x, y;
  Uint16 w, h;
};

x,y – współrzędne ekranowe lewego górnego narożnika. Zwróć uwagę, że mogą być ujemne.
w – szerokość prostokąta w pikselach
h – wysokość prostokąta w pikselach

Poniższy program demonstruje sposób wykorzystania tej funkcji w prostej animacji:

 

// Prostokąty 1
//-------------

#include <SDL.h>
#include <iostream>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy zmienne
  int d = -1;
  int w_max = W_W / 10 - 3;
  int h_max = W_H / 10 - 3;
  int i,j;

  SDL_Rect rect;
  rect.w = w_max;
  rect.h = h_max;

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy prostokąty
    SDL_SetRenderDrawColor(r,255,255,255,255);
    for(i = 0; i < 10; i++)
    {
        rect.y = i * W_H / 10 + (W_H / 10 - rect.h) / 2;
        for(j = 0; j < 10; j++)
        {
            rect.x = j * W_W / 10 + (W_W / 10 - rect.w) / 2;
            SDL_RenderDrawRect(r,&rect);
        }
    }

    // Zmieniamy rozmiary prostokątów
    if((rect.w + d < 2) || (rect.h + d < 2) || (rect.w + d > w_max) || (rect.h + d > h_max)) d = -d;
    rect.w += d;
    rect.h += d;

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    SDL_Delay(20);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Kolejny program wykorzystuje tablicę struktur SDL_Rect do utworzenia ciekawego efektu graficznego zanikających stopniowo prostokątów.

 

// Prostokąty 2
//-------------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba prostokątów
const int N = 100;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * win = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!win)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(win,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(win);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy zmienne

  srand(time(NULL));

  SDL_Rect R[N];
  int dx,dy;

  do dx = 5 - rand() % 11; while(!dx);
  do dy = 5 - rand() % 11; while(!dy);

  int i,w,h,x,y,c;
  w = W_W / 8 + rand() % (W_W / 8);
  h = W_H / 8 + rand() % (W_H / 8);
  x = W_W / 2 - w / 2;
  y = W_H / 2 - h / 2;
  for(i = 0; i < N; i++)
  {
      R[i].x = x;
      R[i].y = y;
      R[i].w = w;
      R[i].h = h;
  }

  // Kasujemy  treść okna
  SDL_SetRenderDrawColor(r,0,0,0,255);
  SDL_RenderClear(r);

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Rysujemy prostokąty od ostatniego do pierwszego
    // zwiększając stopniowo jasność;
    for(i = N - 1; i >= 0; i--)
    {
        c = (N - i - 1) * 255 / (N - 1);
        SDL_SetRenderDrawColor(r,c,c,c,255);
        SDL_RenderDrawRect(r,&R[i]);
    }

    // Modyfikujemy przyrosty
    if((R[0].x + dx < 0) || (R[0].x + R[0].w + dx >= W_W)) dx = -dx;
    if((R[0].y + dy < 0) || (R[0].y + R[0].h + dy >= W_H)) dy = -dy;

    // Modyfikujemy współrzędne pierwszego prostokąta
    R[0].x += dx;
    R[0].y += dy;

    // Ukatualniamy tablicę
    for(i = N - 1; i > 0; i--) R[i] = R[i - 1];

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    SDL_Delay(15);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(win);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


W bibliotece SDL2 istnieje funkcja, która pozwala hurtowo rysować prostokąty, których definicje przechowuje tablica o elementach typu SDL_Rect.

SDL_RenderDrawRects(r,R,n)

r – wskaźnik struktury SDL_Renderer z kontekstem graficznym
R – tablica elementów typu SDL_Rect, które zawierają definicje prostokątów.
n – liczba prostokątów do narysowania z tablicy.

Kolejny program tworzy prostą animację wykorzystującą tę funkcję:

 

// Prostokąty 3
//-------------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * win = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!win)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(win,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(win);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy zmienne
  srand(time(NULL));

  SDL_Rect R[400];
  int cr,cg,cb,dr,dg,db,i,j,n;

  for(i = 0; i < 20; i++)
    for(j = 0; j < 20; j++)
    {
        n = 20 * j + i;
        R[n].w = W_W / 20 - 4;
        R[n].h = W_H / 20 - 4;
        R[n].x = 2 + i * W_W / 20;
        R[n].y = 2 + j * W_H / 20;
    }

    cr = rand() % 256;
    cg = rand() % 256;
    cb = rand() % 256;
    do dr = 3 - rand() % 7; while(!dr);
    do dg = 3 - rand() % 7; while(!dg);
    do db = 3 - rand() % 7; while(!db);

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kolor prostokątów
    SDL_SetRenderDrawColor(r,cr,cg,cb,255);

    // Rysujemy prostokąty
    SDL_RenderDrawRects(r,R,400);

    // Uaktualniamy kolory
    if((cr + dr < 0) || (cr + dr > 255)) dr = - dr;
    if((cg + dg < 0) || (cg + dg > 255)) dg = - dg;
    if((cb + db < 0) || (cb + db > 255)) db = - db;
    cr += dr;
    cg += dg;
    cb += db;

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(win);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Oprócz rysowania krawędzi prostokątów biblioteka SDL2 zawiera funkcję rysującą wypełnione prostokąty:

SDL_RenderFillRect(r,rect)

r – wskaźnik struktury SDL_Renderer z kontekstem graficznym

rect – wskaźnik struktury SDL_Rect, która definiuje prostokąt

 

// Prostokąty 4
//-------------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * win = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!win)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(win,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(win);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy zmienne
  srand(time(NULL));

  SDL_Rect rect;
  rect.w = rect.h = 0;

  int cr,cg,cb;

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Czyścimy obszar okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Sprawdzamy rozmiary prostokąta
    if(!rect.w || !rect.h)
    {
        rect.w = W_W;
        rect.h = W_H;
        rect.x = rect.y = 0;
        cr = rand() % 256;
        cg = rand() % 256;
        cb = rand() % 256;
    }

    // Rysujemy wypałniony prostokąt
    SDL_SetRenderDrawColor(r,cr,cg,cb,255);
    SDL_RenderFillRect(r, &rect);

    // Zmniejszamy rozmiary prostokąta
    rect.w--;
    rect.h = rect.w * W_H / W_W;

    // Modyfikujemy odpowiednio współrzędne
    rect.x = (W_W - rect.w) / 2;
    rect.y = (W_H - rect.h) / 2;

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(win);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Podobnie do funkcji rysującej serię prostokątów w SDL2 istnieje funkcja rysująca serię prostokątów wypełnionych:

SDL_RenderFillRects(r,R,n)

r – wskaźnik struktury SDL_Renderer z kontekstem graficznym

R – tablica o elementach typu SDL_Rect, które definiują prostokąty
n – liczba prostokątów w tablicy R

Poniższy program tworzy zadaną liczbę prostokątów, a następnie przemieszcza je po obszarze okna. Kolor wypełnień jest płynnie zmieniany.

 
// Prostokąty 5
//-------------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba prostokątów
const int N = 10;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * win = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!win)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(win,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(win);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy zmienne
  srand(time(NULL)); // Generator pseudolosowy

  SDL_Rect R[N];   // Tablica prostokątów
  int dx[N],dy[N]; // Tabblice kierunków

  int i;

  for(i = 0; i < N; i++)
  {
      do dx[i] = -3 + rand() % 7; while(!dx[i]);
      do dy[i] = -3 + rand() % 7; while(!dy[i]);
      R[i].x = rand() % (W_W - 10);
      R[i].y = rand() % (W_H - 10);
      R[i].w = 1 + rand() % (W_W - R[i].x) / 3;
      R[i].h = 1 + rand() % (W_H - R[i].y) / 3;
  }

  int cr,cg,cb,dr,dg,db; // Kolory wypełnień
  cr = rand() % 256;
  cg = rand() % 256;
  cb = rand() % 256;
  do dr = -3 + rand() % 7; while(!dr);
  do dg = -3 + rand() % 7; while(!dg);
  do db = -3 + rand() % 7; while(!db);

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Ustawiamy kolor wypełnień
    SDL_SetRenderDrawColor(r,cr,cg,cb,255);

    // Modyfikujemy kolor wypełnień dla następnego obiegu pętli
    if((cr + dr > 255) || (cr + dr < 0)) dr = - dr;
    if((cg + dg > 255) || (cg + dg < 0)) dg = - dg;
    if((cb + db > 255) || (cb + db < 0)) db = - db;
    cr += dr;
    cg += dg;
    cb += db;

    // Wyświetlamy prostokąty
    SDL_RenderFillRects(r,R,N);

    // Modyfikujemy położenia prostokątów
    for(i = 0; i < N; i++)
    {
        if((R[i].x + dx[i] > W_W - R[i].w + 10) || (R[i].x + dx[i] < -10)) dx[i] = - dx[i];
        if((R[i].y + dy[i] > W_H - R[i].h + 10) || (R[i].y + dy[i] < -10)) dy[i] = - dy[i];
        R[i].x += dx[i];
        R[i].y += dy[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(win);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 

 

Trójkąty

Trójkąt (ang. triangle) jest chyba najczęściej używaną figurą w grafice komputerowej. Szczególnie często spotkasz się z trójkątami w grafice trójwymiarowej.

Trójkąt możemy narysować przy pomocy poznanych wcześniej funkcji. SDL2 nie posiada dedykowanej funkcji do rysowania tylko trójkątów. Korzystamy z finkcji SDL_RenderDrawLine() lub SDL_RenderDrawLines().

Poniższy program generuje zadaną liczbę trójkątów o różnych kolorach, po czym animuje je w oknie SDL2.

 

// Trójkąty 1
//-----------

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba trójkątów
const int N = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * win = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!win)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(win,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(win);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy zmienne
  srand(time(NULL)); // Generator pseudolosowy

  SDL_Point T[N][4];     // Tablica trójkątów
  int dx[N],dy[N];       // Tabblice przesunięć
  int cr[N],cg[N],cb[N]; // Tablice kolorów
  int dr[N],dg[N],db[N]; // Tablice zmian kolorów
  int i,j;

  // Generujemy trójkąty

  for(i = 0; i < N; i++)
  {
    // Wierzchołki
    for(j = 0; j < 3; j++)
    {
        T[i][j].x = rand() % W_W;
        T[i][j].y = rand() % W_H;
    }
    T[i][3] = T[i][0]; // Zamknięcie trójkąta

    // Przesunięcia w osiach OX i OY
    do dx[i] = -5 + rand() % 11; while(!dx[i]);
    do dy[i] = -5 + rand() % 11; while(!dy[i]);

    // Kolory trójkątów
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;

    // Zmiany kolorów
    do dr[i] = -3 + rand() % 7; while(!dr[i]);
    do dg[i] = -3 + rand() % 7; while(!dg[i]);
    do db[i] = -3 + rand() % 7; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy poszczególne trójkąty
    for(i = 0; i < N; i++)
    {
        // Kolor linii
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);

        // Trójkąt
        SDL_RenderDrawLines(r,T[i],4);

        // Modyfikujemy współrzędne
        for(j = 0; j < 3; j++)
        {
            if((T[i][j].x + dx[i] < - W_W / 2) || (T[i][j].x + dx[i] > 3 * W_W / 2)) dx[i] = -dx[i];
            if((T[i][j].y + dy[i] < - W_H / 2) || (T[i][j].y + dy[i] > 3 * W_H / 2)) dy[i] = -dy[i];
            T[i][j].x += dx[i];
            T[i][j].y += dy[i];
        }
        T[i][3] = T[i][0];

        // Modyfikujemy kolory
        if((cr[i] + dr[i] > 255) || (cr[i] + dr[i] < 0)) dr[i] = -dr[i];
        if((cg[i] + dg[i] > 255) || (cg[i] + dg[i] < 0)) dg[i] = -dg[i];
        if((cb[i] + db[i] > 255) || (cb[i] + db[i] < 0)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Opóźnienie
    SDL_Delay(20);

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(win);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Problem narysowania trójkąta sprowadza się do wygenerowania współrzędnych jego 3 wierzchołków, które następnie łączymy liniami. Czasem możesz potrzebować narysować trójkąt prostokątny, równoboczny lub równoramienny. Poniżej przedstawiamy proste sposoby osiągnięcia tego celu.

Trójkąt prostokątny

Do wygenerowania trójkąta prostokątnego możemy wykorzystać metodę opisaną wyżej dla prostokątów. Jej adaptacja jest natychmiastowa:

Najpierw losujemy punkt P2, który będzie środkiem okręgu o promieniu równym podstawie a:

Dowolny punkt na obwodzie okręgu posiada współrzędne:

Kąt α jest katem pomiędzy prostą równoległą do osi OX układu współrzędnych i przechodzącą przez środek okręgu, a drugą prostą przechodzącą przez środek i wybrany punkt P1 na obwodzie okręgu. Aby otrzymać jego współrzędne, wystarczy wylosować kat α w zakresie od 0 do 2π radianów i użyć podanych wzorów. W ten sposób otrzymamy dwa wierzchołki trójkąta. Trzeci wierzchołek leży na prostej prostopadłej do odcinka P1P2 i przechodzącej przez P2. Losujemy zatem promień b, środek nowego okręgu umieszczamy w punkcie P2 i wybieramy na obwodzie punkt P3 o współrzędnych wg wzoru:

Otrzymane punkty łączymy odcinkami i mamy trójkąt prostokątny.

Poniższy program wykorzystuje tę metodę do tworzenia i animacji trójkątów prostokątnych:

 
// Trójkąty 2
//-----------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba trójkątów
const int N = 50;

// Kąt prosty w radianach
const double PI2 = M_PI / 2;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point T[N][4];
  int dx[N],dy[N],cr[N],cg[N],cb[N],
      dr[N],dg[N],db[N],i,j,x,a,b;
  double alpha;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy P2
      T[i][1].x = W_W / 2 - W_W / 6 + (rand() % W_W) / 3;
      T[i][1].y = W_H / 2 - W_H / 6 + (rand() % W_H) / 3;

    // Losujemy promień a i kąt alpha
    a = 10 + rand() % x;
    alpha = 2 * M_PI * rand() / RAND_MAX;

    // Wyliczamy P1
    T[i][0].x = T[i][1].x + a * cos(alpha);
    T[i][0].y = T[i][1].y + a * sin(alpha);

    // Losujemy promień b
    b = 10 + rand() % x;

    // Wyliczamy P3
    T[i][2].x = T[i][1].x + b * cos(alpha+PI2);
    T[i][2].y = T[i][1].y + b * sin(alpha+PI2);

    // Zamykamy trójkąt
    T[i][3] = T[i][0];

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne trójkąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,T[i],4);
    }

    // Modyfikujemy współrzędne i kolory
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((T[i][j].x + dx[i] < 0) || (T[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) T[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((T[i][j].y + dy[i] < 0) || (T[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) T[i][j].y += dy[i];
        T[i][3] = T[i][0];
        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Trójkąt równoboczny

Do wygenerowania trójkąta równobocznego również wykorzystamy metodę trygonometryczną. Zasada jest następująca:

Losujemy punkt S, który będzie środkiem okręgu opisującego trójkąt. Losujemy promień okręgu r:

Losujemy kąt α, który wyznaczy nam na obwodzie okręgu wierzchołek startowy trójkąta:

Kolejne dwa wierzchołki otrzymamy odejmując i dodając do kąta α kąt 2/3π:

Punkty wystarczy połączyć odcinkami i mamy trójkąt równoboczny:

Poniższy program wykorzystuje tę metodę do generacji trójkątów równobocznych:

 
// Trójkąty 3
//-----------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba trójkątów
const int N = 50;

// Kąt 2/3 pi radianów
const double PI23 = 2 * M_PI / 3;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point T[N][4];
  int dx[N],dy[N],cr[N],cg[N],cb[N],
      dr[N],dg[N],db[N],i,j,x,sr,xs,ys;
  double alpha;

  // Środek okręgu
  xs = W_W / 2;
  ys = W_H / 2;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy kąt alpha i promień okręgu
    sr = 10 + rand() % x;
    alpha = 2 * M_PI * rand() / RAND_MAX;

    // Wyliczamy wierzchołek A
    T[i][0].x = xs + sr * cos(alpha);
    T[i][0].y = ys + sr * sin(alpha);

    // Wyliczamy wierzchołki B i C
    T[i][1].x = xs + sr * cos(alpha - PI23);
    T[i][1].y = ys + sr * sin(alpha - PI23);
    T[i][2].x = xs + sr * cos(alpha + PI23);
    T[i][2].y = ys + sr * sin(alpha + PI23);

    // Zamykamy trójkąt
    T[i][3] = T[i][0];

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne trójkąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,T[i],4);
    }

    // Modyfikujemy współrzędne i kolory
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((T[i][j].x + dx[i] < 0) || (T[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) T[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((T[i][j].y + dy[i] < 0) || (T[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) T[i][j].y += dy[i];
        T[i][3] = T[i][0];
        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Trójkąt równoramienny

Trójkąt równoramienny posiada dwa boki o równej długości. Narysujemy go za pomocą prawie tej samej metody, co dla trójkąta równobocznego.

Losujemy punkt S, który będzie środkiem okręgu opisującego trójkąt. Losujemy promień okręgu r:

Losujemy kąt α, który wyznaczy nam na obwodzie okręgu wierzchołek C trójkąta:

Losujemy kąt β z zakresu (0...π). Kolejne wierzchołki otrzymamy dodając i odejmując ten kąt od kąta α:

Przykładowy program:

 
// Trójkąty 4
//-----------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba trójkątów
const int N = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point T[N][4];
  int dx[N],dy[N],cr[N],cg[N],cb[N],
      dr[N],dg[N],db[N],i,j,x,sr,xs,ys;
  double alpha,beta;

  // Środek okręgu
  xs = W_W / 2;
  ys = W_H / 2;

  // Losujemy współrzędne wierzchołków, przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy kąt alpha i promień okręgu
    sr = 10 + rand() % x;
    alpha = 2 * M_PI * rand() / RAND_MAX;

    // Wyliczamy wierzchołek C
    T[i][2].x = xs + sr * cos(alpha);
    T[i][2].y = ys + sr * sin(alpha);

    // Losujemy kąt beta
    beta = M_PI * rand() / RAND_MAX;

    // Wyliczamy wierzchołki A i B
    T[i][0].x = xs + sr * cos(alpha - beta);
    T[i][0].y = ys + sr * sin(alpha - beta);
    T[i][1].x = xs + sr * cos(alpha + beta);
    T[i][1].y = ys + sr * sin(alpha + beta);

    // Zamykamy trójkąt
    T[i][3] = T[i][0];

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne trójkąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,T[i],4);
    }

    // Modyfikujemy współrzędne i kolory
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < 4; j++)
          if((T[i][j].x + dx[i] < 0) || (T[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < 4; j++) T[i][j].x += dx[i];
        for(j = 0; j < 4; j++)
          if((T[i][j].y + dy[i] < 0) || (T[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < 4; j++) T[i][j].y += dy[i];
        T[i][3] = T[i][0];
        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 

 

Wielokąty foremne

Do narysowania dowolnego wielokąta foremnego wykorzystamy metodę opisaną dla trójkąta równobocznego:

Losujemy punkt S, który będzie środkiem okręgu opisującego wielokąt. Losujemy promień okręgu r:

Losujemy kąt α, który wyznaczy nam na obwodzie okręgu pierwszy wierzchołek wielokąta:

Pozostałe wierzchołki otrzymamy zwiększając kąt α kolejno o kąt 2π/n, gdzie n oznacza liczbę wierzchołków wielokąta (przykład przedstawia sześciokąt):

A uogólniając wzory dla n-kąta foremnego mamy:

Otrzymane punkty łączymy odcinkami i otrzymujemy wielokąt foremny:

 

Poniższy program tworzy n-kąty foremne (n = 3...9) i animuje je w oknie SDL2:

 
// Wielokąty foremne
//------------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba wielokątów
const int N = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point T[N][10];
  int dx[N],dy[N],n[N],cr[N],cg[N],cb[N],
      dr[N],dg[N],db[N],i,j,x,sr,xs,ys;
  double alpha;

  // Środek okręgu
  xs = W_W / 2;
  ys = W_H / 2;

  // Losujemy liczby wierzchołków, współrzędne wierzchołków,
  // przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy liczbę wierzchołków od 3 do 9:
    n[i] = 3 + rand() % 7;

    // Losujemy kąt alpha i promień okręgu
    sr = 10 + rand() % x;
    alpha = 2 * M_PI * rand() / RAND_MAX;

    // Wyliczamy kolejne wierzchołki wielokata
    for(j = 0; j < n[i]; j++)
    {
      T[i][j].x = xs + sr * cos(alpha + j * 2 * M_PI / n[i]);
      T[i][j].y = ys + sr * sin(alpha + j * 2 * M_PI / n[i]);
    }

    // Zamykamy wielokąt
    T[i][n[i]] = T[i][0];

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,T[i],n[i]+1);
    }

    // Modyfikujemy współrzędne i kolory
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < n[i]; j++)
          if((T[i][j].x + dx[i] < 0) || (T[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < n[i]; j++) T[i][j].x += dx[i];
        for(j = 0; j < n[i]; j++)
          if((T[i][j].y + dy[i] < 0) || (T[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < n[i]; j++) T[i][j].y += dy[i];
        T[i][n[i]] = T[i][0];
        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 

 

Okręgi i elipsy

W SDL 2 nie ma funkcji rysującej okręgi lub elipsy. Nie oznacza to jednak, iż tych figur nie możemy narysować. Istnieje kilka rozwiązań tego problemu. Jednym z nich jest wykorzystanie procedury rysowania wielokątów foremnych. Jeśli taki wielokąt ma odpowiednio dużą liczbę wierzchołków (np. 50), to praktycznie wygląda jak okrąg:

Do wyznaczania współrzędnych stosujemy wzory:

gdzie n jest odpowiednio duże. Poniżej przykład programu, który rysuje okręgi wg tej metody:

 

// Okręgi i elipsy 1
//------------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba okręgów
const int N = 30;

// Liczba wierzchołków
const int M = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point T[N][M+1];
  int dx[N],dy[N],cr[N],cg[N],cb[N],
      dr[N],dg[N],db[N],i,j,x,sr,xs,ys;


  // Środek okręgu
  xs = W_W / 2;
  ys = W_H / 2;

  // Losujemy liczby wierzchołków, współrzędne wierzchołków,
  // przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy promień okręgu
    sr = 10 + rand() % x;

    // Wyliczamy kolejne wierzchołki
    for(j = 0; j < M; j++)
    {
      T[i][j].x = xs + sr * cos(j * 2 * M_PI / M);
      T[i][j].y = ys + sr * sin(j * 2 * M_PI / M);
    }

    // Zamykamy wielokąt
    T[i][M] = T[i][0];

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,T[i],M + 1);
    }

    // Modyfikujemy współrzędne i kolory
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < M; j++)
          if((T[i][j].x + dx[i] < 0) || (T[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < M; j++) T[i][j].x += dx[i];
        for(j = 0; j < M; j++)
          if((T[i][j].y + dy[i] < 0) || (T[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < M; j++) T[i][j].y += dy[i];
        T[i][M] = T[i][0];
        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Jeśli chcemy narysować elipsę, to musimy zmodyfikować wzór na poszczególne punkty:

Zwróć uwagę, że teraz mamy dwa promienie: poziomy rx oraz pionowy ry:

 

// Okręgi i elipsy 2
//------------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba okręgów
const int N = 30;

// Liczba wierzchołków
const int M = 50;

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point T[N][M+1];
  int dx[N],dy[N],cr[N],cg[N],cb[N],
      dr[N],dg[N],db[N],i,j,x,rx,ry,xs,ys;


  // Środek elipsy
  xs = W_W / 2;
  ys = W_H / 2;

  // Losujemy liczby wierzchołków, współrzędne wierzchołków,
  // przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy promienie elipsy
    rx = 10 + rand() % x;
    ry = 10 + rand() % x;

    // Wyliczamy kolejne wierzchołki
    for(j = 0; j < M; j++)
    {
      T[i][j].x = xs + rx * cos(j * 2 * M_PI / M);
      T[i][j].y = ys + ry * sin(j * 2 * M_PI / M);
    }

    // Zamykamy wielokąt
    T[i][M] = T[i][0];

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne wielokąty
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderDrawLines(r,T[i],M + 1);
    }

    // Modyfikujemy współrzędne i kolory
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < M; j++)
          if((T[i][j].x + dx[i] < 0) || (T[i][j].x + dx[i] >= W_W))
          {
            dx[i] = -dx[i];
            break;
          }
        for(j = 0; j < M; j++) T[i][j].x += dx[i];
        for(j = 0; j < M; j++)
          if((T[i][j].y + dy[i] < 0) || (T[i][j].y + dy[i] >= W_H))
          {
            dy[i] = -dy[i];
            break;
          }
        for(j = 0; j < M; j++) T[i][j].y += dy[i];
        T[i][M] = T[i][0];
        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 

Zastanów się, jak uzyskać elipsę obróconą o zadany kąt α. Wskazówkę znajdziesz w rozdziale poświęconym punktom.


Otrzymane w powyższych dwóch programach okręgi i elipsy nie są zbyt ładne, szczególnie gdy są duże. Spowodowane to jest błędami zaokrągleń – obliczenia wykonywane są jako zmiennoprzecinkowe, lecz ich wyniki zostają zapamiętane w postaci całkowitej. Istnieje specjalny algorytm, zwany algorytmem Bresenhama, który rysuje bardzo ładne okręgi i elipsy bez korzystania z funkcji trygonometrycznych. Algorytm ten pracuje tylko z liczbami całkowitymi.

Zacznijmy od okręgu.

Algorytm Bresenhama dla okręgu

Okrąg jest figurą symetryczną, dzięki temu nie musimy wyliczać wszystkich jego punktów. Przyjrzyj się poniższemu rysunkowi:

Załóżmy, że mamy wyznaczony punkt P1 o współrzędnych x1, y1. Środek okręgu znajduje się w środku układu współrzędnych (za chwilę to zmienimy). Dzięki symetrii pozostałe punkty wyznaczymy jako:

P2: x2 = y1, y2 = x1

P3: x3 = y1, y3 = -x1

P4: x4 = x1, y4 = -y1

P5: x5 = -x1, y5 = -y1

P6: x6 = -y1, y6 = -x1

P7: x7 = -y1, y7 = x1

P8: x8 = -x1, y8 = y1

Jak widzisz, współrzędne tych punktów otrzymamy bezpośrednio ze współrzędnych punktu P1. Wystarczy zatem wyznaczyć współrzędne punktów w zaznaczonej na zielono 1/8 obwodu okręgu, a resztę punktów dostaniemy dzięki symetrii.

Jeśli okrąg ma środek w w punkcie S(xs,ys), to współrzędne środka należy dodać do współrzędnych punktów, zatem powyższe wzory przyjmują postać:

P1: x1 + xs, y1 + ys
P2: x2 = y1 + xs, y2 = x1 + ys

P3: x3 = y1 + xs, y3 = -x1 + ys

P4: x4 = x1 + xs, y4 = -y1 + ys

P5: x5 = -x1 + xs, y5 = -y1 + ys

P6: x6 = -y1 + xs, y6 = -x1 + ys

P7: x7 = -y1 + xs, y7 = x1 + ys

P8: x8 = -x1 + xs, y8 = y1 + ys

Wyjdziemy z podstawowego równania okręgu:


gdzie x, y – współrzędne punktu na obrysie okręgu, a r – promień okręgu.

Rysowanie okręgu rozpoczynamy w punkcie (0,r). Kolejne punkty będziemy wyznaczali poruszając się w kierunku zgodnym z ruchem wskazówek zegara aż do osiągnięcia pozycji, dla której x = y.

W rozważanej 1/8 obwodu okręgu z piksela P przechodzimy (zapalamy) do jednego z dwóch pikseli P1 lub P2. Oznaczmy wyrażenie błędu przez:

Zachodzą następujące zależności:

e < 0 , punkt (x,y) leży wewnątrz okręgu
e = 0 , punkt (x,y) leży dokładnie na obwodzie okręgu
e > 0 , punkt (x,y) leży na zewnątrz okręgu

Dla obu punktów P1 i P2 obliczamy wyrażenie błędu:

P1 = (x + 1, y)
e1 = e + (x + 1)2 + y2 - r2
e1 = e + (x + 1)2 + y2 - x2 - y2, za r2 wstawiamy x2 + y2
e1 = e + x2 + 2x + 1 + y2 - x2 - y2,  upraszczamy wzór i otrzymujemy:

e1 = e + 2x + 1

 

P2 = (x + 1, y - 1)
e2 = e + (x + 1)2 + (y - 1)2 - r2
e2 = e + (x + 1)2 + (y - 1)2 - x2 - y2
e2 = e + x2 + 2x + 1 + y2 - 2y + 1 - x2 - y2
e2 = e + 2x - 2y + 2

e2 = e1 - 2y + 1

Gdy wyznaczymy błędy dla obu punktów, obliczamy odchyłkę e12:

e12 = e1 + e2
e12 = e + 2x + 1 + e + 2x - 2y + 2

e12 = 2e + 4x - 2y + 3

Badając znak odchyłki wybieramy:

e12 < 0 - punkt P1, czyli x → x + 1,  y → y

e12 ≥ 0 - punkt P2, czyli x → x + 1, y → y - 1

Powód takiego postępowania wyjaśnia poniższa tabelka:

e1 e2 e12 = e1 + e2 Następny
piksel
Uwagi
< 0 < 0 < 0 P1 = (x + 1, y) P1 i P2 są wewnątrz okręgu, lecz P1 jest bliżej brzegu
< 0 ≥ 0 ? ? Ta sytuacja nie może wystąpić, gdyż oznaczałoby to, iż bliższy środkowi okręgu punkt P2 jest na zewnątrz lub na obrysie, a dalszy P1 jest wewnątrz okręgu.
≥ 0 < 0 < 0 P1 = (x + 1, y) Punkt P2 jest wewnątrz okręgu, a P1 na zewnątrz lub na obrysie. Jednakże P1 jest bliżej obrysu lub w takiej samej odległości od niego jak punkt P2
≥ 0 ≥ 0 ≥ 0 P2 = (x + 1, y - 1) Oba punkty są albo na zewnątrz okręgu, albo na zewnątrz jest P1, a P2 leży na obrysie. W każdym przypadku P2 jest bliżej obrysu.

Wartość początkową e przyjmujemy równą 0.

Po wyborze punktu P1 za nowe e przyjmujemy e1, a po wyborze P2 wyrażenie błędu e przyjmuje wartość e2. Wyznaczanie punktów kończy się, gdy x > y.

Poniższy program wykorzystuje algorytm Bresenhama do rysowania i animacji okręgów.

 

// Okręgi i elipsy 3
//------------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba okręgów
const int N = 30;

// Funkcja rysuje okrąg algorytmem Bresenhama
//-------------------------------------------
void SDL_RenderCircle(SDL_Renderer * r, int xs, int ys, int rs)
{
  int x,y,e,e1,e2;
  SDL_Point P[8];

  // Inicjujemy zmienne
  e = x = 0;
  y = rs;

  while(x <= y)
  {
      // Rysujemy 8 pikseli
      P[0].x =  x + xs; P[0].y =  y + ys;
      P[1].x =  y + xs; P[1].y = -x + ys;
      P[2].x = -x + xs; P[2].y = -y + ys;
      P[3].x = -y + xs; P[3].y =  x + ys;
      P[4].x =  y + xs; P[4].y =  x + ys;
      P[5].x =  x + xs; P[5].y = -y + ys;
      P[6].x = -y + xs; P[6].y = -x + ys;
      P[7].x = -x + xs; P[7].y =  y + ys;
      SDL_RenderDrawPoints(r,P,8);

      // Obliczamy wyrażenia błędów
      e1 = e + 2 * x + 1;
      e2 = e1 - 2 * y + 1;

      x++; // krok w osi x
      if(e1 + e2 < 0) e = e1;
      else
      {
        y--; // krok w osi y
        e = e2;
      }
  }
}

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point S[N]; // Środki okręgów
  int R[N];       // Promienie okręgów
  int dx[N],dy[N],cr[N],cg[N],cb[N],dr[N],dg[N],db[N],i,x;

  // Losujemy promienie, przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy promienie okręgów
    R[i] = 10 + rand() % x;

    // Środki okręgów
    S[i].x = W_W / 2;
    S[i].y = W_H / 2;

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne okręgi
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderCircle(r,S[i].x,S[i].y,R[i]);
    }

    // Modyfikujemy współrzędne środków i kolory
    for(i = 0; i < N; i++)
    {
        if((S[i].x + dx[i] - R[i] < 0) || (S[i].x + dx[i] + R[i] > W_W)) dx[i] = -dx[i];
        if((S[i].y + dy[i] - R[i] < 0) || (S[i].y + dy[i] + R[i] > W_H)) dy[i] = -dy[i];
        S[i].x += dx[i];
        S[i].y += dy[i];

        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 


Algorytm Bresenhama dla elipsy

Z elipsą jest nieco trudniej, ponieważ posiada ona tylko dwie osie symetrii. W konsekwencji musimy wyznaczyć punkty leżące na ćwiartce obwodu (linia w kolorze zielonym na rysunku powyżej).

Algorytm zakłada, że środek elipsy leży w środku układu współrzędnych. Jeśli tak nie jest, to do współrzędnych wyznaczonych punktów należy dodać współrzędne środka. Wyznaczony będzie punkt P1 z pierwszej ćwiartki elipsy. Pozostałe punkty iuzyskuje się poprzez symetrię:

P1 = x, y)
P2 = (x, -y)
P3 = (-x, -y)
P4 = (-x, y)

Zapiszmy równanie elipsy:

x,y – współrzędne punktu na obwodzie elipsy
rx – promień elipsy wzdłuż osi OX
ry – promień elipsy wzdłuż osi OY

Aby unikać dzielenia, przekształcamy równanie elipsy do poniższej postaci:

Dla danego punktu P = (x,y) wyrażenie błędu:

określa położenie tego punktu w stosunku do obrysu elipsy. Jeśli e jest ujemne, to punkt P leży wewnątrz elipsy. Jeśli e jest dodatnie, punkt P leży poza elipsą. Jeśli wyrażenie błędu e przyjmie wartość 0, to punkt P leży dokładnie na obrysie elipsy - jego współrzędne spełniają równanie elipsy. Piszemy o tym dlatego, iż tekstura składa się z siatki pikseli o całkowitych współrzędnych. Nie wszystkie wyznaczone punkty (szczerze mówiąc duża większość z nich) nie będą spełniały równania elipsy, ponieważ jej linia przebiega tak, iż nie ma pikseli dokładnie ją odwzorowujących (patrz - rysunek poniżej). Wyznaczone piksele będą przybliżać rzeczywistą linię elipsy - zatem ze zbioru pikseli tekstury zostaną przez nasz algorytm wybrane te piksele, których środki leżą najbliżej rzeczywistej linii elipsy.

Rysowanie elipsy rozpoczynamy od punktu P = (0,ry) i będziemy poruszali się zgodnie z ruchem wskazówek zegara w obrębie pierwszej ćwiartki elipsy. Jeśli przyjrzysz się dokładnie rysunkowi powyżej, to powinieneś zauważyć, iż ćwiartka elipsy dzieli się na dwie części:

  • Część górna, w której współrzędna x jest systematycznie zwiększana o 1 przy każdym pikselu - współrzędna y zmniejszana jest o 1 co pewien czas.
  • Część dolna, w której współrzędna y zmniejszana jest o 1 przy każdym kolejnym pikselu, a współrzędna x zwiększa się o 1 co pewien czas.

Oczywistym zatem jest, iż na obrysie ćwiartki elipsy musi być punkt, gdzie zmieniamy sposób modyfikacji współrzędnych pikseli. Punkt ten znajdziemy analizując styczną do obrysu elipsy, a właściwie jej współczynnik kierunkowy, który jest równy pochodnej funkcji opisującej elipsę.

W górnej części ćwiartki elipsy przyrost x jest na moduł większy od przyrostu y, zatem:

W dolnej części ćwiartki przyrost x jest na moduł mniejszy od przyrostu y:

Wynika stąd, iż zmiana następuje w punkcie P2 (patrz rysunek powyżej), którego współrzędne (x,y) spełniają równanie:

Rozważmy górną połówkę ćwiartki elipsy. Podobnie jak w przypadku okręgu z punktu P(x,y) możemy przejść tylko do P1(x+1,y) lub P2(x+1,y+1). Współrzędna x jest zwiększana zawsze o 1 w górnej części ćwiartki (patrz powyżej - nachylenie stycznej). O wyborze punktu P1 lub P2 będzie decydowało wyrażenie błędu. Policzmy je dla każdego z punktów:

P1: (x + 1, y)

e1 = e + ry2(x + 1)2 + rx2y2 - rx2ry2
e1 = e + ry2x2 + 2ry2x + ry2 + rx2y2 - rx2ry2,  zastępujemy :   - rx2ry2   wyrażeniem   - ry2x2 - rx2y2
e1 = e + ry2x2 + 2ry2x + ry2 + rx2y2 - ry2x2 - rx2y2 i upraszczamy otrzymując ostatecznie:

e1 = e + 2ry2x + ry2

 

P2: (x + 1, y + 1)

e2 = e + ry2(x + 1)2 + rx2(y - 1)2 - rx2ry2
e2 = e + ry2x2 + 2ry2x + ry2 + rx2y2 - 2rx2y + rx2 - rx2ry2
e2 = e + ry2x2 + 2ry2x + ry2 + rx2y2 - 2rx2y + rx2 - ry2x2 - rx2y2
e2 = e + 2ry2x + ry2 - 2rx2y + rx2

e2 = e1 - 2rx2y + rx2

Obliczamy odchyłkę:

e12 = e1 + e2

Badając znak odchyłki wybieramy:

e12 < 0 - punkt P1, czyli x → x + 1,  y → y, e1 → e

e12 ≥ 0 - punkt P2, czyli x → x + 1, y → y - 1, e2 → e

Wyjaśnienie tego faktu znajdziesz w opisie algorytmu Bresenhama dla okręgu. Wyznaczanie punktów w górnej części ćwiartki elipsy kontynuujemy do momentu, gdy przestanie być spełniona nierówność:

ry2x ≤ rx2y

Teraz przechodzimy do rysowania dolnej części ćwiartki obwodu elipsy. Z punktu P(x,y) możemy przejść albo do punktu P1(x,y-1), albo do punktu P2(x+1,y-1). W dolnej części ćwiartki współrzędna y kolejno wyznaczanych pikseli zawsze zmniejsza się o 1. Wyznaczanie punktów kontynuujemy aż y osiągnie oś OX, czyli dopóki y ≥ 0. Policzmy wyrażenia błędów dla każdego z punktów:

P1: (x, y - 1)

e1 = e + ry2x2 + rx2(y - 1)2 - rx2ry2
e1 = e + ry2x2 + rx2y2 - 2rx2y + rx2  - rx2ry2,  zastępujemy :   - rx2ry2   wyrażeniem   - ry2x2 - rx2y2
e1 = e + ry2x2 + rx2y2 - 2rx2y + rx2  - ry2x2 - rx2y2 i upraszczamy otrzymując ostatecznie:

e1 = e - 2rx2y + rx2

 

P2: (x + 1, y + 1)

e2 = e + ry2(x + 1)2 + rx2(y - 1)2 - rx2ry2
e2 = e + ry2x2 + 2ry2x + ry2 + rx2y2 - 2rx2y + rx2 - rx2ry2
e2 = e + ry2x2 + 2ry2x + ry2 + rx2y2 - 2rx2y + rx2 - ry2x2 - rx2y2
e2 = e + 2ry2x + ry2 - 2rx2y + rx2

e2 = e1 + 2ry2x + ry2

Obliczamy odchyłkę:

e12 = e1 + e2

Badając znak odchyłki wybieramy:

e12 < 0 - punkt P2, czyli x → x + 1,  y → y - 1, e2 → e

e12 ≥ 0 - punkt P1, czyli x → x, y → y - 1, e1 → e

 

// Okręgi i elipsy 4
//------------------

#include <SDL.h>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// Rozmiar okienka
const int W_W = 640;
const int W_H = 480;

// Liczba okręgów
const int N = 30;

// Funkcja rysuje elipsę algorytmem Bresenhama
//-------------------------------------------
void SDL_RenderEllipse(SDL_Renderer * r, int xs, int ys, int rx, int ry)
{
  int x,y,e,e1,e2,rx2,ry2,fx,fy;
  SDL_Point P[4];

  // Inicjujemy punkt startowy
  x = e = 0;
  y = ry;
  rx2 = rx * rx;
  ry2 = ry * ry;
  fx = 0;
  fy = rx2 * ry;

  while(fx <= fy)
  {
    // Rysujemy górne punkty ćwiartki
    P[0].x = x + xs;  P[0].y = y + ys;
    P[1].x = x + xs;  P[1].y = -y + ys;
    P[2].x = -x + xs; P[2].y = y + ys;
    P[3].x = -x + xs; P[3].y = -y + ys;
    SDL_RenderDrawPoints(r,P,4);

    // Wyliczamy wyrażenia błędów
    e1 = e  + (fx << 1) + ry2;
    e2 = e1 - (fy << 1) + rx2;

    fx += ry2;

    // W górnej części ćwiartki x jest zawsze zwiększane
    x++;

    if(e1 + e2 < 0) e = e1; // Punkt P1
    else
    {
        y--;
        fy -= rx2;
        e = e2;             // Punkt P2
    }
  }

  // Teraz dolna część ćwiartki

  while(y >= 0)
  {
    // Rysujemy dolne punkty ćwiartki
    P[0].x = x + xs;  P[0].y = y + ys;
    P[1].x = x + xs;  P[1].y = -y + ys;
    P[2].x = -x + xs; P[2].y = y + ys;
    P[3].x = -x + xs; P[3].y = -y + ys;
    SDL_RenderDrawPoints(r,P,4);

    // Wyliczamy wyrażenia błędów
    e1 = e  - (fy << 1) + rx2;
    e2 = e1 + (fx << 1) + ry2;

    fy -= rx2;

    // W dolnej części ćwiartki y jest zawsze zmniejszane
    y--;

    if(e1 + e2 >= 0) e = e1; // Punkt P1
    else
    {
      x++;
      fx += ry2;
      e = e2;                // Punkt P2
    }
  }
}

int main(int argc, char* args[])
{
   // Inicjujemy SDL
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tworzymy okno
  SDL_Window * w = SDL_CreateWindow("Figury", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);
  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy kontekst graficzny
  SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC);
  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Inicjujemy generator pseudolosowy
  srand(time(NULL));

  // Inicjujemy zmienne
  SDL_Point S[N];   // Środki elips
  int Rx[N],Ry[N];  // Promienie elips
  int dx[N],dy[N],cr[N],cg[N],cb[N],dr[N],dg[N],db[N],i,x;

  // Losujemy promienie, przyrosty ruchów i składowe kolorów
  x = sqrt (W_W * W_W + W_H * W_H) / 6;
  for(i = 0; i < N; i++)
  {
    // Losujemy promienie elips
    Rx[i] = 10 + rand() % x;
    Ry[i] = 10 + rand() % x;

    // Środki elips
    S[i].x = W_W / 2;
    S[i].y = W_H / 2;

    // Przesunięcia
    do dx[i] = -3 + rand() % 7; while(!dx[i]);
    do dy[i] = -3 + rand() % 7; while(!dy[i]);

    // Kolory
    cr[i] = rand() % 256;
    cg[i] = rand() % 256;
    cb[i] = rand() % 256;
    do dr[i] = -2 + rand() % 5; while(!dr[i]);
    do dg[i] = -2 + rand() % 5; while(!dg[i]);
    do db[i] = -2 + rand() % 5; while(!db[i]);
  }

  // Animacja
  SDL_Event e;
  while(1)
  {
    // Kasujemy  treść okna
    SDL_SetRenderDrawColor(r,0,0,0,255);
    SDL_RenderClear(r);

    // Rysujemy kolejne elipsy
    for(i = 0; i < N; i++)
    {
        SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255);
        SDL_RenderEllipse(r,S[i].x,S[i].y,Rx[i],Ry[i]);
    }

    // Modyfikujemy współrzędne środków i kolory
    for(i = 0; i < N; i++)
    {
        if((S[i].x + dx[i] - Rx[i] < 0) || (S[i].x + dx[i] + Rx[i] > W_W)) dx[i] = -dx[i];
        if((S[i].y + dy[i] - Ry[i] < 0) || (S[i].y + dy[i] + Ry[i] > W_H)) dy[i] = -dy[i];
        S[i].x += dx[i];
        S[i].y += dy[i];

        if((cr[i] + dr[i] < 0) || (cr[i] + dr[i] > 255)) dr[i] = -dr[i];
        if((cg[i] + dg[i] < 0) || (cg[i] + dg[i] > 255)) dg[i] = -dg[i];
        if((cb[i] + db[i] < 0) || (cb[i] + db[i] > 255)) db[i] = -db[i];
        cr[i] += dr[i];
        cg[i] += dg[i];
        cb[i] += db[i];
    }

    // Uaktualniamy okno
    SDL_RenderPresent(r);

    // Sprawdzamy, czy użytkownik nie zamyka programu
    if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break;
  }

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Kończymy pracę z SDL2
  SDL_Quit();

  return 0;
}

 

 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2019 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.