Processing/Nie za krótki start
Uwaga techniczna
edytujKod źródłowy w tym podręczniku prezentowany jest z dodatkowymi liczbami na początku każdego wersu. Dzięki temu, łatwiej będzie nam się odnosić do wybranych fragmentów kodu w trakcie omawiania kolejnych zagadnień.
Komentarze, bo się przydają
edytujTo, co następuje po dwóch ukośnikach (sleszach) to tzw. komentarz. Processing tego nie czyta, bo to informacje dla nas - programistów - i naszych znajomych - też programistów - z którymi pracujemy. Komputera to w ogóle nie interesuje. Komentarz taki obejmuje wszystko, co znajduje się za znakiem podwójnego ukośnika - wszystko do końca danej linii:
// to jest komentarz
// a tu jego kontynuacja
To, że komentarz zaczyna się od podwójnego ukośnika daje nam możliwość komentowania na końcu linii tego wszystkiego, co napisaliśmy na jej początku. W ten sposób łatwo tworzyć dokumentację programu opisjąc, co dane komendy konkretnie dla nas robią. Przyjrzyjmy się takiej przykładowej linii kodu:
size( 500, 500 ); // wielkosc okna podana w pikselach
W pierwszej części mamy funkcję size określającą wielkość okna naszego programu oraz napisany po polsku komentarz, który przypomina nam, co funkcja size robi. Proste.
Jedna uwaga językowa. Co prawda komentarze w Processingu mogą być pisane po polsku z polskimi literami, to jednak dobrą praktyką programistyczną jest pisanie ich w języku angielskim lub - jeśli decydujemy się na język polski - to bez polskich liter. Okazuje się bowiem, że czasami chcemy skorzystać z pomocy społeczności programistów Processingowych udzielającej się na oficjalnym forum, a wtedy polskie komentarze mogą okazać się mało przydatne. Co więcej, polskie litery to problem, bo na różnych komputerach różnie z polskimi literami bywa - czasami kończy się tak, że nasi znajomi dostają fragmenty kodu, które w najlepszym wypadku wyglądają tak:
size( 500, 500 ); // wielko?? okna podana w pikselach
Pomimo tych rad i zaleceń, w Polskim podręczniku Processingu stosujemy polskie komentarze (bez polskich liter), żeby nie utrudniać czytania kodu osobom nie znającym dobrze angielskiego.
Naprawdę zaczynamy
edytujPrzejdźmy jednak do programowania. Wiemy już, że funkcja size określa wielkość okna podaną w pikselach. Czym są piksele i jak z nimi pracować dowiemy się dalszych rozdziałach podręcznika. Teraz ważne jest to, że nasz ekran podzielony jest na małe kwadraciki, które nazywać będziemy pikselami. Jeśli czytasz ten podręcznik z monitora laptopa to jest on prawdopodobnie podzielony na 1200 pikseli od lewej do prawej i 800 pikseli od góry do dołu. Znaczy, że:
size( 600, 400 );
tworzy okno o wielkieści +/- jednej czwartej twojego monitora. Okno, które zobaczyliśmy, wypełnione jest na brzydki szary kolor i nieszczególnie nas zachwyca. Łatwo to jednak zmienić dodając do naszego programu funkcję background z odpowiednią informacją o naszym ulubionym kolorze:
size( 600, 400 );
background( 200, 120, 80 );
No i mamy okno rude. Czemu akurat rude? Czym są trzy magiczne numerki w funkcji background?
Kolory - krok pierwszy
edytujMonitor, na którym oglądamy wyniki naszych programów, świeci do nas światłem kolorowym. Ponieważ świeci, kolory te stanowią mieszankę trzech barw podstawowych - czerwonego (R od angielskiego Red), zielonego (G od angielskiego Green) i niebieskiego (B od angielskiego Blue). Wartości podane w nawiasach funkcji background to tzw. wartości RGB składające się koniec końców na nasz rudy kolor. W naszym przykładzie:
background( 200, 120, 80 );
wynikiem jest kolor rudy, czyli sporo czerwonego (200), w sam raz zielonego (120) i nie za wiele niebieskiego (80). Wszystkie wartości, które podajemy funkcji background (i innym funkcjom pracującym z kolorem), zawierają się w przedziale 0-255.
Nie ma co ukrywać, że na początku sprawa może wydawać się nieco zagmatwana (choć po przeczytaniu następnego rozdziału sporo się wyjaśni), dlatego twórcy Processingu przygotowali specjalne narzędzie ułatwiające wybór właściwego koloru. W górnym menu w dziale Tools znajduje się Color Selector. Dzięki niemu możemy łatwo wybrać interesujący nas kolor i przepisać wartości R, G i B np. do funkcji background.
Gładkie kształty
edytujNarysujmy coś wreszcie!
size( 600, 400 );
background( 200, 120, 80 );
ellipse( 300, 200, 50, 50 );
Za chwilę dojdziemy to tego, jak to jest, że ellipse'a to kółko, dlaczego akurat takie, a nie inne i jak zmienić jego kolor. Teraz zwróćmy uwagę na to, że kółko nasze jest nieco kanciaste. Znów nas nie zachwyca. Dlaczego tak się dzieje? Chodzi o to, że Processing traktuje nasze pomysły dość poważnie (czyt. dosłownie) i nie wygładza automatycznie kształtów, które dla nas rysuje. Żeby kółko przypominało kółko, a nie kółko zębate dodamy do naszego kodu funkcję smooth, która każe Processingowi rysować gładko i przepięknie. Spróbujmy:
size( 600, 400 );
background( 200, 120, 80 );
smooth();
ellipse( 300, 200, 50, 50 );
Kolory - krok drugi
edytujWcześniej pisaliśmy, że kolor podajemy do różnych funckji jako trzy wartości RGB (czerwony, zielony i niebieski). Co jednak jeśli chcemy mieć kolor szary? Jaśniejszy lub ciemniejszy, ale jednak szary! Okazuje się, że szarości otrzymujemy z wymieszania takich samych ilości kolorów podstawowych (np. R: 80, G: 80 i B: 80). Twórcy Processingu pozwalają nam na nieco lenistwa i kolory z tzw. skali szarości (od czarnego do białego) wpisywać możemy podając jedną tylko wartość wspólną dla wszystkich składowych RGB.
size( 600, 400 );
background( 200, 120, 80 );
smooth();
strokeWeight( 5 ); // grubosc obwodki
stroke( 80 ); // kolor obwodki ustawiamy na ciemno szary
fill( 180 ); // wypelnienie kolka jasno szare
ellipse( 300, 200, 50, 50 );
Poza dwoma funkcjami odpowiedzialnymi za kolor kółka, pojawiła się w tym kodzie jeszcze jedna ciekawa funkcja: strokeWeight. Poza tym, że ustawia ona grubość obwódki naszego koła na 5 pikseli, interesuje nas z jeszcze jednego powodu. Jej nazwa zapisane jest w konwencji określanej jako lowerCamelCase, czyli tłumacząc to nieco pokracznie niższa składnia wielbłądzia. Chodzi o to, że nazwa ta składa się z dwóch słów sklejonych ze sobą w taki sposób, że pierwsze piszemy małą literą (stąd lower), a pozostałe słowa (w tym przypadku tylko jedno dodatkowe słowo) wielką literą. W efekcie dostajemy długie słowo z kilkoma garbami (stąd camelCase). W Processingu najlepiej stosować składnię lowerCamelCase wszędzie gdzie to możliwe (poza nazwami klas, o których za jakiś czas).
Daj mi więcej!
edytujNarysować koło na środku okna to nie sztuka. Narysować trzy koła to już wypas:
size( 600, 400 );
background( 200, 120, 80 );
smooth();
// grubosci, kolory itp
strokeWeight( 5 );
stroke( 80 );
fill( 180 );
// rysujemy
ellipse( 300, 200, 100, 100 ); // duze kolko
ellipse( 300, 200, 75, 75 ); // srednie kolko
ellipse( 300, 200, 50, 50 ); // male kolko
Teraz już wszysto jest jasne. W nawiasy ellipse'y wpisujemy odpowiednio: pozycję poziomą środka koła (x), pozycję pionową środka koła (y), średnica pozioma (hr) i średnica pionowa (vr). Ogólnie moglibyśmy zapisać to, jako:
ellipse( x, y, hr, vr );
Jeśli obie średnice ellipse'y są równe sobie, otrzymujemy ładne kółko. W naszym przykładzie wszystkie kółka znajdują się na środku ekranu ( 300 x 200 ) oraz mają zmiejszającą się średnicę.
Od góry do dołu
edytujProcessing rysuje wszystko od góry do dołu. Nasz program moglibyśmy zatem przeczytać w taki sposób:
wielkość okna --> 600 x 400
tło --> rude ( R: 200, G: 120, B: 80 )
kształty gładkie
grubość linii --> 5 pikseli
kolor linii --> ciemno szary ( RGB: 80 )
wypełnienie --> jasno szare (RGB: 180 )
rysuję kółko:
pozycja --> w centrum okna ( 300 x 200 )
wielkosc --> duze ( 100 )
rysuję kółko:
pozycja --> w centrum okna ( 300 x 200 )
wielkosc --> srednie ( 75 )
rysuję kółko:
pozycja --> w centrum okna ( 300 x 200 )
wielkosc --> male ( 50 )
Oczywiście komputer myśli w nieco bardziej złożony sposób. Dla nas jednak ważne jest to, że komendy w Processingu wykonywane są od góry do dołu. Spójrzymy na taką sytuację:
size( 600, 400 );
background( 200, 120, 80 );
smooth();
// grubosci, kolory itp
strokeWeight( 5 );
stroke( 80 );
fill( 180 );
// rysujemy
ellipse( 300, 200, 100, 100 ); // duze kolko
ellipse( 300, 200, 75, 75 ); // srednie kolko
// zmiana koloru
fill( 220 );
ellipse( 300, 200, 50, 50 ); // male kolko
Moglibyśmy przeczytać ten fragment tak:
wielkość okna --> 600 x 400
tło --> rude ( R: 200, G: 120, B: 80 )
kształty gładkie
grubość linii --> 5 pikseli
kolor linii --> ciemno szary ( RGB: 80 )
wypełnienie --> jasno szare ( RGB: 180 )
rysuję kółko:
pozycja --> w centrum okna ( 300 x 200 )
wielkosc --> duze ( 100 )
rysuję kółko:
pozycja --> w centrum okna ( 300 x 200 )
wielkosc --> srednie ( 75 )
zmiana koloru wypełnienia --> bardzo jasny szary ( RGB: 220 )
rysuję kółko:
pozycja --> w centrum okna ( 300 x 200 )
wielkosc --> male ( 50 )
Wszystko gra! Grubość linii i jej kolor nie zmieniły w ostatnim kółku, bo o to nie prosiliśmy. Co Co jednak, gdybyśmy chcieli, żeby nasze koło w ogóle nie miało wypełnienia lub obwodu? Są do tego dwie funkcje: noFill i noStroke. Wykorzystajmy je obie:
size( 600, 400 );
background( 200, 120, 80 );
smooth();
// grubosci, kolory itp
strokeWeight( 5 );
stroke( 80 );
fill( 180 );
// rysujemy
ellipse( 300, 200, 100, 100 ); // duze kolko
ellipse( 300, 200, 75, 75 ); // srednie kolko
// zmiana koloru
fill( 220 );
ellipse( 300, 200, 50, 50 ); // male kolko
noStroke(); // kasujemy obwód
// male kolka po bokach
ellipse( 230, 200, 20, 20 );
ellipse( 370, 200, 20, 20 );
noFill(); // kasujemy wypelnienie
stroke( 80 ); // kolor obwodu ustawiamy na RGB: 80
// duze kolka po bokach
ellipse( 230, 200, 40, 40 );
ellipse( 370, 200, 40, 40 );
Voila! Działa, tak jak się tego spodziewaliśmy!
W praktyce
edytujUmiejętność rysowania kół to niby niewiele. Ale koła w kołach kołami poprzekładane dają czasami fajne efekty. Np. takie coś:
size( 200, 200 ); // okno nieduze
background( 255 ); // biale tlo
smooth(); // wygladzanie
// bez obwodu - samo "zolte" wypelnienie
noStroke();
fill( 200, 200, 100 );
// centralne kolko
ellipse( 100, 100, 100, 100 );
// biale kolka maskujace czesc centralnego
fill( 255 );
ellipse( 150, 100, 50, 50 );
ellipse( 100, 150, 50, 50 );
ellipse( 50, 100, 50, 50 );
ellipse( 100, 50, 50, 50 );
// niebieskie kolka
fill( 100, 100, 200 );
ellipse( 100 + ( 50 * 0.7 ), 100 + ( 50 * 0.7 ), 25, 25 );
ellipse( 100 - ( 50 * 0.7 ), 100 + ( 50 * 0.7 ), 25, 25 );
ellipse( 100 - ( 50 * 0.7 ), 100 - ( 50 * 0.7 ), 25, 25 );
ellipse( 100 + ( 50 * 0.7 ), 100 - ( 50 * 0.7 ), 25, 25 );