FAKULTA ELEKTROTECHNIKY A KOMUNIKAČNÍCH TECHNOLOGIÍ VYSOKÉ UČENÍ TECHNICKÉ V BRNĚ Programování a počítače 2 Počítačová cvičení pro obor B-EST Autor textu: doc. Ing. Jiří Šebesta, Ph.D. 2015 Komplexní inovace studijních programů a zvyšování kvality výuky na FEKT VUT v Brně OP VK CZ.1.07/2.2.00/28.0193
2 FEKT VUT v Brně Obsah ÚVOD... 3 1 ZPRACOVÁNÍ OBRÁZKŮ VE FORMÁTU BMP... 3 2 OBJEKTOVÉ TŘÍDY V C++... 9 3 PŘETÍŽENÍ ČLENSKÝCH METOD TŘÍD A DĚDIČNOST... 13 4 FORMULÁŘOVÁ APLIKACE V C++ PRO WINDOWS... 18 5 GRAFICKÁ APLIKACE PRO WINDOWS S OVLADAČEM GDI... 21 6 MANIPULACE S MATICEMI A 2D GRAFY V MATLABU... 24 7 MODEL FYZIKÁLNÍHO JEVU A 3D GRAFY V MATLABU... 27 8 PARAMETRICKÉ 3D GRAFY V MATLABU... 30 9 MODEL KOMUNIKAČNÍHO SYSTÉMU V MATLABU... 36 10 GRAFICKÉ UŽIVATELSKÉ ROZHRANÍ V MATALABU... 39 ODPOVĚDI KE KONTROLNÍM OTÁZKÁM... 42
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 3 Úvod Tato skripta jsou určena pro studenty předmětu Programování a počítače 2 pro obor Elektronika a sdělovací technika, zkr. BPC2E. Obsahují podrobné návody k počítačovým cvičením uvedeného předmětu. Předmět bezprostředně navazuje na předmět Programování a počítače 1, jehož hlavní náplní bylo seznámit posluchače kurzu se základy programovacího jazyka C dle standardu ANSI. První cvičení je ještě zaměřeno na klasický jazyk C a jeho využití při manipulaci s binárními soubory. Následující čtyři cvičení jsou věnovány objektově orientovanému programování s využitím jazyka C++. Z toho první dvě úlohy jsou orientovány na objasnění základních pojmů a přístupů objektově orientovaného programování, zatím co druhé dvě úlohy jsou pak zaměřeny na řešení jednoduchých formulářových aplikací s využitím Microsoft Visual studia. Další skupina úloh zahrnuje základní vzorové úlohy v Matalbu, postupující od jednoduchých manipulací s maticemi, přes dvoudimezionání a třídimenzionální grafy až po ukázku sestavení modelu komunikačního systému včetně sestavení grafického uživatelského rozhraní pro tento model. Základy Matlabu, který je obecně nástrojem pro modelování systémů a signálů s prvky programovaní, jsou nutné jako prerekvizita k řadě navazujících předmětů oboru Elektronika a sdělovací technika. Jmenujme např. předmět Signály a systémy nebo Číslicové zpracování signálů. Každá úloha obsahuje zadání a rozbor veřejně přístupné úlohy, kterou si studenti mohou nachystat v rámci domácí přípravy. Druhou částí je zadání B, které je přístupné vždy až na začátku vlastního cvičení. Třetí částí je bonusová úloha, která je nadstavbou pro samostatné zdokonalení studenta v dané problematice. Rozbory jednotlivých úloh obsahují rovněž části stěžejního zdrojového kódu s vysvětlením. Na závěr každé kapitoly je uvedena doporučená literatura a tři kontrolní otázky, jejichž odpovědi jsou uvedeny v závěrečné kapitole těchto skript. Součástí úloh je i předpřipravený kód, někdy je to pouze kostra, v jiných případech to může být poměrně rozsáhlý zdrojový kód, který je nutné prostudovat a doplnit. Zdrojové kódy k jednotlivým úlohám jsou k dispozici na www stránkách: http://www.urel.feec.vutbr.cz/~sebestaj/bpc2e/cviceni.htm. 1 Zpracování obrázků ve formátu BMP Cílem je seznámit se s programováním binárních souborů, v případě tohoto cvičení pak s bitmapovými obrázky. Úloha A. Pro tuto úlohu je připraven zdrojový kódu programu k příkladu souboru bpc2e_c01a.c, kde je připravena kompletní úloha, která vygeneruje nekompresovaný bitmapový obrázek (viz obr. 1.1). Na začátku programu jsou kompletně připraveny potřebné hlavičky pro BMP soubory s plným 24bitovým rozlišení barev (RGB barvy po 8 bitech) bez komprese. Pro definici parametrů obrázku v hlavičkách souboru jsou využity předdefinované struktury BITMAPFILEHEADER a BITMAPINFOHEADER z knihovny windows.h. Popis jednotlivých proměnných těchto struktur je uveden v tabulkách 1.1 a 1.2. Jediný parametr obrázku, který je třeba nastavit je šířka a výška v pixelech pomocí maker WIDTH a HEIGHT:
4 FEKT VUT v Brně // size of picture definition #define WIDTH 512 #define HEIGHT 512 Tab 1.1. Definice proměnných struktury BITMAPFILEHEADER. Název položky bftype bfsize bfreserved1 bfreserved2 bfoffbits Délka položky 2 byty 4 byty 2 byty 2 byty 4 byty Význam Identifikátor formátu BMP. Aktuální verze formátu BMP zde obsahuje ASCII kód znaků "BM", tj. 0 42 a 0 4D. Celková velikost souboru s obrazovými údaji. Některé aplikace tuto položku ignorují a dosazují zde nulu. Rezerva pro pozdější použití. V současné verzi formátu BMP zde musí být uložena nulová hodnota. Rezerva pro pozdější použití. V současné verzi formátu BMP zde musí být uložena nulová hodnota. Posun struktury BITMAPFILEHEADER od začátku vlastních obrazových dat. Název položky Tab 1.2. Definice proměnných struktury BITMAPINFOHEADER. Délka položky Význam bisize 4 byty Celková velikost datové struktury BITMAPINFOHEADER biwidth 4 byty Šířku obrázku v pixelech biheight 4 byty Výšku obrázku v pixelech biplanes Počet bitových rovin pro výstupní zařízení. V BMP vždy hodnota 2 byty 1. Položka existuje z historických důvodů. Celkový počet bitů na pixel. Podle počtu barev zde mohou být bibitcount 2 byty hodnoty 1, 4, 8 nebo 24 (to odpovídá postupně 2, 16, 256ti barvám popř. plnobarevnému režimu). bicompression 4 byty Typ komprimační metody obrazových dat. Musí být nastavené na jednu z hodnot: 0 (BI_RGB), 1 (BI_RLE8) nebo 2 (BI_RLE4). bisizeimage 4 byty Velikost obrazu v bytech. Pokud je bitmapa nekomprimovaná, může zde být nulová hodnota, protože ji je možno vypočítat z rozměrů obrázků a počtu bitů na pixel. bixpelspermete r biypelspermete r biclrused biclrimportant 4 byty 4 byty 4 byty 4 byty Horizontální rozlišení výstupního zařízení v pixelech na metr. Většina aplikací nemá potřebné informace o výstupním zařízení, a proto do této položky vkládá hodnotu 0. Vertikální rozlišení výstupního zařízení v pixelech na metr. Většina aplikací nemá potřebné informace o výstupním zařízení, a proto do této položky vkládá hodnotu 0. Celkový počet barev, které jsou použité v dané bitmapě. Jestliže je tato hodnota nastavena na nulu, bitmapa používá maximální počet barev. Počet barev, které jsou důležité pro vykreslení bitmapy. Pokud je tato hodnota nulová, jsou všechny barvy důležité. Tento údaj je používán při zobrazování na zařízeních, které mají omezený počet současně zobrazitelných barev. Ostatní položky struktur jsou již předdefinovány, pro BITMAPFILEHEADER: bmp_fh.bftype = 19778; // BITMAPFILEHEADER specification bmp_fh.bfsize = WIDTH * HEIGHT * 3 + 54; bmp_fh.bfreserved1 = 0; bmp_fh.bfreserved2 = 0; bmp_fh.bfoffbits = 54;
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 5 i pro BITMAPINFOHEADER: bmp_ih.bisize = 40; // BITMAPINFOHEADER specification bmp_ih.biwidth = WIDTH; bmp_ih.biheight = HEIGHT; bmp_ih.biplanes = 1; bmp_ih.bibitcount = 24; bmp_ih.bicompression = 0; bmp_ih.bisizeimage = WIDTH * HEIGHT * 3; bmp_ih.bixpelspermeter = 0; bmp_ih.biypelspermeter = 0; bmp_ih.biclrused = 0; bmp_ih.biclrimportant = 0; Hlavičky jsou následně ve správném pořadí nahrány do souboru test.bmp pomocí funkce fwrite(): fwrite(&bmp_fh, sizeof(bmp_fh), 1, fbmp); // write BITMAPFILEHEADER fwrite(&bmp_ih, sizeof(bmp_ih), 1, fbmp); // write BITMAPINFOHEADER V poslední části programu jsou ve dvou vnořených cyklech definovány barvy jednotlivých pixelů a nahrávány do souboru. Pixelová data jsou BMP dvacetičtyřbitovém souboru nahrávána v pořadí RGB (každá barva má 1 byte, a její úroveň od 0 do 255 jako unsigned char) a to postupně po řádcích od levého dolního rohu po pravý horní roh. Každý pixel je definován tripletem těchto úrovní pro červenou, zelenou a modrou složku barvy pixelu. Tento triplet je shrnut ve struktuře bmp_rgb typu RGBTRIPLE. Ve vnořených cyklech se postupně definují a ukládají opět pomocí funkce fwrite() jednotlivé pixely. V příkladu je vidět, že složka červené a modré je vždy nulová, zatímco úroveň zelené barvy je závislá na poloze pixelu. Výsledkem je tedy obrázek s měnící se úrovní zelené barvy od levého dolního rohu po pravý horní roh (viz. obr. 1.1), protože hodnota úrovně zelené barvy je přímo úměrně závislá na součtu indexů řádku a sloupce pro daný pixel: // part for bitmap content definition for(h = 0; h < HEIGHT; h++) // rows in bitmaps from bottom to top { for (v = 0; v < WIDTH; v++) // columns in bitmaps from left to right { bmp_rgb.rgbtred = 0x00; bmp_rgb.rgbtgreen = (h+v)%256; bmp_rgb.rgbtblue = 0x00; fwrite(&bmp_rgb, sizeof(bmp_rgb), 1, fbmp); } } Tzn., že všechny pixely, kde součet h+v je konstantní budou mít stejnou barvu. Pochopitelně je třeba omezit úroveň barvy na rozsah 0 až 255, což lze provést snadno operací zbytku po celočíselném dělení 256. Zkuste pro začátek vymyslet jiný vztah pro výpočet úrovně zelené barvy pixelu, např. při aplikaci následujícího vztahu:
6 FEKT VUT v Brně bmp_rgb.rgbtgreen = (cos(2*6.28*v/(1.0*width))+1)*127; získáte krásnou fotografii svislých trubek včetně jasového přechodu do stínu (viz. obr. 1.2). Zkuste zapřemýšlet proč? Nezapomeňte na přilinkování knihovny math.h. Obr. 1.1. Výsledný bitmapový obrázek podle vzoru z příkladu A. Obr. 1.2. Výsledný experimentální obrázek při aplikaci funkce kosinus.
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 7 Vaším samostatným úkolem je upravit právě část kódu pro definici pixelů obrázku tak, aby výsledkem byl obrázek podle obr. 1.3. Hodnocení: 1,5 bodu. Úloha B. Obr. 1.3. Požadovaný výsledný bitmapový obrázek pro příklad A. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu. Bonusová úloha Pro nadšené programátory i další zájemce je tu opět připravená bonusová úloha. V souboru TPR.bmp je uložen výřez terénního reliéfu jistého území v 256 úrovních šedi od černé definující nejnižší výškovou hladinu až po bílou definující nejvyšší výškovou hladinu. Cílem úlohy je sestavit program, který pro definovanou hladinu moře odpovídající příslušné hodnotě úrovně šedi vygeneruje nový obrázek mapy souše a moře. V řešení může být souše definována zelenou barvou, moře logicky modrou barvou. Číselným parametr hladiny moře v rozsahu 0 až 255 nechť je argumentem při spouštění programu. Parametrem při spouštění může být také jméno výstupního BMP souboru. Terénní reliéf ve stupních šedi je na obrázku 1.4. Jsou zde patrná vyvýšená místa i místa nižší, která připomínají říční odtoky z horské oblasti. Příklad výsledné BMP mapy souše a moře je uveden pro daný reliéf a hladinu moře 80 na obrázku 1.5.
8 FEKT VUT v Brně Kontrolní otázky 1.1) Jaké jsou hodnoty položek ve struktuře typu RGBTRIPLE při plnobarevném bitmapovém formátu obrázku, pokud definují barvu pixelu v úrovni šedi? 1.2) Co reprezentuje sizeof() ve funkci fwrite()? 1.3) Kolik barev je možné nadefinovat při plnobarevném bitmapovém formátu obrázku? Obr. 1.4. Terénní reliéf v úrovních šedi. Obr. 1.5. Vygenerovaná mapa souše a moře pro hladinu moře 80.
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 9 Literatura [1.1] WRÓBLEWSKI, P. Algoritmy. Datové struktury a programovací techniky. Brno: Computer Press, 2004. [1.2] KERNIGHAN, B. W., RITCHIE, D. M. Programovací jazyk C. Brno: Computer Press, 2006. [1.3] SOBOTA, B., MILIÁN, J. Grafické formáty. České Budějovice: KOOP nakladatelství, 1996. 2 Objektové třídy v C++ Cílem je seznámit se základy objektového programování a sestavování vlastních tříd v C++. Úloha A. Pro tuto úlohu je připraven funkční zdrojový kód k příkladu bpc2e_c02a.cpp. V tomto programu jsou deklarovány a definovány metody tříd pro objekt typu bod class Point, přičemž jako privátní proměnné jsou specifikovány souřadnice bodu ve 2D prostoru X a Y a jako metody jsou připraveny inline funkce pro přístup k privátním souřadnicím bodu: // CLASS POINT class Point // coordinates X,Y { public: void SetPoint(uch x, uch y) {X = x; Y = y;} void SetX(uch x) {X = x;} void SetY(uch y) {Y = y;} uch GetX()const {return X;} uch GetY()const {return Y;} private: uch X; uch Y; }; Další deklarovaná a definovaná třída je pro objekt typu úsečka class Line, která využívá již definované třídy Point pro krajní body úsečky (opět privátní proměnné) a skupinu přístupových metod k těmto krajním bodům: // CLASS LINE class Line // points P1, P2 { public: void SetLine(Point p1, Point p2) {P1 = p1; P2 = p2;} Point GetP1()const {return P1;} Point GetP2()const {return P2;} private: Point P1; Point P2;
10 FEKT VUT v Brně }; A nakonec třída pro objekt typu trojúhelník class Triangle, který využívá jak objektů třídy Point, tak i Line: // CLASS TRIANGLE class Triangle // points P1, P2, P3 { public: void SetTriangle(Point p1, Point p2, Point p3); Line GetL12()const {return L12;} Line GetL23()const {return L23;} Line GetL31()const {return L31;} private: Point P1; Point P2; Point P3; Line L12; Line L23; Line L31; }; void Triangle::SetTriangle(Point p1, Point p2, Point p3) { P1 = p1; P2 = p2; P3 = p3; L12.SetLine(P1, P2); L23.SetLine(P2, P3); L31.SetLine(P3, P1); } Prostudujte uvedený zdrojový kód, především pak provázanost objektů daných tříd. Všechny tyto třídy mají nadefinovány potřebné přístupové metody pro nastvaní a zjištění proměnných příslušných objektů. Poslední deklarovanou třídou je class Canvas, která reprezentuje kreslící plátno s definovatelnými rozměry: // CLASS CANVAS class Canvas // height, width, array of character as a pixels of canvas { public: int SetCanvas(uch width, uch high); // set size of canvas uch GetWidth() const {return W;} // return width of canvas uch GetHeight() const {return H;} // return height of canvas void ClearCanvas(void); // clear canvas void PrintCanvas(void); // display canvas on the screen void AddFrame(char pattern); // add frame in the edges of canvas
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 11 int AddPoint(Point point, char pattern); // add point int AddLine(Line line, char pattern); // add line int AddTriangle(Triangle tri, char pattern); // add triangle private: uch H; uch W; char Canv[200][200]; }; int Canvas::SetCanvas(unsigned char width, unsigned char height) { } void Canvas::ClearCanvas(void) { } Protože pro jednoduchost pracujeme stále v konzolových aplikacích, jeden bod (pixel) plátna je reprezentován jedním zobrazitelným znakem. Statická proměnná Canv[200][200] je výsledným 2D polem pro vkládání kreslených znaků (můžeme si představit, že představují barvu bodu nebo typ čáry). Finální rozměr plátna je určen proměnnými W a H (v příkladu rozměr 160 x 80). Ve třídě Canvas jsou kromě očekávaných metod pro inicializaci, smazání a zobrazení plátna na konzoli ještě metody pro vložení rámu na plátno a vložení výše zmíněných objektů typu bod, úsečka a trojúhelník. Poměrně komplikovaně vypadá metoda pro vložení úsečky. Důvodem je ošetření všech možností, které mohou nastat z hlediska souřadnic koncových bodů tak, aby se úsečka vykreslovala vždy správně plnou čarou. Jak bude vypadat výsledný bod (pixel) na plátně je dáno znakem pattern. Ve funkci main() je pak ukázáno vytváření objektů a volání příslušných metod předdefinovaných tříd. Výsledkem je obrázek skládající se ze tří bodů, dvou úseček a trojúhelníku (obr. 2.1). Souřadnice {0,0} jsou v levém dolním rohu (viz metoda Canvas::PrintCanvas()). Vaším úkolem je sestavit vlastní novou třídu pro obecný čtyřúhelník a ve třídě Canvas doplnit metodu pro jeho vložení na plátno. Ve funkci main() pak proveďte vygenerování a vykreslení dvou čtyřúhelníků a dvou trojúhelníku a jejich vykreslení podle obr. 2.2, volba souřadnic koncových bodů úseček není kritická. Hodnocení: 1,5 bodu. Úloha B. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu.
12 FEKT VUT v Brně Obr. 2.1. Výsledný obrázek podle vzoru z příkladu A. Obr. 2.2. Požadovaný výsledný obrázek využívající třídu pro obecný čtyřúhelník pro příklad A. Bonusová úloha Pro zájemce a nadšené programátory je tu opět připravená bonusová úloha, která tentokráte navazuje na úlohu A. Cílem je vytvořit třídu pro vykreslování číslic podobně jako na sedmisegmentovém displeji (nebo na kalkulačce). Vhodné je jako objekt této třídy definovat jednu číslici s umístěním a velikostí. Na obrázku 2.3 je ukázka výsledku pro různé velikosti, pro jeden pixel je použit předdefinovaný znak BLOCK. (znak s ASCII kódem 219). Výhodné je si v třídě pro digitální číslo definovat proměnnou typu char, jejíž jednotlivé bity
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 13 budou odpovídat příslušným segmentům (bude-li daný bit roven 1 bude segment vykreslen, v případě, že bude tento bit roven hodnotě 0, příslušný segment vykreslen nebude). Překódování číslice do této proměnné je možné provádět přímo při inicializaci proměnných vytvořeného objektu a pak předávat vykreslovací metodě ve třídě Canvas. Kontrolní otázky Obr. 2.3. Příklad výsledku bonusové úlohy. 2.1) Co způsobí následující kód ve funkci main(): Point P; int ax; ax = P.X;? Předpokládáme, že třída Point je definována shodně jako v úloze A. 2.2) K čemu slouží modifikátor const v hlavičce metody, např. uch GetX()const;? 2.3) Jaké členy (položky) byste definovali ve třídě pro objekt typu kružnice a současně typu kruhu? Literatura [2.1] WRÓBLEWSKI, P. Algoritmy. Datové struktury a programovací techniky. Brno: Computer Press, 2004. [2.2] VIRIUS, M. Od C k C++. České Budějovice: KOOP nakladatelství, 2002. [2.3] PROKOP, J. Algoritmy v jazyku C a C++. Praha: GRADA, 2008. 3 Přetížení členských metod tříd a dědičnost Cílem je seznámit se dalšími atributy objektového programování, které jsou zaměřené na přetěžování členských metod a využitím dědičnosti.
14 FEKT VUT v Brně Úloha A. Pro tuto úlohu je připraven zdrojový kódu rozpracovaného programu k příkladu v souboru bpc2e_c03a.cpp. Úloha řeší jednoduchý objektový projekt pro správu studentů. V uvedeném zdrojovém souboru je nejprve vytvořena třídy class Date pro objekt typu datum: // CLASS DATE class Date // day, month, year { public: int SetDate(char* new_date); // in form DD.MM.YYYY char* GetDate() const; int GetDayVal() const {return Day + 31*Month + 366*Year;} protected: int Day; int Month; int Year; }; int Date::SetDate(char *new_date) { } char* Date::GetDate() const { } a následně třída class PersID pro objekt typu rodné číslo: // CLASS PERSID class PersID:public Date // child of Date + Tail, Sex { public: int SetPID(char* new_pid); char* GetPID() const; char GetSex() const {return Sex;} private: int Tail; // PID tail char Sex; // Sex 'f' - woman, 'm' - man }; int PersID::SetPID(char *new_pid) { }
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 15 char* PersID::GetPID() const { } Ze zdrojového textu programu je patrné, že třída PersID je vytvořena jako dědic třídy Date. Je to logické, protože rodné číslo nese datum narození a doplňující unikátní čtyřmístné číslo (za lomítkem). V obou třídách jsou připraveny přístupové metody zahrnující komplexní ochranu vstupujících dat (až na kontrolu 29. února v nepřestupné roky). Vašim prvním úkolem je doplnit třídu Date o přetíženou funkci (metodu) pro nastavení vstupního data. V třídě je vyřešena funkce pro nastavení data zadaného jako řetězec v klasickém tvaru DD.MM.YYYY, (kde DD je číselná hodnota dne v měsíci, MM je pořadí měsíce v roce a YYYY je rok. Hlavička této metody je následující: int Date::SetDate(char *new_date); Doplňte tedy třídu Date o přetíženou metodu, která je přístupovou metodou pro nastavení položek data, přičemž den, měsíc a rok jsou tři nezávislé parametry metody. Hlavička takové přetížené metody by měla mít tvar: int Date::SetDate(int new_d, int new_m, int new_y); V této metodě proveďte podobné ošetření jako v případě již definované metody int Date::SetDate(char *new_date). Ve funkci main() pak ověřte, že obě přetížené metody jsou funkční. Například při následujícím testu: char txt1[100]; Date D; // object of date D.SetDate("12.6.2015"); strcpy(txt1, D.GetDate()); cout << endl << txt1 << endl; D.SetDate(12, 12, 2000); strcpy(txt1, D.GetDate()); cout << endl << txt1 << endl; by se měly všechny položky data u obou objektů správně vložit a výsledkem by měl být výpis podle obrázku 3.1. Obr. 3.1. Výpis dat zadaných různými způsoby pomocí přetížených metod do objektu typu Date. Prozatím nepoužitý kód zakomentujte blokovým komentářem /* */). Dále si prostudujte příklad vytvoření potomka třídy PersID z rodičovské třídy Date. To se totiž bude hodit ve druhé části úkolu, kdy je třeba z definované třídy Person (osoba) vytvořit dědičnou třídu
16 FEKT VUT v Brně Student. Třída Person obsahuje chráněné proměnné jméno, příjmení, adresa a rodné číslo (s výhodou je využito výše definované třídy PersID pozor toto není dědičnost jen využití jiné komponenty tedy třída ve třídě, někdy je to označováno jako kompozice): // CLASS PERSON class Person // FName, SName, Addr, PID { public: int SetPerson(char* new_fname, char* new_sname, char* new_addr, char* new_pid); char* GetPerson() const; // int GetBirthDateVal() const; protected: char FName[10]; char SName[10]; char Addr[30]; PersID PID; }; int Person::SetPerson(char* new_fname, char* new_sname, char* new_addr, char* new_pid) { } char* Person::GetPerson() const { } Odvozená třída Student pak má privátní položky Spec (obor), SYear (rok studia), Qual (el. kvalifikace získaný paragraf) a datum platnosti kvalifikace QValid (opět třída Date ve třídě): // CLASS STUDENT class Student // child of Person + Spec, SYear, Qual, QValid { public: // ADD METHODS private: char Spec[6]; // specialization, e.g. BEST int SYear; // study year int Qual; // degree (paragraph) of el. qualification Date QValid; // date of validity of el. qualification }; // TO DO METHODS
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 17 Vaším úkolem je, jak je patrné z uvedeného zdrojového kódu pro třídu Student, definovat kýženou dědičnost a doplnit přístupové metody podobně jako v případě třídy Person. Použití přístupových metod je patrné z funkce main(), kde je nadeklarováno fixně 10 studentů v poli Student S[10] (jde tedy o pole objektů). Všechny datové položky (členy) jsou ve funkci main() naplněny, níže je uveden příklad pro prvního studenta Jaroslava Nováka s indexem 0: S[0].SetPerson("Jaroslav", "Novak", "V lesiku 128 ", "960105/1361"); S[0].SetStudent("BEST", 1, 4, "13.6.2015"); V metodě SetStudent() se již využívá nové přetížené metody pro nastavení data. V reálném použití by se mohly údaje o studentech načítat ze souboru a noví studenti přidávat z klávesnice a pochopitelně s dynamickým přidáváním objektů typu student. V funkci main() je rovněž připraven cyklus, který vytiskne údaje o všech studentech: cout << endl << "List of students:" << endl; for(i = 0; i < num_st; i++) { strcpy(txt1, S[i].GetPerson()); strcpy(txt2, S[i].GetStudent()); cout << i+1 << ". " << "\t" << txt1 << " " << txt2 << endl; } Ze zdrojového kódu je tedy zřejmé, že je nutné sestavit přístupové metody třídy Student, a to pro vkládání dat do objektu typu Student metodu SetStudent() a pro čtení dat z objektu typu Student metodu GetStudent(). Obě tyto metody přistupují pouze k novým členům ve třídě Student, pro děděné členy lze s výhodou použít metody ze třídy Person. Po správném doplnění kódu příkladu byste měli získat výpis studentů s údaji podle obrázku 3.2. Obr. 3.2. Výpis seznamu studentů včetně osobních a studijních údajů pro příklad A. Hodnocení: 1,5 bodu. Úloha B. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu.
18 FEKT VUT v Brně Bonusová úloha Pro doplňující bonusovou úlohu byla vybrána klasická aplikace výběru objektů na základě hodnoty jednoho vybraného parametru (člena) objektu. Pro řešení tohoto úkolu je již potřeba mít k dispozici hotové řešení zadání A. Cílem je doplnit funkci main() o algoritmus, který na základě zadané el. kvalifikace (získaný paragraf studentem) vypíše pouze studenty, kteří mají el. kvalifikaci shodnou se zadanou hodnotou. Pro zadání výběrové el. kvalifikace využijte standardní vstup cin. Vzor kódu je uveden níže, přičemž stačí nasměrovat vstup do deklarované celočíselné proměnné: int num; cout << "Insert par. of el. qualification: "; cin >> num; Doplňte tedy funkci main() o výběr studentů dle požadované el. kvalifikace. S výhodou využijte přístupové inline metody GetQual(). Studenty splňující zadané kritérium el. kvalifikace včetně jejich personálních a studijních údajů vytiskněte. Příklad výpisu pro zadanou kvalifikaci s paragrafem 4 je uveden na obrázku 3.3. Obr. 3.3. Výpis seznamu studentů včetně osobních a studijních údajů, kteří mají el. kvalifikaci dle paragrafu 4. Kontrolní otázky 3.1) Jak program pozná, kterou z přetížených metod má v dané situaci použít? 3.2) Jakým způsobem lze ověřit správné zadání rodného čísla? 3.3) Jakým způsobem lze modifikovat zděděnou členskou metodu? Literatura [3.1] DOSTÁL, R. C/C++. Hotová řešení. Brno: Computer Press, 2010. [3.2] VIRIUS, M. Od C k C++. České Budějovice: KOOP nakladatelství, 2002. [3.3] PROKOP, J. Algoritmy v jazyku C a C++. Praha: GRADA, 2008. 4 Formulářová aplikace v C++ pro Windows Cílem je seznámit se s vytvářením jednoduchých aplikací v C++ typu WindowsForm pro OS Windows s využitím integrovaného prostředí Microsoft Visual Studia.
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 19 Úloha A. Pro tuto úlohu tentokrát nejsou připraveny žádné zdrojové kódy. Cílem je, abyste si vyzkoušeli kompletní návrh jednoduchého projektu v MSVS2013, počínaje nastavení formulářové aplikace, přes definici vizuální podoby programu, až po doplnění automaticky vygenerovaného kódu k vizuální podobě programu o požadované ošetření událostí (vlastní metody pro dané ovládací prvky). Požadovaná aplikace by měla být jednoduchou kalkulačkou pro komplexní čísla se základními operacemi pro dvě komplexní čísla (součet, rozdíl, násobek a podíl). Připravte si v MSVS 2013 (případně i jiné verzi) projekt formulářové aplikace typu WidowsForm (podle postupu z přednášek). Ve druhém kroku vytvořte vizuální podobu formuláře aplikací vhodných ovládacích prvků (inspirujte se projektem BPC2E_Ex114 z přednášek). Bude potřeba ve formuláři aplikace definovat čtyři editační okna (TextBox) pro vstup reálné a imaginární části dvou vstupních čísel (např. A a B) a 2 textová pole (Label) pro zobrazení výsledku operace. Další textová pole je vhodné definovat pro pomocné texty jako je real., imag. apod. Formulář dále doplňte o čtyři tlačítka pro jednotlivé matematické operace. V závěrečné fázi doplňte projekt o metody událostí kliknutí na tlačítka pro jednotlivé matematické operace. Tedy pro implementovaná tlačítka vygenerujte funkce pro ošetření události kliknutí na tlačítko metody OnClick, do nichž vložte kód pro načtení hodnot vstupních čísel z editačních oken, výpočet dané komplexní operace voláním vaší knihovní funkce a zobrazení výsledků v textových polích. Pro čtení hodnot z editačních TextBoxů a pro tisk hodnot do textových polí výsledků Label je výhodné sestavit jednoduché funkce (podobně jako ve vzorovém projektu z přednášky BPC2E_Ex114). V těchto funkcích je potřeba použít konverzi proměnné objektového řetězce třídy String na vhodný číselný formát, např. double. K tomuto účelu lze využít konverzní metody ToDouble, jejímž parametrem je položka ovládacích prvku nesoucí řetězec typu String, například takto: A_real = System::Convert::ToDouble(this->text_Are->Text); kde A_real je proměnná typu double, do níž konvertujeme řetězec z TextBoxu text_are (editační okno pro reálnou část prvního operandu) a Text je položka nesoucí řetězec zapsaný do daného editačního okna, který je typu String. Protože při operacích nad komplexními čísly jsou výsledkem dvě hodnoty, reálná a imaginární část výsledné hodnoty, použijte v knihovních funkcích pro výpočty nad komplexními čísly předávání výsledku odkazem. Volání takové knihovní funkce může vypadat například takto: cmul(&re, &im, get_are(), get_aim(), get_bre(), get_bim()); Proměnné re a im jsou výstupy komplexní operace a funkce get_xyy() zajišťují návratové hodnoty typu double, které jsou konvertovány z řetězců typu String z editačních oken. Číselné hodnoty re a im je pak třeba překonvertovat pomocí metody ToString() na typ String pro zobrazení v příslušném grafickém objektu na formuláři aplikace (u objektu typu Label je cílem položka Text): this->resre->text = System::Convert::ToString(re); kde Resre je objekt typu Label. Příklad funkční podoby formulářové aplikace je uveden na obrázku 4.1.
20 FEKT VUT v Brně Hodnocení: 1,5 bodu. Úloha B. Obr. 4.1. Příklad formulářového okna pro komplexní kalkulačku. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu. Bonusová úloha Sestavte jednoduchou formulářovou aplikaci pro Windows, která náhodně vygeneruje losování dvou tahů sportky. Připravte si vhodný formulář, který bude obsahovat dvě tlačítka pro spuštění náhodného generování a textová pole label pro jednotlivá vylosovaná čísla. V projektu rovněž sestavte nezávislou funkci, která vygeneruje vektor sedmi celých čísel v rozsahu 1 až 49, přičemž žádné se nesmí opakovat. Použijte starý známý algoritmus z jazyka C s aplikací funkcí srand() a rand() z knihovny stdio.h a pomocné funkce time(null) z knihovny time.h pro vstupní hodnotu inicializace semínka náhodného generátoru. Pro jednoduchost můžete tuto pomocnou funkci vložit přímo do hlavičkového souboru formuláře za #pragma endregion. Protože generujete vektor sedmi čísel, musíte použít předávání tohoto vektoru odkazem. V této funkci současně proveďte i třídění pro prvních šest čísel od nejmenšího po největší (dodatkové na sedmé pozici nesmí být zahrnuto) tak, aby zobrazení vylosovaných čísel vypadalo podobně jako u zobrazení rekapitulace losování v televizním přenosu. Jako reakci na stisk tlačítka zavolejte uvedenou funkci a výsledná tažená čísla vložte do příslušných objektů label. Výsledné okno aplikace s vygenerovanými tahy sportky je uvedeno na obrázku 4.2. Je možné si ze cvičných důvodů nadefinovat i vlastní jiný formulář aplikace. Kontrolní otázky Obr. 4.2. Příklad zobrazení výsledků generátoru pro sportku ve formuláři. 4.1) Proč se ve formulářové aplikaci pro kalkulačku nad komplexními čísly používá ke zobrazení racionálního čísla desetinná čárka? 4.2) Bylo by vhodné pro nějaký účel definovat ve formulářové aplikaci pro kalkulačku nad komplexními čísly určitou vlastní třídu? Popište její členy a metody.
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 21 4.3) K čemu slouží ve zdrojovém kódu hlavičkového souboru formuláře kód using namespace? Literatura [4.1] DOSTÁL, R. C/C++. Hotová řešení. Brno: Computer Press, 2010. [4.2] FORD, S. 266 tipů a triků pro Microsoft Visual Studio. Brno: Computer Press, 2009. [4.3] VIRIUS, M. Od C k C++. České Budějovice: KOOP nakladatelství, 2002. 5 Grafická aplikace pro Windows s ovladačem GDI Cílem cvičení je procvičit si práci s 2D grafickým ovladačem GDI, který je nejjednodušším nástrojem pro tvorbu grafických aplikací ve Windows. Úloha A. Sestavte a odlaďte aplikaci jednoduché formulářové aplikace pro Windows, ve které se bude graficky zobrazovat průběh měsíčních teplot během zvoleného roku. Vaším prvním dílčím úkolem je doplnit funkci pro načtení teplot GetTemp(), která je připravena v knihovně temp.h a která využívá globální proměnnou double temp[16][12], definující pole teplot s průměrnými měsíčními teplotami od roku 1995 (první řádek, sloupce jsou měsíce). Připravený formulář aplikace MyForm.h obsahuje objekt typu NumericUpDown pojmenovaný NUD pro definici roku (od 1995 do 2010 včetně). Formulář aplikace dále obsahuje objekt typu PictureBox se jménem PB pro zobrazení teplotního grafu (viz obr. 5.1). Ten má již předpřipravené grafické výstupy pro tisk mřížky tabulky s popisem os ve funkci pro událost Paint(): System::Void PB_Paint(System::Object^ sender, System ::PaintEventArgs^ e) { int n, year; double temp[12]; array<point>^ GraphPoints = gcnew array<point>(12); Graphics ^ g = e->graphics; Pen^ GridPen = gcnew Pen(Color::Black,1.0f); Pen^ FramePen = gcnew Pen(Color::Black,3.0f); SolidBrush^ PointBrush = gcnew SolidBrush(Color::Blue); Pen^ GraphPen = gcnew Pen(Color::Blue,2.0f); System::Drawing::Font^TextFont = gcnew System::Drawing::Font("Arial", 12); SolidBrush^ TextBrush = gcnew SolidBrush( Color::Black ); e->graphics->drawrectangle(framepen, 100, 50, 550, 300); // frame for (n=0; n<12; n++) // vertical lines e->graphics->drawline(gridpen, 100+n*50, 50, 100+n*50, 355);
22 FEKT VUT v Brně e->graphics->drawstring("jan", TextFont, TextBrush, 80, 360); for (n=0; n<13; n++) // horizontal lines e->graphics->drawline(gridpen, 95, 50+n*25, 650, 50+n*25); e->graphics->drawstring("+40 C", TextFont, TextBrush, 40, 40); } // to do - printing temperature curve V první části jsou nadefinovány objekty kreslících nástrojů, jako jsou pera, štětce a fonty písma. Následuje vykreslení obdélníku pro umístění grafu a systém vertikálních a horizontálních čar definujících mřížku grafu. K příslušným čarám jsou pak dodefinovány texty pro zkratky měsíců a hodnoty teplot. Tuto část je možné dále zjednodušit definicí pole řetězců a aplikací cyklu. Pro názornost je ponecháno uvedené řešení s výpisem jednotlivých textů nezávisle. Váš další úkol je doplnění této funkce o zobrazení průběhu teploty pro daný rok, kód se umístí místo komentáře // to do - printing temperature curve. Pro vykreslení využijte metodu e DrawCurve(), která křivku teplot automaticky vyhladí pomocí metody splinů. Tato metoda má jeden ze vstupních parametrů pole bodů array<point>^ (tedy pole bodů, kterými bude křivka proložena). Toto pole bodů je třeba nejprve deklarovat následujícím způsobem: array<point>^ GraphPoints = gcnew array<point>(12); Touto konstrukcí je deklarováno pole bodů s názvem GraphPoints s počtem dvanácti bodů (počet bodů křivky je v kulatých závorkách). K jednotlivým položkám pak přistupujeme standardně jako u pole struktur nebo objektů (objekt Point obsahuje dvě položky X (souřadnice bodu x) a Y (souřadnice bodu y), např. GraphPoints[3].X = 120; vloží na pozici souřadnice x třetího (resp. čtvrtého) bodu hodnotu 120. Přepočet teplot a měsíců na souřadnice proveďte na základě rozboru kódu pro generování mřížky tabulky. Při vykreslování grafu vhodným způsobem zvýrazněte prokládané body např. pomocí metody FillEllipse(). Posledním úkolem je sestavení funkce pro událost změny roku v NUD, kde je třeba vždy při změně volat překreslení grafu s teplotami pro vybraný rok konstrukcí: this->pb1->refresh(); kde PB je objekt příslušné PictureBoxu, který chceme překreslit. Voláním této metody se zavolá výše uvedená funkce PB_Paint() a provede se komplexní překreslení grafu.
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 23 Obr. 5.1. Grafické zobrazení průběhu teplot v roce 2003. Hodnocení: 1,5 bodu. Úloha B. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu. Bonusová úloha Upravte úlohu podle zadání A tak, abyste získali zobrazování teplotního grafu pomocí sloupcového (bar) grafu a to včetně výpisu příslušné teplotě k danému sloupci. Teploty větší než 0 C se budou zobrazovat červenými sloupci nad hodnotou 0 C a teploty záporné modrými sloupci pod úrovní 0 C. Příklad grafického výstupu je uveden na obrázku 2. Nejlépe je ve formuláři projektu z úlohy A vytvořit nový druhý objekt typu PictureBox a sloupcové grafy vykreslovat současně s teplotní křivkou. Pro sloupcové grafy využijte metodu FillRectangle(). Pokud chcete i teplotu číselně vypsat nad, případně pod, sloupec, využijte opět metodu DrawString(), přičemž požadovaný řetězec typu String z hodnoty v poli teplot temp[] získáte aplikací konverzní metody ToString: temp[n].tostring(" 0.0") kde n je index vypisovaného prvku pole a jako parametr je uveden řetězec specifikující požadovaný formát (rozlišení čísla) po konverzi (v našem případě výpis na jedno desetinné místo).
24 FEKT VUT v Brně Obr. 5.2. Grafické zobrazení průběhu teplot v roce 2008 pomocí sloupcového grafu. Kontrolní otázky 5.1) Popište, co definují parametry v metodě: Pen^ GridPen = gcnew Pen(Color::Black,1.0f);? 5.2) K čemu slouží metoda Refresh() v objektech typu PictureBox? 5.3) Proč jsou při volání metod při kreslení grafických objektů použity šipky, např. e->graphics->drawrectangle(framepen, 100, 50, 550, 300);? Literatura [5.1] ECKEL, B. Myslíme v jazyku C++. Knihovna programátora. Praha: GRADA, 2000. [5.2] FORD, S. 266 tipů a triků pro Microsoft Visual Studio. Brno: Computer Press, 2009. [5.3] VIRIUS, M. Od C k C++. České Budějovice: KOOP nakladatelství, 2002. 6 Manipulace s maticemi a 2D grafy v Matlabu Cílem cvičení je procvičit si manipulaci s maticemi a vektory v Matlabu a sestavování a 2D grafů. Úloha A. Sestavte matlabovský skript, který vygeneruje tři grafy vycházející ze změřených průměrných teplot za určité období. V předpřipraveném skriptu bpc2e_c06.m je definována matice Temp obsahující průměrné měsíční teplotami od roku 1995 (první řádek, sloupce jsou měsíce) měřené ve 13 h na jisté meteorologické stanici. Dále je ve skriptu definován vektor LTAvT obsahující dvanáct hodnot průměrných měsíčních teplot z dlouhodobého měření. V první fázi upravte teploty v tabulce Temp v osmém řádku od měsíce března do září včetně o deset stupňů Celsia níže. V této době byl měřicí systém porouchaný a měřil teploty o 10 C vyšší. Úpravu proveďte jednoduše pomocí standardní
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 25 dvojtečkové notace. Dalším podúkolem je vygenerování grafu (viz obr. 6.1), kde budou vyneseny všechny závislosti naměřené teploty v průběhu 12 měsíců pro všechny roky měření z tabulky Temp do společného grafu modrou křivkou a současně červeně vyneste křivku průměrné měsíční teploty z uvedeného měření. Nepoužívejte žádné konstrukce cyklů a využijte výhod Matlabu pro manipulaci s maticemi. Modré křivky v grafu lze vygenerovat jen jedním voláním funkce plot(). Pro červenou křivku bude nutné volat funkci plot() podruhé. Aby se červený graf vykreslil do stejného grafu s modrými křivkami a přitom nedošlo k překreslení celého grafu s modrými křivkami, nastavte před vynášením červené křivky atribut grafu hold na hodnotu on. Po vykreslení červené křivky průměrné hodnoty teploty nastavte atribut hold opět na off. Pro výpočet průměrné hodnoty teploty aplikujte funkci sum(). Dále vygenerujte druhý graf, který zobrazí teploty pro všechny měsíce měřeného období za sebou (modře) a rovněž odchylku od dlouhodobého průměru (zelená křivka), viz obr. 6.2. Pro tento úkol použijte funkci reshape() na pole Temp. Obr. 6.1. Grafické zobrazení měsíčních teplot pro všechny roky. Obr. 6.2. Grafické zobrazení měsíčních teplot a odchylek od dlouhodobého průměru za sebou.
26 FEKT VUT v Brně Opět se vyhýbejte aplikaci cyklů, v Matlabu ztrácejí často smysl a zbytečně prodlužují výpočty. U obou grafů specifikujte další parametry jako jsou mřížky, popisy os i název grafu. Pro definici rozsahu vykreslení x-ové osy můžete použít funkci xlim(), popis najdete v nápovědě Matlabu. Standardně si Matlab volí rozsah os sám, někdy to však kazí interpretaci grafických výsledků. Hodnocení: 1,5 bodu. Úloha B. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu. Bonusová úloha Vykreslete bodový graf odchylek od dlouhodobého průměru pro jednotlivé měsíce pro měřené období (modré kroužky) a rovněž průměrnou hodnotu odchylky tečkovanou červenou křivkou se čtvercovými body, viz obr. 6.3. Obr. 6.3. Grafické zobrazení měsíčních odchylek teplot pro všechny roky a průměrná odchylka. Kontrolní otázky 6.1) K čemu slouží příkaz hold u definice grafů? 6.2) Co provede následující konstrukce v Matlabu: A = reshape(b',1,[]);? 6.3) Co provede následující konstrukce v Matlabu: A = B(2:4,5:6);?
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 27 Literatura [6.1] DOŇAR, B., ZAPLATÍLEK, K. MATLAB pro začátečníky. Praha: BEN Technická literatura, 2003. [6.2] KATTAN, P. I. MATLAB for Beginners. A Gentle Approach. Lousiana: Peter I. Kattan, 2009. 7 Model fyzikálního jevu a 3D grafy v Matlabu Cílem cvičení je procvičit si práci s 3D grafy v Matlabu. Úloha A. Sestavte matlabovský skript, který vygeneruje graf závislosti dálky šikmého vrhu jako 3D graf pro parametry počáteční rychlosti v rozsahu od 0 do 100 km/h a pro počáteční úhel šikmého vrhu v rozsahu 0 do 90. Pro dálku šikmého vrhu odvozením podle obrázku 7.1 platí: d v 2 0 sin 2α g = (7.1) kde g je tíhové zrychlení na povrchu Země 9,81 m/s 2. Skript musí generovat tři typy 3D grafů: povrchový (pomocí funkce surf(), viz obr. 7.3), síťový (pomocí funkce mesh(), viz obr. 7.4) a konturový (pomocí funkce contour(), viz obr. 7.5). Obr. 7.1. Šikmý vrh situační nákres. Obecně funkce mesh(), surf() a contour() mají tři parametry, případně čtyři při využití neprimární palety barev. Např. funkce surf(x, Y, Z, C) vykreslí povrchový graf Z = f(x,y), kde X, Y jsou matice vygenerované funkcí meshgrid(), Z je matice výsledné 3D funkce. C definuje barevnou paletu, není-li definováno, použije se aktuální, jak bylo uvedeno výše. Matice X a Y vygenerované funkcí meshgrid() určují síť bodů, které se pak mohou použít jako vstupní hodnoty pro výpočet funkce. Tzn., že tyto matice mají rozměr podle zvolené mřížky a jednotlivé prvky pole obsahují skutečné vstupní hodnoty pro danou proměnnou. Matice X má pak pro daný sloupec všechny hodnoty stejné odpovídající hodnotě x v kartézské souřadné soustavě. Obdobně matice Y má pro daný řádek všechny
28 FEKT VUT v Brně hodnoty stejné odpovídající hodnotě y v kartézské souřadné soustavě. Níže je uveden příklad, který vygeneruje povrchový graf funkce z = x 2 +y 2 včetně aplikace funkce meshgrid(), titulku grafu a popisu os a to se standardní barevnou paletou, výsledek je na obrázku 7.2: [x,y] = meshgrid(-5:0.1:5); Z = x.^2+y.^2; figure() surf(x,y,z) title('z=x^2+y^2') xlabel('x') ylabel('y') zlabel('z') z=x 2 +y 2 50 40 30 z 20 10 0 5 5 0 0 y -5-5 x Obr. 7.2. Povrchový graf pro skript z příkladu. d = f(v0, alfa) d [m] 80 70 60 50 40 30 20 10 0 70 60 50 40 30 20 80 60 100 10 80 40 60 20 40 20 0 0 alfa [ ] 0 v0 [km/h] Obr. 7.3. Povrchový graf pro dálku šikmého vrhu.
60 Programování a počítače 2. Počítačová cvicení pro obor B-EST. 29 d = f(v0, alfa) 80 60 d [m] 40 20 0 80 60 40 40 60 80 100 20 20 alfa [ ] 0 0 v0 [km/h] Obr. 7.4. Síťový graf pro dálku šikmého vrhu. Velikost ok mřížky je třeba volit rozumně tak, aby graf nebyl příliš diskretizovaný a současně aby nebyla síť tak hustá, že se slijí hrany (sítě) černé barvy v jeden černý obraz. Barevnou intenzitní paletu ve formě sloupku pro osu z lze aktivovat příkazem colorbar. 90 d = f(v0, alfa) 80 70 10 20 10 40 30 60 30 alfa [ ] 50 40 10 20 50 70 40 30 30 60 20 10 0 0 10 20 30 40 50 60 70 80 90 100 v0 [km/h] 10 20 10 20 30 40 50 Obr. 7.5. Konturový graf pro dálku šikmého vrhu. Hodnocení: 1,5 bodu. Úloha B. Zadáno cvičícím na začátku cvičení. Hodnocení: 1,5 bodu.
30 FEKT VUT v Brně Bonusová úloha Vygenerujte smíšený graf závislosti dálky šikmého vrhu podle zadání A obsahující síťový graf spolu s konturovým pomocí funkce meshc(). Výsledek je zřejmý z obrázku 7.6. Upravte rovněž barevnou paletu podle obrázku. Barvy jsou vzestupně plná černá, plná modrá, plná zelená a plná červená. Nezapomeňte i na popisy os a titulek grafu. d = f(v0, alfa) 80 70 70 60 d [m] 60 50 40 30 20 10 50 40 30 20 Kontrolní otázky 0 0 10 20 80 40 60 60 40 80 20 0 100 0 alfa [ ] v0 [km/h] Obr. 7.6. Kombinovaný graf síťový a konturový. 7.1) Jakou barvu bude mít graf, pokud bude ve skriptu pro generování u grafu definována barevná paleta následovně: colormap([0 1 0])? 7.2) Jak lze nastavit osu z u grafu tak, aby rozsah osy z odpovídal přesně od minma do maxima funkční hodnoty Z (Z je matice výsledné dvourozměrné funkce)? 7.3) Co zajistí u 2D grafu příkaz hidden off? Literatura [7.1] DOŇAR, B., ZAPLATÍLEK, K. MATLAB pro začátečníky. Praha: BEN Technická literatura, 2003. [7.2] KATTAN, P. I. MATLAB for Beginners. A Gentle Approach. Lousiana: Peter I. Kattan, 2009. 8 Parametrické 3D grafy v Matlabu Cílem cvičení je procvičit si práci se soubory a parametrickými 3D grafy v Matlabu. Úloha A. Protože budete řešit transformaci z kartézských do sférických souřadnic, ve kterých budete vykreslovat grafy, resp. objekty, jako parametrické 3D funkce s definicí barvy povrchu a protože to představuje jistou představivost ve 3D prostoru a tedy pro matlabovské začátečníky jistý obtížný kousek, je součástí zadání i názorný příklad, viz bpc2e_c08ex.m.
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 31 V tomto příkladu je nejprve pomocí meshgridu vytvořena sít v kartézské soustavě, kde lon (zeměpisku délka) koresponduje s osou x a lat (zeměpisná šířka) s osou y: [lon,lat] = meshgrid(-180:0.5:180, -90:0.5:90); Máme tedy síť, která má definovaná oka s délkou 0,5 a je položená na rovné zemi (v soustavě x, y). Následně je vygenerována matice c jako funkce lon+lat, to znamená, že její hodnota bude stoupat s rostoucí hodnotou lon i lat: c = lon+lat; Pro nízké indexy řádků i sloupců se budou její hodnoty pohybovat okolo -270, tedy -180 + (- 90), naopak pro nejvyšší indexy se budou hodnoty v matici C blížit +270. Pokud použijeme následující skript pro vykreslení síťového grafu pomocí funkce mesh(): figure(1) mesh(lon,lat,0*lon,c) xlabel('lon') ylabel('lat') axis equal colormap('lines') view(0,90) získáme graf 8.1. Souřadnice x a y tedy odpovídají lon i lat. Třetí matice odpovídající ose z je nulová (násobení je zde kvůli zajištění rozměru nulové matice). Ve funkci mash() matice c figuruje jako čtvrtý parametr, což znamená, že hodnotou c pro dané lon a lat je nějaká barva z vybrané palety. Protože je z vždy nulové graf leží v rovině xy a jenom se mění jeho barva podle matice c a vybrané palety colormap(). Pro ilustraci je vybrána paleta Lines, proto tolik proužků, ale zkuste si jinou paletu, např. Winter, která lépe ukáže, jak roste c pro dané místo (souřadnice) sítě umístěné v rovině xy. Máme tedy vytvořen určitý vzor, který je rozložený na podlaze (z = 0) jako koberec. A nyní přichází kýžená transformace, kdy pro parametry lon a lat definujeme transformované proměnné x, y a z ve sférických souřadnicích. Z hlediska představivosti jde jen o to, že vezmeme původní síť (koberec položený na zemi) a stočíme jej do koule, přičemž musíme někde oka (koberec) trochu roztáhnout, jinde přitlačit k sobě. Výsledkem je kouli v 3D soustavě a příslušné barevné proužky teď figurují na povrchu koule. V našem případě máme poloměr koule r = 1, obecně však pro uvedenou transformaci platí: x = r cos( α) cos( β ) y = r sin( α) cos( β ) z = r sin( β ) (8.1) kde α odpovídá námi definované proměnné lon a β proměnné lat. Hodnota r určuje poloměr výsledné koule. V testovacím příkladě je síť tak hustá, že se nám jednotlivé hrany slévají dohromady. Zkuste navrhnout síť řidší a uvidíte, že transformace do koule v některých místech oka roztáhne a jinde smrskne (to je kvůli sinům a kosinům v transformačním vztahu).
32 FEKT VUT v Brně Obr. 8.1. Barevný plot na zemi. Sestavíme-li skript pro transformaci kulových souřadnic: x = cos(lon*pi/180).*cos(lat*pi/180); y = sin(lon*pi/180).*cos(lat*pi/180); z = sin(lat*pi/180); a aplikujeme již skutečné 3D zobrazení (z již není nulové, viz třetí vztah v transformaci) pomocí funkce mesh(): figure(2) mesh(x,y,z,c) title('striped ball') xlabel('x') ylabel('y') zlabel('z') axis equal colormap('lines') view(45,20) Získáme krásnou barevnou mičudu, na které je vidět zhuštění původního 2D zobrazení v oblasti pólů a zředění okolo rovníku. Vašim úkolem je na základě uvedeného příkladu sestavit matlabovský skript, který vygeneruje grafy rozložení teploty v severním Atlantiku. Abyste mohli oddělit oceány od souše, je k dispozici binární soubor earth.dat obsahující jednobajtové hodnoty, kdy 1 odpovídá moři a 0 souši na zemském povrchu. Rozdělení je po 0,5 jak v zeměpisné délce <- 180, +180 >, tak i v zeměpisné šířce <-90, +90 >. Řádky odpovídají zeměpisné šířce, sloupce zeměpisné délce. Sestavte skript, který zobrazí masku souší a moří Země, viz obr. 8.3. Jde v podstatě o řešení z ukázkového příkladu s vlastní paletou o dvou barvách. Data ze souboru masky souší a moří načtete následujícím skriptem: fid1 = fopen('earth.dat', 'r') e_mask = fread(fid1, [361 721]); fclose(fid1)
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 33 Obr. 8.2. Barevný plot srolovaný do mičudy pomocí transformace. Proveďte jak zobrazení rozložené do roviny xy (obr. 8.3a) tak i zobrazení po transformaci, čímž získáte vykreslený globus souší a moří. Pro další úkol je v souborech NATemp1985.dat a NATemp2010.dat uložena mapa teploty severního Atlantiku měřená v roce 1985 a 2010 v zeměpisné délce <-135, +35 > a v zeměpisné šířce <+15, +90 >. Rozdělení je opět po 0,5 a stejné uspořádání dat jako pro masku souší a moří avšak jako hodnoty ve formátu float odpovídající průměrné teplotě v daném roce. Souše mají přidělenu fixní hodnotu -10. Sestavte výřezy grafů teploty vod v severním Atlantiku pro oba roky i rozdíl (pro zjištění globálního oteplování), viz obr. 8.4. V závěrečné části proveďte transformace do sférických souřadnic a zobrazte teploty moří na povrchové mapě Země, u rozdílové hodnoty zobrazte jen měřenou oblast (máte jen kousek plotu na kousek koule), viz obr. 8.5. Pro zobrazení stačí používat funkci mesh(), protože síť je dostatečně hustá. Jak je patrné z výsledků i severní Atlantik se nám pomalu ohřívá. Kdo za to může? Obr. 8.3. Maska souší a moří Země.
34 FEKT VUT v Brně Obr. 8.4. Teplota moře ve 2D. Bonusová úloha V rámci bonusové úlohy upravte kód příkladu A tak, že transformaci budete definovat jako vlastní funkci. Řešení ověřte na stejných datech, musíte získat shodné výsledky. Kontrolní otázky 8.1) Kam se bude vykreslovat graf při definici: subplot(1,2,2)? 8.2) K čemu slouží příkaz: axis equal? 8.3) K čemu slouží funkce view()u zobrazování grafů jaké má parametry?
Programování a počítače 2. Počítačová cvicení pro obor B-EST. 35 Obr. 8.5. Teplota moře ve 3D. Hodnocení: 3 body. Literatura [8.1] DOŇAR, B., ZAPLATÍLEK, K. MATLAB pro začátečníky. Praha: BEN Technická literatura, 2003. [8.2] DOŇAR, B., ZAPLATÍLEK, K. MATLAB začínáme se signály. Praha: BEN Technická literatura, 2006. [8.3] KATTAN, P. I. MATLAB for Beginners. A Gentle Approach. Lousiana: Peter I. Kattan, 2009.
36 FEKT VUT v Brně 9 Model komunikačního systému v Matlabu Cílem cvičení je vyzkoušet si sestavit skripty v Matlabu pro model jednoduchého komunikačního systému pro přenos obrázků. Úloha A. Sestavte model komunikačního systému, který pracuje s modulací 8PSK a který přenáší komprimované BMP obrázky. Pro kompresi (zdrojové kódování) a zpětnou dekompresi (zdrojové dekódování) BMP obrázků použijte následující algoritmus: Nejprve z jednotlivých barevných složek vytvořte matici C, která je součtem všech tří RGB složek pro jednotlivé pixely (elementy matice). Následně vytvořte matici pro červenou a zelenou složku jako poměr úrovně červené ku součtu všech složek (R/C) a podobně pro zelenou matici úrovní (G/C). Pokud byste to samé provedli i pro modrou matici úrovní, součet všech třech poměrových matic by byl 1 (tedy 100%). Tzn., že poměrovou matici modré lze dopočítat z poměrových matic červené a zelené. Celá komprese spočívá v tom, že C matice nese celkovou úroveň jasu a poměrové matice procentuální zastoupení barev. Jasovou matici C podělte 3, čímž získáte rozsah pro jas 0 až 255 (osmibitovou hodnotu) pro každý pixel. Z této matice vytvořte finální matici jasu pro přenos rádiovým kanálem tak, že použijete jen šest nejvyšších bitů (jas bude komprimován na 64 úrovní). Pro poměrovou složku červené a zelené vytvořte zředěné matice tak, že obrázek rozdělíte na bloky 4x4 pixelů a budete uvažovat průměrnou poměrnou úroveň červené, resp. zelené pro tento blok pixelů. Výsledné poměrové matice budou mít 4x méně řádků i 4x méně sloupců (celková redukce je 16x). Následně tyto matice ořežte na 6 nejvyšších bitů (např. tak, že poměr 0 až 100% vynásobíte 2 6 a zanedbáte desetinnou část). Tím je hotová celá komprese. Inverzním způsobem a dopočítáním složky modré získáte zpět komprimovaný BMP obrázek, tento proces bude třeba provést po demodulaci signálu. Nejlépe bude řešit zdrojové kódování a dekódování jako vlastní matlabovské funkce, které se budou volat ve skriptu modelu. Důvod je i ten, že můžete provést test správné činnosti kompresoru a rekonstrukce komprimovaného BMP obrázku bez modulace a demodulace. Pro testování máte připravený obrázek mkid.bmp o velikosti 200 x 200 pixelů s roztomilou dětskou tvářičkou. Na obrázku 9.1a je původní BMP fotka a na obrázku 9.1b komprimovaná (rekonstruovaná). Rozdíl je takřka neznatelný, přičemž kompresní poměr je docela velký. Původní obrázek potřebuje 200x200x3x8 bitů (tři barevné složky s rozlišením 8 bitů) = 960000 bitů, komprimovaný 200x200x1x6bitů pro jas plus 50x50x2x6 bitů pro dvě poměrové barevné složky bloků 4x4 pixely, tedy celkem 270000 bitů. Kompresní poměr je 960000/270000 = 3,56. Obr. 9.1a. Originální fotka. Obr. 9.1b. Rekonstruovaná komprimovaná fotka.