piątek, 24 lutego 2017

Processing vs. Java

Processing jest ściśle związany z Javą, podobnie jak C++ jest związane z C.
Można dzięki temu w miarę łagodnie i etapami przejść od programowania w Processingu do programowanie w Javie.
Można najpierw programować Processing w Eclipse - ulubionym IDE starych wyjadaczy Javy, potem w Eclipse programować w Javie, ale z użyciem bibliotek Procesingu, potem mieszając...
Aż wreszcie zostać z samą Javą...
A wtedy ofert pracy nie zabraknie.

http://www.instructables.com/id/Proclipsing-Using-the-Eclipse-IDE-for-Processing-p/

https://youtu.be/0cqSjyvA8EY

środa, 15 lutego 2017

Walentynkowy wykres funkcji 2D

Z okazji walentynek zrobiliśmy wariację na temat dwuwymiarowej funkcji ( f(x,y) ).

Korzystamy z ogólnej idei krzywych "sercokształtnych", jednak zamiast rysować krzywą, badamy cały szeroki zakres x, y kolorując wyniki.
Program oparty jest na poprzedniej wizualizacji funkcji 2D więc nie prezentuje go w całości - brakujące elementy są takie jak poprzednio:


Główna różnica polega na rozbudowanej instrukcji wybierającej kolor, która może posługiwać się skalą, albo też tylko kolorować jakościowo różne obszary (większe od zera, zero i mniejsze od zera). Można śmiało powalczyć z kolorystyką, no i z innymi niż zaimplementowana funkcjami.

środa, 8 lutego 2017

Szkocka krata i gra w życie - czyli sąsiedztwo Moora

Dotychczasowe przykłady automatu dwuwymiarowego stosowały tzw. sąsiedztwo von Neumana. Alternatywą jest sąsiedztwo Moore, które teraz zaimplementujemy w naszym automacie z regułą modulo:
Różnica polega na tym, że sąsiadami komórki aktualnej, poza sąsiadującymi "przez ścianę", są też komórki sąsiadujące "po rogach". Czyli zamiast 4 sąsiadów komórka ma ich 8.
Reszta programu pozostaje bez zmian, ale efekt mniej już przypomina arabskie mozaiki, a bardziej jakieś tkaniny - np. momentami serwetki, a czasem szkocką kratę...

Mając już taką wersję automatu komórkowego jesteśmy dosłownie o krok od zaimplementowania najsławniejszego z automatów - "Life" czyli "gry w życie" Johna Conwaya.
Po pierwsze deklarujemy trzy zmienne (linie 12-14), które są parametrami modelu - minim to najmniejsza liczba sąsiadów przy których komórka przeżywa, czyli jej stan pozostaje równy 1, maxim to maksymalna liczba sąsiadów przy których komórka przeżywa, a birth  to liczba sąsiadów, przy której komórka "martwa" (o stanie 0) może stać się żywa, czyli posiąść stan 1. Wartości domyślne tych zmiennych odpowiadają parametrom Life Conwaya, ale modyfikując je możecie zbadać też niektóre inne automaty z tej rodziny - czasem są zaskakujące.
Po drugie, ze względów kosmetycznych staramy się żeby szerokość i wysokość świata automatu była wielokrotnością 3 (linia 6) i  wyświetlamy sobie kontrolnie na konsoli realny rozmiar świata i okna, żeby mieć pewność że okno ma wystarczający rozmiar.
Wreszcie zmieniamy inicjalizację automatu w taki sposób żeby był zasiewany wyłącznie jedynkami - bo takie są reguły tego modelu.
Po modyfikacji deklaracji i procedury setup() możemy przejść do implementacji reguły automatu w procedurze draw():
Sama reguła (linie 87-98) zależna jest obowiązkowo od stanu komórki aktualnej. 
  • Jeśli jest ona martwa to może ożyć wtedy i tylko wtedy gdy ma birth sąsiadów. 
  • Gdy jest już żywa, to może przeżyć jeśli jej liczba sąsiadów mieści się w zakresie od minim do maxim.

W obu przypadkach zapisujemy obie wartości do tablicy WorldNew, bo nie zawiera ona stanu poprzedniego tylko poprzedni od poprzedniego. Można by ten fragment zoptymalizować, ale chyba nie warto...
Można też zredukować liczbę kolorów w pętli wyświetlania co nieco przyśpieszy wykonanie instrukcji switch. Zrobiłem to (linie 54-58), ale nie sprawdzałem jaki jest zysk, bo i tak wąskim gardłem tego programu jest przede wszystkim wyświetlanie.
Poniżej efekt działania po dosyć dużej liczbie kroków, gdzie praktycznie całość świata zajmują już struktury niezmienne "martwe natury" i proste oscylatory...
Ale i tak najciekawsze jest obserwowanie dynamiki - żadem obrazek tego nie odda. Obserwując uważne uda wam się na pewno zaobserwować tzw. szybowce, ale może też inne gatunki "statków kosmicznych"



poniedziałek, 6 lutego 2017

Przyśpieszanie automatu 2D

