PROGRAMOWANIE – PRZYKŁADY
IDE C++ WinBGI C# Wpadki

Przykład C++

Tworzenie epicykloidy i hipocykloidy Równania krzywych i ich implementacja Algorytm animacji Program w C++ Rozwiązanie obiektowe Program w C++ (wersja 2) Poprzedni przykład Następny przykład Program w Visual C# Kontakt

Tworzenie epicykloidy i hipocykloidy

Zadaniem programu jest przedsta­wienie w postaci animacji procesu powsta­wania epicykloidyhipocykloidy. Obie krzywe przestępne są opisy­wane przez koniec ramienia przytwier­dzonego sztywno do okręgu (koła) toczą­cego się bez poślizgu po stałym okręgu. Gdy okręgi stykają się zewnę­trznie (rys. poniżej z lewej), krzywa jest epicy­kloidą, a gdy wewę­trznie (rys. poniżej z prawej), jest hipocy­kloidą. Oczywiście w drugim przypadku promień ruchomego okręgu powinien być mniejszy od promienia nieru­chomego okręgu.

Na rysunkach oznaczono przez O i S środki okręgów stałego i ruchomego, przez R i r ich promienie, zaś przez d długość ramienia SP przytwier­dzonego do toczącego się okręgu. Przyjęto ponadto, że środek O stałego okręgu jest jednocze­śnie środkiem układu współ­rzędnych i na początku ruchu środek S toczącego się okręgu leży na dodatniej półosi x, a punkt P po jego lewej stronie, gdy okręgi stykają się zewnętrznie, albo po prawej, gdy stykają się wewnętrznie. Dowodzi się, że epicy­kloida spełnia równania

a hipocykloida równania

Kształt krzywych zależy od stosunku R do r. Jeżeli R jest podzielne przez r, krzywa jest zamknięta, a jeżeli nie, zamyka się po pewnej liczbie obiegów ruchomego okręgu, gdy R/r jest liczbą wymierną, a nie zamyka się nigdy, gdy jest liczbą niewy­mierną. Rozróżnia się również epicy­kloidę i hipocy­kloidę zwyczajną, gdy punkt P leży na toczącym się okręgu, czyli gdy d=r, skróconą, gdy d<r, oraz wydłużoną, gdy d>r, zaś gdy d=0, epicy­kloida redukuje się do okręgu o pro­mieniu R+r, a hipocy­kloida do okręgu o pro­mieniu R-r. Dla d<0 ustawienie początkowe punktu P jest przeciwne względem środka S.

Równania krzywych i ich implementacja

Przypomnijmy, że powyższe równania parame­tryczne zostały zasto­sowane w programie rysowania epicy­kloidy i hipocy­kloidy. Równania te określają zależność współrzę­dnych punktu leżącego na krzywej od kąta φ, jaki tworzy odcinek łączący środki obu okręgów z osią x. Bardziej odpowie­dnią do wykorzy­stania w animacji jest zależność współrzę­dnych tego punktu od kąta t pomiędzy prostą przecho­dzącą przez środki okręgów a ramie­niem d (rys.). Zmienia­jący swoją wartość parametr t kojarzy się wówczas z czasem, w miarę upływu którego okrąg o pro­mieniu r obraca się i toczy po stałym okręgu o pro­mieniu R, a koniec przytwier­dzonego do ruchomego okręgu ramienia, na którym głównie skupia uwagę obser­wator, rysuje krzywą.

Ruch jednego okręgu po drugim odbywa się bez poślizgu, toteż łuk AB ma taką samą długość jak łuk BC (rys. powyżej, łuki wyróżnione kolorem oliwkowym):

Stąd i powyższych równań parametry­cznych uzyskujemy równania epicy­kloidy

oraz równania hipocykloidy

