Ćwiczenie P-2 Processing definiowanie i rysowanie obiektów 2D i 3D Instrukcja laboratoryjna opracował: mgr inż. Jakub Możaryn Człowiek - najlepsza inwestycja Projekt współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego Warszawa 2009
2 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D SPIS TREŚCI 1. Rysowanie krzywych... 3 1.1. RYSOWANIE KRZYWYCH OPISANYCH WIELOMIANAMI... 3 1.2. FUNKCJE DO GENEROWANIA KRZYWYCH... 7 1.2.1. Krzywe Beziera... 7 1.2.2. Krzywe Catmull-Roma... 8 1.2.3. Rysowanie i kontrola kształtu krzywej... 8 2. Grafika 3D... 12 2.1. SILNIKI 3D... 13 2.2. OKREŚLENIE TRYBU WYŚWIETLANIA GRAFIKI... 13 3. Bryły 3D... 14 3.1. SFERA I PROSTOPADŁOŚCIAN... 14 3.3. DEFINIOWANIE ZŁOŻONYCH KSZTAŁTÓW... 14 4. Transformacje... 17 4.1. PRZESUNIĘCIA... 17 4.2. OBROTY... 17 4.3. SKALOWANIE... 18 4.4. DEFINIOWANIE WIELU UKŁADÓW WSPÓŁRZĘNYCH STOS MACIERZY 18 4.5. RUCH OBIEKTÓW W PRZESTRZENI... 18 5. Zadania do samodzielnego wykonania... 21 5.1. ZADANIE RYSOWANIE ZŁOŻONYCH KRZYWYCH... 21 6. Literatura... 22
Ćwiczenie P-2 3 Definiowanie i rysowanie obiektów 2Di 3D 1. Rysowanie krzywych Jednym z istotnych zagadnień grafiki komputerowej jest generowanie i rysowanie złożonych krzywych w dwóch i trzech wymiarach. W przypadku prostych kształtów do ich narysowania wystarczy podstawowa wiedza matematyczna. Natomiast w przypadku bardziej złożonych kształtów niezbędna staje się znajomość rachunku różniczkowego. W rozdziale omówione zostaną elementarne krzywe i opisujące je wielomiany stosowane w programach graficznych oraz przedstawione bardziej złożone sposoby generowania krzywych na przykładzie krzywych Beziera i Catmull-Roma. Omawiane tematy zostaną zobrazowane prostymi aplikacjami w języku Processing. Dodatkowo zostanie przedstawiony sposób wyświetlania tekstu stosowany w języku Processing. 1.1. RYSOWANIE KRZYWYCH OPISANYCH WIELOMIANAMI W programach graficznych krzywe można w prosty sposób generować korzystając z opisu funkcją wielomianową stopnia n zapisaną w postaci następującego równania algebraicznego: y=a n x n +a n-1 x n-1 + +a 1 x+a 0 (1) gdzie a n, a n-1,, a 1, a 0 są współczynnikami wielomianu. W praktyce w programach graficznych nie stosuje się wielomianów o stopniu większym niż trzy. Wynika to głównie z rosnącego nakładu obliczeniowego oraz trudniejszej kontroli kształtu opisanych przez nie krzywych. Zadaniem prostego programu, przedstawionego w tym rozdziale, jest wygenerowanie trzech krzywych opisanych wielomianami pierwszego (prosta), drugiego (parabola) i trzeciego stopnia (parabola sześcienna). Ponieważ w aplikacji będzie wypisywany tekst, należy do projektu dodać specjalny plik o rozszerzeniu '.vlw', interpretowany przez Processing jako zbiór czcionek. Tworzy się go wybierając z głównego menu opcję Tools->CreateFont... (Rys. 1.1). Otworzy się wtedy okno służące do konwersji dostępnych w systemie czcionek na format interpretowany przez Processing. Podczas tworzenia i dodawania czcionek do projektu należy wybrać odpowiedni rodzaj czcionki, jej rozmiar, sposób wyświetlania (wygładzanie opcja Smooth ), oraz czy ma być dokonana konwersja wszystkich znaków (opcja All Characters ). Po zatwierdzeniu przyciskiem OK. zostanie utworzony plik o podanej nazwie w formacie '.vlw' i zostanie on automatycznie umieszczony w katalogu '/data' aktualnego projektu. Na początku należy zadeklarować wykorzystywane zmienne oraz napisać funkcję inicjującą. Jako zmienną globalną należy zadeklarować obiekt klasy PFont przechowujący informacje o czcionce, którą będzie wypisywany tekst na ekranie.
4 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D Rysunek 1.1. Okno do konwersji i dodawania czcionek do projektu Kolejny krok to napisanie funkcji inicjującej. Oprócz standardowego określenia rozmiaru okna aplikacji, oraz tła, zostanie w niej określony font służący do wypisywania tekstu. W tym celu należy skojarzyć obiekt klasy PFont z odpowiednim plikiem '.vlw', korzystając z loadfont(). font = loadfont("nazwa_pliku.vlw"); gdzie parametrem funkcji loadfont() jest nazwa pliku z czcionkami, który znajduje się w katalogu '/data'. Ponieważ w rozbudowanych aplikacjach może być kilka obiektów klasy PFont należy określić, z którego obiektu będą wykorzystywane czcionki podczas pisania tekstu. W tym celu należy skorzystać z funkcji textfont(pfont font) gdzie parametrem jest wykorzystywany obiekt klasy PFont. Aplikacja 1.1 deklaracja zmiennych i funkcja inicjująca PFont font; void setup(){ size(400, 400); //rozmiar okna background(255); //kolor tła font = loadfont("verdana-10.vlw"); //określenie czcionki textfont(font); //funkcja draw() wykonywana jest tylko raz noloop();
Ćwiczenie P-2 5 Definiowanie i rysowanie obiektów 2Di 3D Następnie należy napisać funkcję draw(), która rysuje wykresy trzech różnych wielomianów na ekranie wraz z osiami i opisem tekstowym. Aplikacja 1.1 funkcja rysująca void draw(){ rysujosie(); strokeweight(2); stroke(255,0,0); float a0=-10, a1=2, a2=0, a3=0; rysujwielomian(a0,a1,a2,a3); stroke(0,255,0); a0=0.1; a1=0.01; a2=0.004; a3=0; rysujwielomian(a0,a1,a2,a3); stroke(0,0,255); a0=0.01; a1=0.01; a2=0.001; a3=0.0001; rysujwielomian(a0,a1,a2,a3); wprowadztekst(); //grubość linii //kolor punktów - czerwony //kolor punktów zielony //kolor punktów niebieski Pierwsza funkcja rysujosie() odpowiada za narysowanie osi rzędnych i osi odciętych na ekranie z wykorzystaniem funkcji line(). Aplikacja 1.1 funkcja rysująca osie void rysujosie(){ stroke(0,0,0); strokeweight(1); line(0,height/2,width,height/2); line(width/2,0,width/2,height); //kolor osi - czarny //grubość osi //oś OX //oś OY Funkcja rysujwielomian() służy do narysowania funkcji opisanej wielomianem. Wywoływana jest ona z parametrami będącymi współczynnikami wielomianu trzeciego stopnia. W zależności od tego, które z nich mają wartość 0 uzyskuje się wielomiany stopnia mniejszego niż trzy. W pętli wyświetlane są kolejne punkty wielomianu, korzystając z funkcji point(x,y). Kolor piksela określa funkcja stroke(). Punkty krzywej zostały tak przesunięte, aby środek układu współrzędnych znajdował się w środku okna aplikacji. Aplikacja 1.1 funkcja rysująca wielomian void rysujwielomian(int a0,a1,a2,a3){ float y; for (int x=round(-width/2); x<round(width/2); x++){ y = a3*pow(x,3)+a2*pow(x,2)+a1*x+a0; point(x+width/2, height-(y+height/2)); //rysowanie punktu
6 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D Ostatnia funkcja wprowadztekst()służy do wyświetlenia tekstu na ekranie. Wykorzystano w niej funkcję text() text(string tekst, int/float x, int/float y, int/float z) Parametrami są: tekst wypisywany ciąg znaków, x, y, z współrzędne położenia początku tekstu na ekranie. Kolor czcionki zmienia się korzystając z instrukcji fill() i odpowiada on kolorowi krzywej, której dotyczy opis. Aplikacja 1.1 funkcja wypisująca tekst na ekranie void wprowadztekst(){ fill(255,0,0); text("prosta: y=a1*x+a0", 210, 230); fill(0,255,0); //kolor tekstu - czerwony //kolor tekstu - zielony text("parabola: y=a2*x^2+a1*x+a0", 210, 250); fill(0,0,255); //kolor tekstu - niebieski text("parabola \nsześcienna: y=a3*x^3+a2*x^2+a1*x+a0", 210, 270); Rysunek 1.2. Okno aplikacji 1.1, rysowanie krzywych
Ćwiczenie P-2 7 Definiowanie i rysowanie obiektów 2Di 3D 1.2. FUNKCJE DO GENEROWANIA KRZYWYCH 1.2.1. Krzywe Beziera Krzywe Beziera są parametrycznymi krzywymi trzeciego stopnia i stanowią podstawowy element programów do tworzenia i edycji rysunków wektorowych. Język Processing jest wyposażony w funkcję służącą do rysowania krzywej Beziera: bezier (x0, y0, x1, y1, x2, y2, x3, y3) bezier (x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3) gdzie wszystkie parametry są typu int/float. Postać krzywej Beziera zależy od czterech punktów kontrolnych, z których tylko dwa krańcowe - pierwszy i ostatni - leżą na krzywej. Rysunek 1.3. Krzywa Beziera. Na rysunku zaznaczono punkty kontrolne krzywej, parametry funkcji bezier(). Do wywołania funkcji bezier() wymagane jest 8 lub 12 parametrów, w zależności od liczby wymiarów, w których chcemy takie krzywe rysować. W przypadku rysowania krzywej w dwóch wymiarach nie podaje się współrzędnej z.
8 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D 1.2.2. Krzywe Catmull-Roma Druga funkcja służąca do rysowania krzywych trzeciego stopnia to curve(). Algorytm wyznaczania krzywej opiera sie na metodzie Catmull-Roma. Analogicznie jak funkcja bezier() wymaga określenia współrzędnych czterech punktów: curve (x0, y0, x1, y1, x2, y2, x3, y3) curve (x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3) Przez te cztery punkty prowadzona jest gładka krzywa interpolująca, ale rysowana jest ona tylko pomiędzy punktem drugim i trzecim; punkt pierwszy i ostatni służą do modelowania kształtu krzywej: Rysunek 1.4. Krzywa Catmull-Roma. Na rysunku zaznaczono punkty kontrolne krzywej, parametry funkcji curve(). 1.2.3. Rysowanie i kontrola kształtu krzywej W kolejnym przykładzie zostanie opisana aplikacja, w której określone będą punkty krańcowe i wewnętrzne punkty kontrolne oraz rysowana będzie krzywa Beziera. Dodatkowo użytkownik będzie mógł kontrolować kształt i rozmiar krzywej poprzez zmianę położenia punktów. Na początku należy zadeklarować liczbę rysowanych punktów, ich rozmiar, dwie tablice opisujące położenie 4 punktów oraz jedną tablicę przechowującą informację, który punkt jest przesuwany.
Ćwiczenie P-2 9 Definiowanie i rysowanie obiektów 2Di 3D Aplikacja 1.2 deklaracja zmiennych int pts=4; //liczba punktów int ptrozmiar=6; //rozmiar punktów //połoŝenie punktów (tablice) float []x=new float[pts]; float []y=new float[pts]; boolean []Przesuniecie = new boolean[pts]; //przesuwanie punktów W funkcji inicjującej zostaną wygenerowane w sposób losowy współrzędne każdego z punktów. Flaga Przesunięcie zostanie ustawiona na false, co oznacza ze wszystkie punkty są nieruchome i nie jest możliwe ich przesuwanie. Aplikacja 1.2 funkcja inicjująca void setup(){ size(300,300); smooth(); for (int i=0;i<pts;i++){ //losowanie współrzędnych punktów x[i] = round(random(width-1)); y[i] = round(random(height-1)); Przesuniecie [i]=false; //blokowanie punktów W funkcji draw() wywołana zostanie funkcja użytkownika bezierrysuj(), która pozwala na narysowanie krzywej Beziera pomiędzy określonymi punktami. Punkty krańcowe połączone są z wewnętrznymi punktami kontrolnymi linią prostą. Aplikacja 1.2 funkcja rysująca void draw(){ background(255); //czyszczenie okna nofill(); strokeweight(2); bezierrysuj(); //rysowanie krzywej //zmiana współrzędnych przesuwanego punktu for(int i=0;i<pts;i++){ if (Przesuniecie[i]){ x[i] = mousex; y[i] = mousey;
10 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D Aplikacja 1.2 funkcja bezierrysuj() void bezierrysuj(){ ` stroke(0, 0, 255); //rysowanie krzywej Beziera bezier (x[0], y[0],x[1], y[1], x[2], y[2], x[3], y[3]); //rysowanie linii pomiędzy wewnętrznymi punktami kontrolnymi //a punktami krańcowymi strokeweight(1); stroke(255,0,0); //kolor linii - czerwony line(x[0],y[0],x[1],y[1]); line(x[2],y[2],x[3],y[3]); stroke(0); //kontur punktów kontrolnych for(int i=0;i<pts;i++){ if (i==1 i==2){ //oznaczenie wewnętrznych punktów kontrolnych fill(255, 0, 0); rectmode(center); rect(x[i],y[i],ptrozmiar, ptrozmiar); //kwadrat }else{ //oznaczenie punktów krańcowych fill(0); ellipse(x[i], y[i], ptrozmiar, ptrozmiar); //okrąg W kolejnym kroku zostaną obsłużone zdarzenia związane z przyciskiem myszy. W ciągu instrukcji funkcji mousepressed(), związanej z naciśnięciem przycisku myszy, sprawdzane jest położenie kursora, oraz elementy tablicy zawierającej informacje czy dane punkty można przesuwać. W przypadku gdy kursor znajdzie się nad danym punktem i naciśnięty zostanie lewy przycisk myszy, punkt zostaje odblokowany i może być przesuwany. Druga funkcja, mousereleased(), jest związana ze zwolnieniem przycisku myszy. W tym przypadku wszystkie punkty są blokowane.
Ćwiczenie P-2 11 Definiowanie i rysowanie obiektów 2Di 3D Aplikacja 1.2 obsługa zdarzeń związanych z przyciskiem myszy //naciśnięcie przycisku myszy void mousepressed(){ for(int i=0;i<pts;i++){ //sprawdzenie warunków, który punkt ma być odblokowany if(mousex>=x[i]-5&&mousex<=x[i]+ ptrozmiar +5 && mousey>=y[i]-5&&mousey<=y[i]+ ptrozmiar +5){ //odblokowanie wybranego punktu Przesuniecie[i]=true; //zwolnienie przycisku myszy void mousereleased(){ //zablokowanie punktu for(int i=0;i<pts;i++){ Przesuniecie[i]=false; Ostatecznie należy obsłużyć ruch myszy funkcja mousemoved(). W aplikacji funkcja ta służy tylko sprawdzeniu, gdzie znajduje się kursor. W przypadku gdy znajdzie się nad jednym z punktów definiujących krzywą, zmieniana jest ikona kursora (ikona z ręką). W pozostałych przypadkach ikoną kursora jest strzałka. Aplikacja 1.2 obsługa zdarzenia związanego z ruchem myszy //zmiana wyświetlanej ikony kursora, jeśli znajduje się nad punktem void mousemoved (){ //zmiana ikony kursora na ikonę strzałki cursor(arrow); for(int i=0;i<pts;i++){ if(mousex>=x[i]-5&&mousex<=x[i]+ptrozmiar+5 && mousey>=y[i]-5&&mousey<=y[i]+ ptrozmiar +5){ //zmiana ikony kursora na ikonę ręki cursor(hand);
12 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D Rysunek 1.5. Okno aplikacji 1.2, kontrola rozmiaru i kształtu krzywej Beziera 2. Grafika 3D w trzech wymiarach stanowi istotny element produkowanych obecnie programów stosowanych m.in. w przemyśle, rozrywce, medycynie, farmacji lub badaniach naukowych. Większość dostępnych programów 3D (AutoCad, LightWave, Maya, 3ds Max, Blender) posiada dość złożony interfejs co powoduje, że osiągnięcie zamierzonego efektu wymaga specjalistycznej wiedzy. Niskopoziomowe programowanie grafiki 3D także należy do skomplikowanych zagadnień. Możliwość wyświetlania trzech wymiarów na dwuwymiarowej płaszczyźnie jest stosunkowo złożone, wymaga dobrych umiejętności projektowania algorytmów i programowania. Ponadto, żeby wyświetlanie trójwymiarowych scen było płynnie i szybkie potrzebna jest znajomość technologii przyspieszania wyświetlania grafiki: optymalizacji kodu, stosowania bibliotek systemowych np. DirectX, oraz możliwości sprzętu (procesora, kart grafiki, szybkości podzespołów, etc.). W Processingu umożliwiono proste budowanie scen złożonych z trójwymiarowych obiektów i operacji na tych obiektach (obroty, przesunięcia, etc.), co znacznie przyspiesza proces projektowania aplikacji i kodowania. Sprzyja to także wyjaśnieniu podstawowych koncepcji grafiki trójwymiarowej. Możliwość eksportu programów do postaci apletów zagnieżdżonych na stronach WWW lub do plików PDF powoduje, że Processing jest bardzo atrakcyjną technologią także do wizualizacji 3D. Processing posiada kilka trybów wyświetlania grafiki. Okna generowane w Processingu mogą wykorzystywać do wyświetlania obrazów dwuwymiarowych wbudowane oprogramowanie tzw. silniki: P2D lub bibliotekę Java 2D, natomiast do wyświetlania grafiki trójwymiarowej dedykowany silnik P3D lub bibliotekę OpenGL. Zasady ich stosowania podano w podrozdziale 2.2.
Ćwiczenie P-2 13 Definiowanie i rysowanie obiektów 2Di 3D 2.1. SILNIKI 3D Warto wspomnieć czym są silniki 3D. Zwykle jest to zbiór bibliotek i oprogramowania, które pozwala na analizowanie scen trójwymiarowych, obliczanie współrzędnych i przemieszczeń, a następnie przeliczanie ich na współrzędne w dwóch wymiarach, tak żeby wyświetlić scenę trójwymiarową na ekranie monitora proces ten nazywany jest renderingiem. Ponadto silniki 3D wspomagają pracę z obiektami geometrycznymi w trzech wymiarach, ułatwiają określanie źródeł światła, nakładanie tekstur oraz ustawianie widoku scen trójwymiarowych z wykorzystaniem tzw. wirtualnych kamer. 2.2. OKREŚLENIE TRYBU WYŚWIETLANIA GRAFIKI Pisanie aplikacji trójwymiarowych w Processingu wymaga określenia, który silnik 3D będzie stosowany do wyświetlania scen. W tym celu wykorzystywana jest funkcja size(), wywoływana na początku programu. size(int/float szerokość, int/float wysokość, TRYB) Funkcja size() definiuje nam rozmiar okna w piselach (szerokość wysokość). Ponadto opcjonalnie można podać tryb wyświetlania grafiki. W przypadku gdy funkcja ta nie występuje, standardowo tworzone jest okno 100 100 pikseli. Funkcja size() powinna być wywołana na początku funkcji setup(). Processing umożliwia korzystanie z kilku różnych trybów wyświetlania grafiki podawanych jako parametr TRYB. Zostały one zebrane w tabeli 2.1 Jeśli nie jest określony tryb wyświetlania to domyślnie przyjmowany jest silnik Java 2D. Tabela 2.1. Tryby wyświetlania grafiki TRYB JAVA2D P2D P3D OPENGL PDF Opis Domyślny tryb wyświetlania grafiki, wspierający wyświetlanie grafiki dwuwymiarowej. Daje najlepszy efekt wizualny, jest jednak najwolniejszym z dostępnych trybów. (Processing 2D) - szybki tryb wyświetlania grafiki dwuwymiarowej, mniej dokładny od trybu Java 2D. (Processing 3D) - szybki tryb wyświetlania grafiki trójwymiarowej. Przeznaczony szczególnie dla aplikacji internetowych. Mniej dokładny od trybu OpenGL. Szybki tryb wyświetlania grafiki trójwymiarowej, wykorzystujący możliwości sprzętu kompatybilnego z biblioteką Open GL. Aby z niego skorzystać należy najpierw zaimportować bibliotekę Open GL (w menu głównym wybrać Sketch > Import Library > opengl). Tryb wyświetlania grafiki, który pozwala na zapisanie grafiki 2D do piku Acrobat PDF. Daje najlepsze efekty, jeśli wynikiem pracy mają być statyczne obiekty wektorowe przeznaczone do wydruku. Aby z niego skorzystać należy najpierw zaimportować bibliotekę PDF ( w menu głównym wybrać Sketch > Import Library > pdf).
14 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D W przypadku operacji na pikselach, (funkcje get(), blend(), działania na wektorze pixels[] ) najszybciej będą działały tryby P2D i P3D, w porównaniu z Java 2D lub Open GL. Natomiast, jeśli odtwarzane są pliki z filmami lub wykonywana jest obróbka wielu obrazów, tryby P2D i P3D są szybsze. Ich ograniczeniem jest niższa dokładność niż w przypadku trybów Java 2D lub Open GL, dlatego też nadają się głównie dla aplikacji internetowych. Projektując aplikacje multimedialne (np. prezentacje, wizualizacje), odtwarzane na jednym komputerze, lepiej jest korzystać z dokładniejszych trybów (Java 2D i Open GL). 3. Bryły 3D W Processingu udostępniono dwie standardowe bryły trójwymiarowe: prostopadłościan i sferę. Pozostałe bryły wymagają osobnego zdefiniowania jako połączonych ze sobą wierzchołków w przestrzeni lub w postaci chmury punktów. 3.1. SFERA I PROSTOPADŁOŚCIAN Sferę (kulę pustą w środku, której powierzchnia przybliżona jest zbiorem połączonych trójkątów) tworzy się przy pomocy funkcji sphere(). sphere(int/float promien) Funkcja sphere() posiada tylko jeden parametr - promień. Prostopadłościan tworzy się przy pomocy funkcji box(). box(int/float szerokość, int/float wysokość, int/float głębokość); Funkcja ta wywoływana jest z trzema parametrami: szerokość (wzdłuż osi OX), wysokość (wzdłuż osi OY) i głębokość (wzdłuż osi OZ). Domyślnie środek rysowanej figury znajduje się w punkcie (0,0,0) lewy, górny róg ekranu. Aplikacja 3.1. funkcja inicjująca i rysująca. void setup(){ size(400,400,p3d); noloop(); void draw(){ box(200,200,200); //rozmiar okna i tryb wyświetlania //rysowanie prostopadłościanu 3.3. DEFINIOWANIE ZŁOŻONYCH KSZTAŁTÓW Złożone kształty w Processingu tworzy się, definiując położenie poszczególnych wierzchołków, które są później ze sobą połączone. Wierzchołek określa się przy pomocy funkcji vertex(). vertex(int/float x, int/float y, int/float z) gdzie parametrami są współrzędne wierzchołka.
Ćwiczenie P-2 15 Definiowanie i rysowanie obiektów 2Di 3D Blok definicji nowego kształtu lub bryły to ciąg funkcji definiujących wierzchołki ograniczony przez instrukcje beginshape() i endshape(). beginshape(); //definicje wierzchołków endshape(); Tryby wyświetlania grafiki P2D, P3D oraz OPENGL pozwalają na określenie koloru i wypełnienia dla każdego wierzchołka. Instrukcja beginshape() ma następującą składnię: beginshape(rodzaj) Parametr RODZAJ mówi, jaki rodzaj figury ma być utworzony z podanych wierzchołków. W przypadku jego braku pomiędzy kolejnymi wierzchołkami tworzona jest nieregularna, łamana linia. Dostępne wartości parametru RODZAJ i opis generowanych kształtów zebrano w Tab. 3.1. Tabela 3.1. Rodzaje kształtów generowanych w zależności od wartości parametru RODZAJ RODZAJ POINTS LINES (brak) TRIANGLES TRIANGLE_FAN TRIANGLE_STRIP QUADS QUAD_STRIP Rysowane są tylko wierzchołki. Opis Narysowane wierzchołki łączone są liniami. Łączone są tylko poszczególne pary punktów. Narysowane wierzchołki są po kolei łączone segmentami prostych lub krzywych tworząc nieregularną linię łamaną lub krzywoliniową. Narysowane wierzchołki łączone są w trójkąty, łączone są tylko poszczególne trójki punktów. Figury wypełnione są kolorem. Narysowane wierzchołki łączone są po kolei w trójkąty, każde kolejne dwa punkty są łączone z pierwszym wierzchołkiem i z ostatnim wierzchołkiem. Figury wypełnione są kolorem. Narysowane wierzchołki łączone są po kolei w trójkąty, rozpoczynając od trzeciego punktu. Figury wypełnione są kolorem. Narysowane wierzchołki łączone są w prostokąty, łączone są tylko poszczególne czwórki punktów. Figury wypełnione są kolorem. Narysowane wierzchołki łączone są po kolei w prostokąty, rozpoczynając do czwartego punktu. Figury wypełnione są kolorem. W przypadku gdy funkcja endshape() jest wywołana z parametrem CLOSE, pierwszy wierzchołek połączony jest z ostatnim i tworzona jest zamknięta figura o określonej krawędzi i wypełnieniu. W dwóch i trzech wymiarach można także rysować złożone krzywe Beziera i Catmull- Roma, oraz tworzyć figury złożone z połączonych krzywych. W tym celu należy skorzystać z funkcji beziervertex() i curvevertex(). Funkcje beziervertex() lub curvevertex() muszą znaleźć się w definicji nowego kształtu (beginshape() endshape()) tylko w przypadku gdy brak jest parametru RODZAJ. Zaletą funkcji beziervertex() i curvevertex() jest mniejsza liczba parametrów podawanych podczas definicji krzywych niż w przypadku funkcji bezier() i curve().
16 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D Funkcja beziervertex() ma następującą składnię beziervertex(x1, y1, x2, y2, x3, y3) beziervertex(x1, y1, z1, x2, y2, z2, x3, y3, z3) gdzie wszystkie parametry są typu int/float. Parametry funkcji beziervertex() to położenie trzech spośród czterech punktów kontrolnych definiujących krzywą - nie podaje się punktu początkowego, gdyż zakłada się, że pokrywa się on z punktem końcowym poprzedniego segmentu zawartego w obszarze (beginshape() endshape()) Wywołanie funkcji beziervertex() powoduje dodanie nowego segmentu do kształtu zdefiniowanego ciągiem instrukcji zawartych w obszarze (beginshape() endshape()). Jeśli krzywa Beziera jest pierwszym elementem danego kształtu, musi być poprzedzona zdefiniowaniem pierwszego punktu (funkcja vertex()). Funkcja curvevertex() ma następującą składnię curvevertex(x, y) curvevertex(x, y, z) gdzie wszystkie parametry są typu int/float. Parametry funkcji curvevertex() określają położenie kolejnego punktu krzywej interpolującej. Ciąg kolejno wywołanych funkcji tego typu: beginshape(); curvevertex(...); curvevertex(...);... curvevertex(...); endshape( ); powoduje utworzenie krzywej interpolującej wszystkie podane punkty, ale rysowana jest ona tylko pomiędzy drugim podanym punktem a przedostatnim. Pierwszy i ostatni punkt są wykorzystywane jako punkty modelujące kształt krzywej na końcach. Jest to inny sposób definiowania krzywych Catmulla-Roma. Aby narysować przynajmniej jedną krzywą, wymagane jest więc zdefiniowanie co najmniej 4 punktów w obszarze (beginshape() endshape()).
Ćwiczenie P-2 17 Definiowanie i rysowanie obiektów 2Di 3D 4. Transformacje Każdy z punktów w przestrzeni można definiować z wykorzystaniem trzech współrzędnych x, y, z. Każdą grupę punktów można także przetransformować do innych współrzędnych w przestrzeni, zachowując proporcje odległości pomiędzy punktami. Standardowe transformacje, na które pozwala Processing to przesunięcie, obrót i skalowanie. 4.1. PRZESUNIĘCIA Jak pokazano w przykładach z rozdziału 3, położenie środka standardowych kształtów (prostopadłościan, kula) znajduje się w środku układu współrzędnych. Domyślnie wybrany jest on jako (0,0,0), czyli jest umiejscowiony w górnym lewym rogu okna. Do przesunięcia układu współrzędnych stosowana jest funkcja translate(). translate(int/float x, int/float y); translate(int/float x, int/float y, int/float z); Parametrami funkcji translate() jest przesunięcie w kierunkach x, y, z o podaną liczbę pikseli. Przesunięcie ma wpływ tylko na obiekty rysowane po wywołaniu funkcji translate(). Funkcja ta jest funkcją addytywną. Tak więc dwukrotne wywołanie funkcji translate(10,10,10) spowoduje, że środek układu współrzędnych przesunie się o 20 pikseli w każdym z kierunków x, y, z. Ruch w kierunku z spowoduje, że obiekty będą mniejsze lub większe, co wynika z zasad zachowania perspektywy. 4.2. OBROTY W Processingu obrót realizowany jest z wykorzystaniem funkcji rotate() w przypadku grafiki dwuwymiarowej lub rotatex(), rotatey(), rotatez() w przypadku grafiki trójwymiarowej. rotate(int/(float kąt); rotatex(int/float kąt); rotatey(int/float kąt); rotatez(int/float kąt); Parametrem wymienionych funkcji jest kąt obrotu podawany w radianach. Dodatnia wartość parametru definiuje obrót wokół osi OX (rotatex()), OY(rotateY()), OZ(rotateZ()), zgodny z kierunkiem wskazówek zegara. W przypadku każdej z tych funkcji obracana jest cała zawartość okna rysowana po ich wywołaniu. Funkcja obrotu jest funkcją addytywną.
18 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D 4.3. SKALOWANIE Powiększanie lub pomniejszanie kształtów wiąże się ze zmianą odległości pomiędzy definiującymi je wierzchołkami. Wykorzystywana do tej transformacji jest funkcja scale(). scale(sx,sy,sz) Parametry sx, sy, sz są współczynnikami skali w danym kierunku (x, y, z) podawanymi jako liczby rzeczywiste. W przypadku funkcji scale() skalowana jest cała zawartość okna rysowana po jej wywołaniu. Funkcja skalująca jest multiplikatywna: dwukrotne wywołanie funkcji scale(3,3,3), spowoduje przeskalowanie obiektów 9-krotnie w każdym kierunku (900%). 4.4. DEFINIOWANIE WIELU UKŁADÓW WSPÓŁRZĘNYCH STOS MACIERZY Sporo problemów może spowodować addytywność lub multiplikatywność funkcji dokonujących transformacji. W celu ułatwienia pracy, w Processingu umożliwiono zdefiniowanie układów współrzędnych, przy pomocy ciągów funkcji transformujących i rysujących figury ograniczonych instrukcjami pushmatrix() i popmatrix(), oraz rysowanie i przekształcenia figur w tak zdefiniowanym nowym układzie. pushmatrix(); //przekształcenia i rysowanie obiektów w nowym układzie popmatrix(); Użycie funkcji pushmatrix() powoduje, że początek i kierunki osi nowego układu współrzędnych określone są przez funkcje transformujące wywołane przed funkcją pushmatrix(). Kolejne przekształcenia i rysowanie obiektów wykonywane są w nowym układzie współrzędnych. Po wykonaniu funkcji popmatrix() wszystkie zmienne definiujące układ współrzędnych przyjmują wartości, które zostały ustawione przed blokiem pushmatrix() popmatrix(). Możliwe jest zagnieżdżanie bloków definiujących nowe układy współrzędnych. Operacje zagnieżdżania bloków pushmatrix() popmatrix()nazywają się operacjami na stosie macierzy modelowania. Zapisywanie na stosie macierzy przekształceń opisujących transformacje układów powoduje blokowanie układów współrzędnych oraz figur z nimi związanych. Znacznie ułatwia to generowanie scen składających się z wielu brył i ma szczególne znaczenie w przypadku tworzenia animacji. Sposób pracy z wieloma układami współrzędnych zostanie pokazany w następnym podrozdziale. 4.5. RUCH OBIEKTÓW W PRZESTRZENI Na podstawie przykładowej aplikacji, zostaną zobrazowane i wytłumaczone opisane transformacje i przekształcenia układów współrzędnych, oraz praca ze stosem macierzy modelowania. W funkcji inicjującej zostanie określony rozmiar okna, oraz wybrany silnik do generowania sceny 3D.
Ćwiczenie P-2 19 Definiowanie i rysowanie obiektów 2Di 3D Aplikacja 4.1. funkcja inicjująca. void setup(){ size(800,400,p3d); W funkcji rysującej, na początku zostanie przerysowane tło, a następnie ustawiony kolor krawędzi rysowanych brył. Kolejne instrukcje, zamknięte w bloku pushmatrix() popmatrix(), ustawiają na środku okna czerwony sześcian, obracający się wokół wszystkich osi. Środek sześcianu znajduje się w środku układu współrzędnych. Aby animować ruch (obrót) wokół każdej z osi, jako parametr funkcji rotatex(), rotatey(), rotatez(), jest podawana wartość kąta zmieniająca się w zależności od kolejnego numeru klatki (wywołania funkcji rysującej). Numer klatki, obliczany od początku działania aplikacji, jest zwracany jako wynik działania funkcji framecount(). Instrukcja popmatrix() spowoduje, że układ współrzędnych znów będzie miał domyślne położenie i kierunki. Należy teraz określić położenie kolejnych elementów wzgędem siebie. Zostanie tutaj zilustrowane zastosowanie stosu macierzy modelowania. Na początku otwierany jest blok definicji nowego układu współrzędnych i na środku okna zostanie ustawiony pierwszy układ UW_1. Wszystkie obiekty będą obracały się wokół osi OY tego układu. Przy pomocy kolejnej, zagnieżdżonej instrukcji pushmatrix(), zostanie ustawiony układ współrzędnych UW_2, przesunięty względem UW_1 o 150 pikseli w kierunku x. Wszystkie obiekty w układzie UW_2 będą cały czas obracały się wokół osi OX i OY tego układu. W układzie UW_2 rysowany jest kolejny sześcian o bokach w kolorze zielonym. Opisane transformacje powodują, że porusza się on po orbicie czerwonego sześcianu, a przy okazji sam kręci się wokół osi OX i OY przechodzących przez jego środek. Analogicznie, przy pomocy zagnieżdżonego kolejnego bloku definiującego nowy układ współrzędnych (UW_3), rysowany jest niebieski sześcian, krążący po orbicie zielonego sześcianu i kręcący się wokół trzech osi przechodzących przez jego środek. Rysunek 4.1. Okno aplikacji 4.1.
20 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D Aplikacja 4.1. funkcja rysująca void draw ( ) { background(0,0,0); stroke(0); int fc=framecount; //numer klatki int przesx=width/2; int przesy=height/2; pushmatrix(); translate(przesx,przesy); //przesunięcie środka układu współrzędnych rotatex(fc*pi/100); //obrót wokół osi OX rotatey(fc*pi/100); //obrót wokół osi OY rotatez(fc*pi/100); //obrót wokół osi OZ fill(255,0,0); //kolor sześcianu - czerwony box(100,100,100); //rysowanie sześcianu popmatrix(); //informacje o transformacjach - cofnięte pushmatrix(); //określenie UW_1 //przesunięcie środka układu współrzędnych,, wpływa na UW_2 i UW_3 translate(przesx,przesy); rotatey(fc*pi/100); //obrót wokół osi OY, wpływa na UW_2 i UW_3 pushmatrix(); //określenie UW_2,zagnieŜdŜenie 1 translate(150,0); //przesunięcie środka układu współrzędnych rotatex(fc*pi/100); //obrót obiektów wokół osi OX, UW_2 rotatey(fc*pi/100); //obrót obiektów wokół osi OY, UW_2 fill(0,255,0); //kolor sześcianu - zielony box(50,50,50); //rysowanie sześcianu rotatey(fc*pi/60); //obrót wokół osi OY, wpływa tylko na UW_3 pushmatrix(); //określenie UW_3, zagnieŝdŝenie 2 translate(70,0); //przesunięcie obiektów w UW_3 rotatex(fc*pi/30); //obrót obiektów wokół osi OX, UW_3 rotatey(fc*pi/30); //obrót obiektów wokół osi OY, UW_3 rotatez(fc*pi/30); //obrót obiektów wokół osi OZ, UW_3 fill(0,0,255); //kolor sześcianu - niebieski box(20,20,20); //rysowanie sześcianu popmatrix(); //koniec zagnieŝdŝenia 2 popmatrix(); //koniec zagnieŝdŝenia 1 popmatrix();
Ćwiczenie P-2 21 Definiowanie i rysowanie obiektów 2Di 3D 5. Zadania do samodzielnego wykonania 5.1. ZADANIE RYSOWANIE ZŁOŻONYCH KRZYWYCH Treść zadania: Należy napisać aplikację rysującą złożoną krzywą pomiędzy wieloma punktami naniesionymi przez użytkownika na ekran z wykorzystaniem krzywych Camtull-Roma. Każdy kolejny dodawany punkt staje się automatycznie końcowym punktem krzywej. Ostatnie cztery punkty definiują kolejny segment krzywej (4 punkty krzywej Camtull-Roma). Przydatne informacje: Podczas implementacji istotne będzie wywołanie funkcji curve(). Może być także przydatna tablica dwuwymiarowa przechowująca współrzędne punktów, co ułatwia ich rysowanie na ekranie. Uwaga: W Processingu podczas deklaracji tablicy musi być podany jej wymiar. W programie, podczas dodawania punktów można dokonać następującego przepisania, tak aby zwiększyć rozmiar tablicy o jeden element. int[] wspxa=new int[wspx.length+1]; //deklaracja nowej tablicy for (int i=0;i<wspx.length;i++){ //przepisanie elementów wspxa[i]=wspx[i]; wspx=wspxa; //przepisanie tablicy Rysunek 5.1. Przykład okna aplikacji 5.1
22 Ćwiczenie P-2 Definiowanie i rysowanie obiektów 2D i 3D 6. Literatura [1] Bożena Pawlak Processing nowe narzędzie do tworzenia apletów. Analiza możliwości na przykładzie apletów z dziedziny grafiki komputerowej, Praca dyplomowa inżynierska, Wydział Mechatroniki, 2005/2006. [2] Ira Greenberg: Processing: Creative Coding and Computational Art, Friendsof, 2007. [3] Casey Reas, Ben Fry, Processing: A Programming Handbook for Visual Designers and Artists, MIT Press, 2007. [4] Daniel Shiffman: Learning Processing. A Beginners Guide to Programming Images, Animation and Interaction, Elsevier, 2008. [5] Pomoc środowiska Processing.