Automat prezentowany poprzednio jest wystarczająco szybki, żeby go obserwować, ale gdybyśmy chcieli robić jakieś eksperymenty - np. sprawdzać okresowość, albo gdybyśmy zwiększyli rozmiar świata do 1000x1000 to byłoby prawdopodobnie już zbyt wolno. Przynajmniej na moim komputerze robiło się nużąco ;-)
W jaki sposób możemy ten program przyśpieszyć?
Prawdziwi programiści dokonali by tzw. profilowania kodu i ustalili, w których liniach program "siedzi" najdłużej. Wy na razie musicie mi uwierzyć na słowo - w tym przypadku najkosztowniejsze czasowo jest po prostu rysowanie na ekranie, czyli wywołania procedury point().
Co możemy na to poradzić skoro za każdym razem musimy odrysować stan całego automatu?
Ale czy na pewno?
Zauważcie że jeśli startujemy od pojedynczej komórki (co jak ustaliliśmy jest znacznie ciekawsze niż start z losowego wypełnienia większą ich liczbą) to widzimy wyraźną okresowość działania automatu. Bardzo rzadko faktycznie zmienia się stan wszystkich komórek - zazwyczaj spora cześć, zwłaszcza czarnych pozostaje w tym samym kolorze...
Gdybyśmy wiedzieli czy komórka na ekranie ma już właściwy kolor moglibyśmy rysować tylko to co się zmieniło, zmniejszając liczbę wywołań kosztownej operacji point().
Ale przecież wiemy! Potrzebna informacja jest już w programie - wystarczy porównać komórkę tablicy WorldOld z tablica WorldNew.
Pojawia się jednak problem kosmetyczny. Napis który dotychczas pojawiał się na co krok odświeżanym tle jest teraz na skraju pola, który odświeżany jest stosunkowo rzadko, więc wyniki działania kolejnych wywołań text() nakładają się na siebie.
Musimy więc przenieść napis gdzieś poza obszar wyświetlania świata i samemu zadbać o odświeżanie tła. To właśnie wykonywane jest w liniach 93-96.
Wszystko w porządku?
Nie?
Oczywiście że nie - chyba że sami wpadliście na konieczne modyfikacje setupu.
Po pierwsze okno musi mieć dodatkowe miejsce na wyświetlanie napisu. Będzie więc trochę prostokątne (linia 15).
Ale jest jeszcze jedna, poważniejsza sprawa. Musimy zadbać o to, żeby w pierwszym kroku CAŁA tablica New różniła się od tablicy Old.  Gwarantuje to wypełnienie tablicy New wartościami -1. Tablica Old jest przez system wypełniona zerami z pewną domieszką naszych wylosowanych stanów. Dzięki temu wszystko zadziała...
Czyli udaje nam się przynajmniej okresowo osiągnąć prędkość ok. 70 klatek na sekundę. Na waszych nowych komputerach może być nawet szybciej.

Ale bez przesady z tymi zachwytami ;-) Cudów nie ma i ta optymalizacja nie zawsze zadziała, a wręcz może być niepotrzebnym obciążeniem. Kiedy?
Wtedy gdy wystartujemy z dosyć gęstego zasiewu, np. Dens=0.1
W takiej sytuacji dosyć szybko osiągamy stan, w którym każda komórka zmienia się w każdym kroku i znowu trzeba odrysować każdą. 


niedziela, 5 lutego 2017

Rozbudowa automatu 2D

Teraz rozbudujemy nasz dwuwymiarowy automat komórkowy tak, żeby zabawa była ciekawsza ;-)
Przy okazji porównamy szybkości naszych komputerów.
Na początek zmiany w setupie:
  • Po pierwsze zwiększamy tablice, żeby automat miał więcej miejsca na rozwój. Tą liczbę warto lekko tuningować (zwiększając lub zmniejszając małe liczby) ponieważ każde ustawienie automatu ma swoją naturalną szerokość i jeśli obwód torusa (jakim jest świat naszego automatu) pokryje się z naturalną szerokością, to wynik będzie wyglądał tak jak w przestrzeni "nieskończonej".
  • Po drugie dodajemy zmienne Div i self, analogicznie do tego co robiliśmy w jednowymiarowej implementacji reguły modulo. 
  • Po trzecie poprawiamy losowanie (linia 24), dopasowując je do istnienia zmiennej Div. Musimy użyć rzutowania na (int) bo wynik funkcji random() w Processingu jest typu float co może niemile zaskakiwać osoby przyzwyczajone do C/C++ czy Pascala.
  • Możecie też sprawdzić co by było gdyby wykomentować linię 16. U mnie efekt jest fatalny - odblokowanie antyaliasingu bardzo spowalnia program, nie mówiąc już o tym, że dla automatu komórkowego jest to jedynie źródło artefaktów graficznych.
Przejdźmy teraz do zmian w procedurze draw():
Tu też mamy kilka zmian, choć raczej kosmetycznych.
  1. W pętli zmiany stanu automatu (linie 55-75) używamy nowych zmiennych Div i self. Div służy oczywiście jako dzielnik operacji modulo (linia 73) a self pozwala zadecydować czy aktualny stan komórki ma/nie ma wpływu na stan następny za pomocą znanej nam już operacji ?:   (linia 66).
  2. W pętli rysującej dodaliśmy kolejne możliwe kolory konturów i pikseli (linie 42 i 43)
  3. Napis w numerem kroku przesunęliśmy na dół okna zmieniając współrzędną y na wartość WorldSize. Uzupełniliśmy go też o wartość definiowanej przez Processing zmiennej frameRate, która podaje realną wydajność programu w postaci średniej liczby klatek na sekundę.

Ustawiając w procedurze setup() dużą wartość frameRate() wymuszamy na Processingu maksymalny "wysiłek", ale nie mamy żadnej gwarancji że realna liczba klatek będzie choćby zbliżona. W końcu tej roboty jest całkiem sporo - narysowanie każdego punktu to wywołanie procedury point(), a tych wywołań jest ponad ćwierć miliona na klatkę obrazu!
Zresztą sami zobaczcie i podzielcie się informacją w komentarzach na blogu.

Dla Dens=0.1 efekt graficzny nie będzie zbyt ciekawy, za to wynik liczby klatek na sekundę maksymalnie miarodajny.
Dla dalszych prac proponuję jednak wrócić do inicjowania pojedynczą komórką i pomanipulować parametrami modelu self, Div i WorldSize