Podobnie jak we wspomnianym programie rysowania przyjmiemy, że program animacji wczytuje z klawia­tury w funkcji parametry znak 'e' lub 'E', gdy krzywa ma być epicy­kloidą, albo 'h' lub 'H', gdy ma być hipocy­kloidą, a następnie trzy liczby rzeczy­wiste określające promienie stałego i ruchomego okręgu oraz długość ramienia. Przypo­mnijmy, że pobrane liczby zostają zapamiętane w zmiennych globalnych, zaś znak identyfi­kujący krzywą jest przekształ­cany na wygo­dniejszą do użycia wartość logiczną. Wizuali­zacja okręgów i ramienia może wymagać odpowie­dniego przeska­lowania wczytanych liczb celem dostoso­wania ich do rozmiaru obszaru robo­czego okna grafi­cznego. Definicje zmiennych globalnych programu i funkcji skalo­wania danych mogą wyglądać nastę­pująco:

double R;       // Promień stałego okręgu
double r;       // Promień ruchomego okręgu
double d;       // Długość ramienia
bool epic;      // Epicykloida (true), hipocykloida (false)
int p, q;       // Współrzędne środka okna graficznego

void skalowanie()
{
    p = getmaxx() / 2;
    q = getmaxy() / 2;
    int n = 9 * min(p, q) / 10;
    double skala = n / ((epic ? (R + r) : (R - r)) + max(r, fabs(d)));
    R *= skala;
    r *= skala;
    d *= skala;
}

Rysowanie ruchomego okręgu wraz z ramieniem w różnych miejscach obszaru roboczego okna wymaga wyzna­czania współrzę­dnych środka tego okręgu i końca ramienia dla różnych wartości parametru t. Korzy­stając z przekształ­conych równań parame­trycznych obu krzywych oraz uwzglę­dniając współ­rzędne środka obszaru roboczego i jego odwrotny kierunek osi y, obli­czenia te możemy sformu­łować w postaci dwóch funkcji:

void epicykloida(double t, int &xs, int &ys, int &xp, int &yp)
{
    double fi = t * r / R, psi = t + fi, x, y;
    xp = int((x = (R + r) * cos(fi)) - d * cos(psi)) + p;
    yp = q - int((y = (R + r) * sin(fi)) - d * sin(psi));
    xs = int(x) + p;
    ys = q - int(y);
}

void hipocykloida(double t, int &xs, int &ys, int &xp, int &yp)
{
    double fi = t * r / R, psi = t - fi, x, y;
    xp = int((x = (R - r) * cos(fi)) + d * cos(psi)) + p;
    yp = q - int((y = (R - r) * sin(fi)) - d * sin(psi));
    xs = int(x) + p;
    ys = q - int(y);
}

Algorytm animacji

W animacji opartej na przesyłaniu prostoką­tnego fragmentu obrazu pomiędzy ekranem a pamięcią operacyjną używane będą: zmienna rzeczy­wista t i stała DT oznacza­jące czas i odstęp pomiędzy dwoma kolejnymi momentami czasowymi (przyjęto 0.025), zmienne całkowite xs, ys, xpyp określa­jące bieżącą pozycję ekranową środka ruchomego okręgu i końca ramienia opisu­jącego krzywą oraz funkcja cykloida wyzna­czająca wartości tych czterech zmiennych w chwili t. Nazwa cykloida jest w rzeczy­wistości wskaźni­kiem na funkcję typu void o pięciu argumen­tach, z których pierwszy typu double jest przeka­zywany przez wartość, zaś pozostałe typu int są przeka­zywane przez referencję. Taką samą sygnaturę, czyli taką samą liczbę argu­mentów, ich typy i typ zwracanej wartości mają zdefinio­wane powyżej funkcje epicy­kloidahipocy­kloida, gdyż to one będą występo­wały w roli hipote­tycznej funkcji cykloida. Prosty schemat, według którego przebiegać będzie animacja, ma postać:

cykloida(t = 0, xs, ys, xp, yp);
circle(p, q, int(R));
while (!kbhit())
{
    ...         // Kopiuj fragment ekranu do bloku pamięci
    circle(xs, ys, int(r));
    moveto(xs, ys);
    lineto(xp, yp);
    Sleep(PRZERWA);
    ...         // Odtwórz fragment ekranu z bloku pamięci
    cykloida(t += DT, xs, ys, xp, yp);
    lineto(xp, yp);
}

Na wstępie oblicza się współrzędne środka ruchomego okręgu i końca ramienia w momencie początkowym zero i rysuje stały okrąg. Następnie w każdym kroku pętli kopiuje się do pamięci opera­cyjnej fragment ekranu, na którym ma być naryso­wany ruchomy okrąg i ramię, rysuje ten okrąg i ramię, a po króciutkim wstrzy­maniu wykonania programu (przyjęto 20 milisekund) odtwarza pierwotny wygląd zdezaktua­lizowanego fragmentu ekranu, oblicza współ­rzędne nowego środka ruchomego okręgu i końca ramienia w kolejnym momencie czasowym przesu­niętym o wartość DT i doryso­wuje odcinek łamanej wyobraża­jącej krzywą.

Operację kopiowania prostokątnego fragmentu obrazu z ekranu do pamięci realizuje funkcja getimage, a z pamięci na ekran funkcja putimage. Obie odwołują się do obszaru pamięci za pomocą wskaźnika, który wygodnie jest traktować jako tablicę dynamiczną bajtów. Liczbę bajtów potrze­bnego obszaru oblicza funkcja imagesize na podstawie współrzę­dnych dwóch skraj­nych rogów kopiowa­nego prosto­kąta (jego położenie nie ma znaczenia). W rozpatry­wanym przypadku prostokąt ten jest kwadratem obejmu­jącym ruchomy okrąg i przytwier­dzone do niego ramię. Jego bok ma długość 2a, gdzie a jest większą z wartości r i d (rys.). Rozmiar kopio­wanego prosto­kąta warto powiększyć o kilka pikseli, by uniknąć migotania obrazu na jego brzegu.

Po tych rozwa­żaniach przejdźmy do sformu­łowania funkcji animacji. Wskaźnik na funkcję opisującą krzywą przeka­żemy do niej w jej argumencie. Uwzglę­dniając przydział pamięci tablicy bajtów służącej do chwilowego przechowy­wania fragmentu obrazu i zwal­nianie jej, funkcję animacji możemy zaprogra­mować nastę­pująco:

void animacja(void (*cykloida)(double, int&, int&, int&, int&))
{
    int ri = int(r), a = max(int(fabs(d)), ri) + 7;
    unsigned int n = imagesize(0, 0, 2 * a, 2 * a);
    byte *pObraz = new byte[n];
    int xs, ys, xp, yp;
    double t;
    cykloida(t = 0, xs, ys, xp, yp);
    circle(p, q, int(R));
    while (!kbhit())
    {
        getimage(xs - a, ys - a, xs + a, ys + a, pObraz);
        circle(xs, ys, ri);
        moveto(xs, ys);
        lineto(xp, yp);
        Sleep(PRZERWA);
        putimage(xs - a, ys - a, pObraz, COPY_PUT);
        cykloida(t += DT, xs, ys, xp, yp);
        lineto(xp, yp);
    }
    delete[] pObraz;
}

Program w C++

Pełny kod programu w C++, który wczytuje z klawia­tury parametry epicy­kloidy lub hipocy­kloidy i prezen­tuje za pomocą animacji proces powstawania tej krzywej, ma postać:

#include <iostream>
#include <cmath>
#include <graphics.h>

using namespace std;

const int PRZERWA = 20;     // Szybkość animacji
const double DT = 0.025;    // Odstęp czasowy

double R;       // Promień stałego okręgu
double r;       // Promień ruchomego okręgu
double d;       // Długość ramienia
bool epic;      // Epicykloida (true), hipocykloida (false)
int p, q;       // Współrzędne środka okna graficznego

bool parametry()
{
    cout << "Epicykloida/Hipocykloida\n------------------------\n";
    char c;
    do
    {
        cout << "E/H: ";
        cin >> c;
        cin.ignore(1000, '\n');
    } while (c != 'e' && c != 'E' && c != 'h' && c != 'H');
    epic = (c == 'e') || (c == 'E');
    cout << "R : ";
    cin >> R;
    cout << "r : ";
    cin >> r;
    cout << "d : ";
    cin >> d;
    return !cin.fail() && (R > 0) && (r > 0) && (epic || (r < R));
}

void skalowanie()
{
    p = getmaxx() / 2;
    q = getmaxy() / 2;
    int n = 9 * min(p, q) / 10;
    double skala = n / ((epic ? (R + r) : (R - r)) + max(r, fabs(d)));
    R *= skala;
    r *= skala;
    d *= skala;
}

void epicykloida(double t, int &xs, int &ys, int &xp, int &yp)
{
    double fi = t * r / R, psi = t + fi, x, y;
    xp = int((x = (R + r) * cos(fi)) - d * cos(psi)) + p;
    yp = q - int((y = (R + r) * sin(fi)) - d * sin(psi));
    xs = int(x) + p;
    ys = q - int(y);
}

void hipocykloida(double t, int &xs, int &ys, int &xp, int &yp)
{
    double fi = t * r / R, psi = t - fi, x, y;
    xp = int((x = (R - r) * cos(fi)) + d * cos(psi)) + p;
    yp = q - int((y = (R - r) * sin(fi)) - d * sin(psi));
    xs = int(x) + p;
    ys = q - int(y);
}

void animacja(void (*cykloida)(double, int&, int&, int&, int&))
{
    int ri = int(r), a = max(int(fabs(d)), ri) + 7;
    unsigned int n = imagesize(0, 0, 2 * a, 2 * a);
    byte *pObraz = new byte[n];
    int xs, ys, xp, yp;
    double t;
    cykloida(t = 0, xs, ys, xp, yp);
    setcolor(LIGHTCYAN);
    circle(p, q, int(R));
    while (!kbhit())
    {
        getimage(xs - a, ys - a, xs + a, ys + a, pObraz);
        circle(xs, ys, ri);
        moveto(xs, ys);
        lineto(xp, yp);
        setcolor(WHITE);
        Sleep(SZYBKOSC);
        putimage(xs - a, ys - a, pObraz, COPY_PUT);
        cykloida(t += DT, xs, ys, xp, yp);
        lineto(xp, yp);
        setcolor(LIGHTCYAN);
    }
    delete[] pObraz;
}

int main()
{
    if (parametry())
    {
        initwindow(640, 480, epic ? "Epicykloida" : "Hipocykloida");
        skalowanie();
        animacja(epic ? epicykloida : hipocykloida);
        closegraph();
        return 0;
    }
    else
    {
        cout << "Problem z danymi\n";
        return 1;
    }
}

A oto dwa przykłady obrazów wygenero­wanych przez program w trakcie kilku sekund trwania animacji:

76

a) epicykloida dla R=100, r=70, d=120,

b) hipocykloida dla R=17.5, r=3.1, d=8.

Rozwiązanie obiektowe

Reprezentantem ruchomego okręgu (koła) wraz z przytwier­dzonym na sztywno ramie­niem może być w C++ obiekt. Aby utworzyć taki obiekt, należy najpierw zdefi­niować klasę stano­wiącą nowy typ danych. Elementami składo­wymi klasy są pola (dane) określa­jące cechy tworzo­nych według niej obiektów i metody (funkcje) opisujące zacho­wanie tych obiektów. W rozpatry­wanym przypadku polami klasy byłyby m.in. zmienne zawiera­jące współ­rzędne środka i promień okręgu, współ­rzędne końca ramienia, wskaźnik na funkcję reprezen­tującą rysowaną krzywą (epicy­kloidę lub hipocy­kloidę) i bieżący czas animacji, zaś metodami funkcja ryso­wania okręgu i ramienia oraz przesu­wania ich w inne miejsce.

Definicję klasy i jej metod można umieścić, podobnie jak w pro­gramie animacji układu planetar­nego, bezpośrednio w kodzie, w którym tworzone są obiekty tej klasy. Wygodniej­szym jednak sposobem przygoto­wania klasy do jej użycia jest wyodrę­bnienie jej w module składa­jącym się z pliku nagłówko­wego (.h) zawiera­jącego definicję klasy i pliku kodu źródło­wego (.cpp) jej funkcji składowych. Aby unikąć błędu wielokro­tnego wsta­wienia definicji klasy do programu, gdy kilka wchodzą­cych w jego skład modułów z niej korzysta, należy zasto­sować kompi­lację warunkową:

#ifndef Nazwa symboliczna
#define Nazwa symboliczna
  Definicja klasy
#endif

Użyta nazwa symboliczna powinna być niepowta­rzalna. Preprocesor sprawdza, czy nie jest ona zdefinio­wana (dyrektywa #ifndef). Jeżeli nie jest, definiuje ją (dyrektywa #define) i wstawia tekst definicji klasy do programu, a jeżeli jest, pomija cały tekst aż do dyrektywy #endif. Tak więc definicja klasy zostanie wstawiona do programu tylko raz bez względu na to, ile dyrektyw #include poleca wsta­wienie zawiera­jącego ją pliku nagłów­kowego. W środo­wisku Visual Studio C++ sekwencję dyrektyw #ifndef—#endif można zastąpić dyrektywą #pragma once.

Nową klasę o nazwie Cykloida przydatną w animacji prezentu­jącej powsta­wanie epicy­kloidy i hipocy­kloidy definiujemy w module złożonym z pliku nagłów­kowego cyklo.h i pliku cyklo.cpp (zob. tworzenie nowego modułu w Borland C++, MinGW C++Visual C++). Plik nagłówkowy modułu zawiera dyrektywy włącza­jące dwie biblioteki używane w drugiej części modułu i programie oraz definicję klasy Cykloida precyzu­jącą jej pola i nagłówki metod:

#ifndef H_CYKLO
#define H_CYKLO

#include <cmath>
#include <graphics.h>

class Cykloida
{
    int ri;             // Promień ruchomego okręgu
    int a;              // 1/2 boku obrazka
    int xs, ys;         // Środek ruchomego okręgu
    int xp, yp;         // Koniec ramienia
    double t;
    void(*cykloida)(double, int&, int&, int&, int&);
    byte *pObraz;
public:
    Cykloida(double R, double r, double d, void(*krzywa)(double, int&, int&, int&, int&));
    ~Cykloida();
    void Pokaz();
    void Przesun(double dt);
};

#endif // H_CYKLO

Wszystkie pola klasy Cykloida są prywatne (ustawienie domyślne private, gdy nie podano specyfi­katora dostępu), dostęp do nich mają jedynie metody tej klasy. Z kolei wszystkie metody są publi­czne (specyfi­kator public), można się do nich odwoływać wewnątrz klasy i spoza niej. Ich implemen­tację zawiera drugi plik modułu. Każda metoda jest w nim zdefinio­wana podobnie jak zwykła funkcja z tą różnicą, że jej nazwę poprzedza nazwa klasy i operator zasięgu :: (dwa dwukropki bez odstępu), który określa przyna­leżność metody do danej klasy:

#include "cyklo.h"

Cykloida::Cykloida(double R, double r, double d, void(*krzywa)(double, int&, int&, int&, int&))
{
    ri = int(r);
    a = max(int(fabs(d)), ri) + 7;
    unsigned int n = imagesize(0, 0, 2 * a, 2 * a);
    pObraz = new byte[n];
    cykloida = krzywa;
    circle(getmaxx() / 2, getmaxy() / 2, int(R));
    cykloida(t = 0, xs, ys, xp, yp);
}

Cykloida::~Cykloida()
{
    delete[] pObraz;
}

void Cykloida::Pokaz()
{
    getimage(xs - a, ys - a, xs + a, ys + a, pObraz);
    circle(xs, ys, ri);
    moveto(xs, ys);
    lineto(xp, yp);
}

void Cykloida::Przesun(double dt)
{
    putimage(xs - a, ys - a, pObraz, COPY_PUT);
    cykloida(t += dt, xs, ys, xp, yp);
    lineto(xp, yp);
}

Dyrektywa #include włączająca plik nagłów­kowy modułu jest niezbędna ze względu na wyma­gany dostęp metod klasy Cykloida do definicji klasy oraz biblio­teki matematycznej i graficznej. Ujęcie nazwy pliku nagłówko­wego w cudzy­słowy zamiast nawiasów kątowych oznacza, że plik ten nie znajduje się w standar­dowym folderze plików włącza­nych, lecz w folderze bieżącym zawiera­jącym pliki z kodem źródłowym programu.

Metoda Cykloida jest konstru­ktorem – specjalną metodą wykony­waną podczas tworzenia obiektu, a metoda ~Cykloida destru­ktorem wywoły­wanym automaty­cznie w trakcie niszczenia obiektu. Nazwy tych metod są zawsze takie same jak nazwa klasy, z tym że nazwa destru­ktora jest poprze­dzona znakiem tyldy (~). Obie metody nie zwracają żadnej wartości (nawet typu void), dlatego nie określa się ich typu. Zadaniem konstru­ktora jest zainicjali­zowanie obiektu polega­jące m.in. na nadaniu jego polom odpowie­dnich wartości, zaś destru­ktora wykonanie pewnych czynności czyszczących związanych z likwidacją obiektu. Jeśli żadne dodatkowe czynności przy usuwaniu obiektu nie są podejmo­wane, definio­wanie destru­ktora jest zbędne.

Konstruktor klasy Cykloida przypisuje polom tworzonego obiektu wartości określone w argumen­tach, wyznacza rozmiar tablicy bajtów potrze­bnej w animacji do chwilo­wego przecho­wywania zasłania­nego fragmentu obrazu, przydziela pamięć tej tablicy, rysuje stały okrąg oraz oblicza współ­rzędne środka ruchomego okręgu i końca ramienia w momencie początkowym zero, natomiast destru­ktor zwalnia pamięć przydzie­loną tablicy bajtów, gdy obiekt jest niszczony. Dwie pozostałe metody określają zacho­wanie obiektu. Mianowicie metoda Rysuj kopiuje do tablicy bajtów fragment ekranu, który ma być zamazany przez ruchomy okrąg i przymo­cowane do niego ramię oraz rysuje ten okrąg i ramię, zaś metoda Przesun odtwarza pierwotny wygląd zdezaktua­lizowanego fragmentu ekranu, oblicza współ­rzędne nowego środka ruchomego okręgu i końca ramienia w kolejnym momencie czasowym przesu­niętym o wartość argumentu, a na koniec doryso­wuje odcinek krzywej. Sformuło­wanie algorytmu animacji przy użyciu klasy Cykloida jest bardzo proste:

Cykloida *obiekt = new Cykloida(R, r, d, epic ? epicykloida : hipocykloida);
while (!kbhit())
{
    obiekt->Pokaz();
    Sleep(PRZERWA);
    obiekt->Przesun(DT);
}
delete obiekt;

Program w C++ (wersja 2)

Wersja obiektowa programu konsolowego w C++, który wczytuje z klawia­tury parametry epicy­kloidy lub hipocy­kloidy i prezen­tuje w oknie grafi­cznym proces powsta­wania tej krzywej, może wyglądać nastę­pująco:

#include <iostream>
#include "cyklo.h"

using namespace std;

const int PRZERWA = 20;     // Szybkość animacji
const double DT = 0.025;    // Odstęp czasowy

double R;       // Promień stałego okręgu
double r;       // Promień ruchomego okręgu
double d;       // Długość ramienia
bool epic;      // Epicykloida (true), hipocykloida (false)
int p, q;       // Współrzędne środka okna graficznego

bool parametry()
{
    cout << "Epicykloida/Hipocykloida\n------------------------\n";
    char c;
    do
    {
        cout << "E/H: ";
        cin >> c;
        cin.ignore(1000, '\n');
    } while (c != 'e' && c != 'E' && c != 'h' && c != 'H');
    epic = (c == 'e') || (c == 'E');
    cout << "R : ";
    cin >> R;
    cout << "r : ";
    cin >> r;
    cout << "d : ";
    cin >> d;
    return !cin.fail() && (R > 0) && (r > 0) && (epic || (r < R));
}

void skalowanie()
{
    p = getmaxx() / 2;
    q = getmaxy() / 2;
    int n = 9 * min(p, q) / 10;
    double skala = n / ((epic ? (R + r) : (R - r)) + max(r, fabs(d)));
    R *= skala;
    r *= skala;
    d *= skala;
}

void epicykloida(double t, int &xs, int &ys, int &xp, int &yp)
{
    double fi = t * r / R, psi = t + fi, x, y;
    xp = int((x = (R + r) * cos(fi)) - d * cos(psi)) + p;
    yp = q - int((y = (R + r) * sin(fi)) - d * sin(psi));
    xs = int(x) + p;
    ys = q - int(y);
}

void hipocykloida(double t, int &xs, int &ys, int &xp, int &yp)
{
    double fi = t * r / R, psi = t - fi, x, y;
    xp = int((x = (R - r) * cos(fi)) + d * cos(psi)) + p;
    yp = q - int((y = (R - r) * sin(fi)) - d * sin(psi));
    xs = int(x) + p;
    ys = q - int(y);
}

int main()
{
    if (parametry())
    {
        initwindow(640, 480, epic ? "Epicykloida" : "Hipocykloida");
        skalowanie();
        setcolor(LIGHTCYAN);
        Cykloida *obiekt = new Cykloida(R, r, d, epic ? epicykloida : hipocykloida);
        while (!kbhit())
        {
            obiekt->Pokaz();
            setcolor(WHITE);
            Sleep(PRZERWA);
            obiekt->Przesun(DT);
            setcolor(LIGHTCYAN);
        }
        delete obiekt;
        closegraph();
        return 0;
    }
    else
    {
        cout << "Problem z danymi\n";
        return 1;
    }
}

Uwagi. Kompilator Borland C++ 5.5 traktuje powyższy kod źródłowy jako niepo­prawny i wyprowadza listę ok. 25 błędów w swoich plikach biblio­tecznych (rys.). Problem nie jest odoso­bniony, o czym świadczą liczne zapytania i sugestie na forach interne­towych dotyczące podobnych sytuacji. Najprostszym sposobem przeko­nania się, czy napisany kod jest poprawny, jest przenie­sienie definicji klasy Cykloida i jej metod do głównego pliku programu. Udana kompi­lacja i poprawne działanie testu pozwalają przypuszczać, że źródłem nieprzeno­śności programu z Visual C++ do Borland C++ są pliki nagłów­kowe. Istotnie, problem można rozwiązać, włączając do pliku cyklo.cpp biblio­tekę iostream.

Kompilator MinGW C++ ma również zastrzeżenie do kodu źródło­wego zawartego w pliku cyklo.cpp, uznając nazwę max za niezadekla­rowaną. Błąd można łatwo naprawić, używając np. nazwy kwalifi­kowanej std::max. W obu przypadkach przyczyną kłopotów są niejedno­lite standardy obowią­zujące w różnych środowi­skach progra­mowania.


Warto wspomnieć, że Mikołaj Kopernik (1473-1543), anali­zując traje­ktorie planet Układu Słone­cznego, sformu­łował interesu­jące twier­dzenia dotyczące zaga­dnienia powsta­wania hipocy­kloidy. Prawdzi­wość kilku z nich możemy sprawdzić, posłu­gując się powyż­szymi progra­mami. Jeśli miano­wicie będziemy obserwo­wali ruch koła toczącego się bez poślizgu wewnątrz dwa razy większego okręgu (R=2r), to zobaczymy, że


Opracowanie przykładu: sierpień/wrzesień 2019