Sekwencja wykałaczkowa w LOGO

Sekwencja wykałaczkowa w LOGO

Powyższy obrazek przedstawia pierwsze 8 poziomów wywołania funkcji dywan w postaci „dywan 1 x 50”, gdzie x to kolejny numer wywołania. Taki układ odcinków, dodawanych po kolei do siebie nazywamy w matematyce sekwencją wykałaczkową (toothpick sequence).

Funkcja ta powoduje wyrysowanie rekurencyjnej mozaiki posiadającej właściwości zbliżone do fraktali. Zasada tworzenia tej mozaiki opiera się na selektywnym dorysowywaniu kolejnych odcinków pionowych i poziomych w ściśle określonych punktach przestrzeni. W efekcie powstaje powtarzający się układ kwadratów i prostokątów.

W pierwszym kroku rysujemy odcinek o długości 50 jednostek.
Krok drugi to dorysowanie na końcach istniejącego odcinka nowych odcinków, w pozycji prostopadłej do istniejącego, w taki sposób, aby środek nowego odcinka dotykał końca odcinka już istniejącego.

W każdym kolejnym kroku rysujemy na każdym wolnym końcu odcinków nowe odcinki zgodnie z tą samą zasadą: prostopadle do istniejącego oraz środek nowego na końcu istniejącego. Poniższy rysunek przedstawia tą zasadę, zaznaczając grubszą linią nowe odcinki z danego kroku.

Sekwencja wykałaczkowa w LOGO

Cała powstająca struktura jest formą zbliżoną do fraktalu. Rośnie z podobnych do siebie kształtów na rogach a przy powtórzeniu będącym potęgą liczby 2, uzyskuje najgęstsze pole na powierzchni najbardziej przypominającej kwadrat.
Aby uzyskać taki efekt będziemy potrzebować zasadniczo dwóch funkcji podstawowych, oraz szeregu funkcji pomocniczych, zmniejszających poziom skomplikowania kodu w głównej pętli programu. Poniżej przedstawiam wszystkie funkcje programu wraz z ich omówieniem. Funkcje posiadają dla ułatwienia numerowane linie, tak by łatwiej można było omówić poszczególne elementy kodu. Numery linii, zakończone kropkami przy wpisywaniu kodu do Logo Komeniusza muszą zostać pominięte.

oto dywan :r :n :a
  jeśli i ( reszta :n 2 = 0 ) ( :r = 1 ) [narysuj_x :n :a]
  jeśli i ( reszta :n 2 = 0 ) ( :r = 2 ) [narysuj_y :n :a]
  jeśli i ( reszta :n 2 = 1 ) ( :r = 1 ) [narysuj_y :n :a]
  jeśli i ( reszta :n 2 = 1 ) ( :r = 2 ) [narysuj_x :n :a]
już

Linia numer 1. Definiuje funkcję o nazwie dywan, służącą do wywołania rysowania całej mozaiki. Parametry użyte w funkcji oznaczają odpowiednio:
r – układ pierwszego elementu, dla wartości 1 pionowy, dla wartości 2 poziomy;
n – głębia mozaiki lub ilość powtórzeń dodawania kolejnych serii odcinków, przyjmuje wartości dodatnie, całkowite, nie mniejsze niż 1;
a – długość rysowanego odcinka w pikselach, sugerowana liczba całkowita, większa od 10, ale jedyny warunek jaki musi spełnić to bycie liczbą większą od 0.

Linie od 2 do 5 powodują wykonanie pierwszego odcinka w prawidłowym kierunku, w zależności od wpisanej wartości r oraz ilości powtórzeń rysowania n.
Linia 6 to standardowe zakończenie funkcji w języku LOGO.

Następne dwie funkcje są identyczne i odwołują się wzajemnie do siebie samych, różnią się jedynie literą x lub y w nazwie i odwołaniu do drugiej funkcji, które to różnice zaznaczę w opisie.

oto narysuj_x :n :a
  pod
  jeśli ( :n <= 0 ) [stop]
  narysuj_y :n - 1 :a
  pozycjonowanie :n :a
  niech "repetycja długość :listapoz
  powtórz :repetycja [pod nowex element numpow :listapoz powtórz :repetycja [pod nowey element numpow :listapoz tester]]
  powtórz długość :listapkt [pod napoz element numpow :listapkt rys_x :a]
  napoz [0 0]
  jeśli i ( kolorpkt = 15 ) ( :n = 1 ) [rys_x :a]
  opu
już
oto narysuj_y :n :a
  pod
  jeśli ( :n <= 0 ) [stop]
  narysuj_x :n - 1 :a
  pozycjonowanie :n :a
  niech "repetycja długość :listapoz
  powtórz :repetycja [pod nowex element numpow :listapoz powtórz :repetycja [pod nowey element numpow :listapoz tester]]
  powtórz długość :listapkt [pod napoz element numpow :listapkt rys_y :a]
  napoz [0 0]
  jeśli i ( kolorpkt = 15 ) ( :n = 1 ) [rys_y :a]
  opu
już

Linia 1. Definiuje nazwę funkcji głównej narysuj_x lub narysuj_y. Obie funkcje wywoływane są z parametrami zgodnymi z tymi opisanymi przy funkcji dywan.

Linia 2. Jedno z kilku miejsc w kodzie, które gwarantują, że żółw będzie przesuwać się po ekranie bez pozostawiania śladów.

Linia 3. Zabezpieczenie przed wykonywaniem pętli w nieskończoność. Jeśli numer powtórzenia pętli będzie równy liczbie 1, to pętla zostanie zatrzymana i rozpocznie się wykonywanie kolejnych powtórzeń z rosnącą liczbą określającą pole powierzchni jakie zajmie gotowa mozaika na ekranie.

Linia 4. Odwołuje się do funkcji przeciwnej do aktualnie wykonanej, która ostatecznie będzie odpowiadać za wyrysowanie odcinków prostopadłych do rysowanych w bieżącej funkcji. Funkcja jest wywoływana z numerem pomniejszonym o 1 oraz ma przekazany parametr a, odpowiedzialny za informację o długości rysowanych odcinków.

Linia 5. Wywołanie funkcji pomocniczej, która przygotowuje listę pozycji na ekranie, w których mogą kończyć się odcinki już narysowane. Funkcja będzie omówiona szczegółowo w dalszej części opisu.

Linia 6. Przypisuje do nowej zmiennej tymczasowej repetycja informację o ilości punktów obecnych na liście listapoz wygenerowanej w linii numer 5.

Linia 7. Jest wyjątkowo długa, ale działanie ma bardzo proste. Funkcja bierze z listy listapoz wszystkie punkty i wywołuje dla każdego z nich test w funkcji tester. Ta część funkcji składa się z pętli w pętli, gdzie pierwsza pętla generuje koordynaty „x” a druga koordynaty „y” każdego z punktów, używając jednowymiarowej listy współrzędnych z listapoz, która jest przetwarzana w funkcji tester na dwuwymiarową listę koordynatów punktów, w których już narysowane odcinki rzeczywiście się kończą.

Linia 8. Pobiera z listy punktów wygenerowanych w funkcji tester koordynaty i rysuje w nich nowe odcinki przy pomocy funkcji rys_x lub rys_y, w zależności od numeru pętli głównej.

Linia 9. Ustawia żółwia na pozycji wyjściowej w środku ilustracji na koordynatach 0,0.

Linia 10. Jest wykonywana tylko jeden raz w całym programie i odpowiada za wyrysowanie pierwszego, centralnego odcinka w środku obrazka. Jest w niej testowany kolor piksela, na którym stoi żółw, będąc na koordynatach 0,0. Jeśli jest to biały kolor a numer powtórzenia pętli głównej programu jest równy 1, to zostanie wyrysowany odcinek zgodny z kierunkiem określonym w funkcji dywan.

Linie 11 i 12. Resetują ustawienie pisaka żółwia, pozwalając mu ponownie rysować oraz kończą funkcję.

oto pozycjonowanie :n :a
  przyp "listapoz [0]
  przyp "listapkt []
  jeśli ( :n > 1 ) [powtórz ( :n + 1 ) / 2 [przyp "listapoz nak ( numpow / 2 * :a ) * - 1 :listapoz]]
  jeśli ( :n > 1 ) [powtórz ( :n + 1 ) / 2 [przyp "listapoz nak ( numpow / 2 * :a ) :listapoz]]
już

Funkcja pomocnicza „pozycjonowanie” jest odpowiedzialna za przygotowanie wstępnej listy koordynatów, w których mogą kończyć się odcinki już narysowane. Parametry, które otrzymuje są zgodne z numerem powtórzenia pętli głównej (n) oraz przekazują bazową długość rysowanego odcinka (a).
Linie 2 i 3. Obie linie tworzą puste (lub prawie puste) listy liczb, pierwsza listapoz zbiera liczby wygenerowane w tej funkcji, druga lista o nazwie listapkt pozostaje pusta i jest jedynie przygotowaniem do wypełnienia punktami w ramach wykonanie funkcji tester w późniejszym czasie.

Linie 3 i 4. Różnią się między sobą tylko obecnością mnożenia przez -1 w jednym miejscu. Linie te na podstawie zdobytych w parametrach informacji o numerze powtórzenia oraz długości odcinka, generują zestaw liczb będący wielokrotnością połowy długości bazowej rysowanego odcinka, bowiem tylko w tych punktach może nastąpić zakończenie któregoś z rysowanych odcinków. Na każdej liście znajduje się liczba 0 oraz para liczb parzystej i nieparzystej będąca wielokrotnością połowy długości odcinka. Każda taka para jest dodawana do zbioru po zwiększeniu numeru powtórzenia o 2. Liczby te zapisywane są na liście listapoz.

Pierwszy odcinek o długości x ma swój środek w punkcie 0,0 a końce w punktach oddalonych o pół długości odcinka, czyli x/2 co w zależności od wywołanej funkcji da punkty [0,x/2] i [0,-x/2] lub [x/2,0] i [-x/2,0]. Zatem na listapoz będzie to widoczne jako lista [0, x/2, -x/2], czyli lista wszystkich liczb jakie można napotkać przy tworzeniu punktów, gdzie może istnieć koniec losowego odcinka.

oto rys_x :a
  pw 90 ws :a / 2 opu np :a pod ws :a / 2 lw 90
już
oto rys_y :a
  ws :a / 2 opu np :a pod ws :a / 2
już

Funkcje pomocnicze rys_x i rys_y odpowiadają bezpośrednio za wyrysowanie odcinka pionowego (rys_y) lub poziomego (rys_y) o zadanej długości (a). W obu przypadkach żółw podnosi pisak, przesuwa się na koniec odcinka, opuszcza pisak, rysuje cały odcinek, znów podnosi pisak i wraca na pozycje startową. W przypadku rysowania odcinka poziomo funkcja instruuje żółwia o potrzebie obrócenia się przed przesunięciem. W jednej i drugiej instrukcji żółw kończy operację dokładnie w tym samym punkcie, w którym ją rozpoczął oraz jest ustawiony w dokładnie tym samym kierunku co przed rozpoczęciem rysowania.

Ostatnia funkcja tworzy listę punktów, w której realnie znajduje się koniec narysowanego odcinka.

oto tester
  niech "warunek 0
  niech "terazx pozx
  niech "terazy pozy
  jeśli ( kolorpkt = 0 ) [niech "warunek :warunek + 1]
  pod nowexy :terazx + 3 :terazy jeśli ( kolorpkt = 0 ) [niech "warunek :warunek + 1]
  pod nowexy :terazx - 3 :terazy jeśli ( kolorpkt = 0 ) [niech "warunek :warunek + 1]
  pod nowexy :terazx :terazy + 3 jeśli ( kolorpkt = 0 ) [niech "warunek :warunek + 1]
  pod nowexy :terazx :terazy - 3 jeśli ( kolorpkt = 0 ) [niech "warunek :warunek + 1]
  pod nowexy :terazx :terazy
  jeśli ( :warunek = 2 ) [przyp "listapkt nak poz :listapkt]
już

Funkcja tester nie potrzebuje pobierać bezpośrednio dodatkowych informacji od innych funkcji. Korzysta jedynie z listy punktów wygenerowanych w funkcji pozycjonowanie. Funkcja w dużej części jest praktycznie taka sama, linie 6-9 różnią się tylko miejscem umieszczenia znacznika +3 i 3, o czym za chwilę.

Linie 2-4. Linie te definiują tymczasowe zmienne pomocnicze używane wyłącznie wewnątrz aktualnego wywołania funkcji i tylko w tej jednej funkcji. Odpowiednio warunek, czyli czynnik błędu, lub licznik pikseli oraz aktualne położenie żółwia na ekranie (terazx i terazy). Zmienna warunek rozpoczyna istnienie mając wartość 0, która będzie rosła zgodnie z ilością pikseli, które znajdują się dookoła żółwia oraz bezpośrednio pod nim. Pikseli tych może być maksymalnie 5 i taką właśnie wartość maksymalnie powinna przybrać ta zmienna.

Linia 5. Jeśli żółw stoi na jakiejś linii, to warunek jest zwiększany o 1. W tym miejscu można by przerwać dalsze sprawdzanie, gdyż jeśli żółw nie stoi na linii, to w danym miejscu nie kończy się żaden odcinek i można przejść do dalszego sprawdzania. Ta funkcjonalność nie jest jednak zaprogramowana. Jeśli czujesz się, drogi czytelniku, na siłach, to popraw funkcję tak, aby po uzyskaniu wyniku 0 w tym miejscu, pozostała część sprawdzania nie odbyła się, a żółw mógł rozpocząć sprawdzanie następnych punktów na ekranie.

Linia 6. Powoduje tymczasowe przeskoczenie żółwia o 3 piksele w prawo i sprawdzenie obecności linii w tamtym miejscu. Jeśli linia tam istnieje, to warunek rośnie o 1.

Linia 7. Działa podobnie jak linia 6, ale powoduje przeskoczenie żółwia w lewo.

Linie 8 i 9. Analogicznie jak poprzednio, ale żółw skacze o 3 piksele w górę lub w dół.

W tym momencie mamy sprawdzone na okoliczność obecności linii pięć punktów ekranu w formie krzyża, gdzie środek jest punktem znajdującym się w punkcie obecnym na liście z procedury pozycjonowanie, którego koordynaty są przekazane do tej funkcji w ramach wykonanie funkcji narysuj_x lub narysuj_y. Jeśli spojrzymy na układ sprawdzanych w tej funkcji punktów, zauważymy rzecz następującą. Jeśli żółw zaczynał na końcu linii to wartość zmiennej warunek będzie równa 2. Po punkcie za obecność linii pod żółwiem oraz w jednym z kierunków lewo, prawo, góra lub dół. Jeśli wartość będzie mniejsza niż 2, to mamy do czynienia z nieprawidłowym ustawieniem żółwia. Wartość warunku nigdy nie powinna być równa liczbie 1. Przy warunku = 0 żółw znajduje się całkowicie poza rysunkiem. Przy wartości większej niż 2, wiemy, że w tym punkcie spotykają się przynajmniej dwa końce odcinków, zatem żółw nie będzie w tym miejscu rysować odcinka poprzecznego.

Linia 10. Ustawia żółwia w pozycji wyjściowej, dokładnie w punkcie, jest to potrzebne do prawidłowego zapisania wyniku testu w następnej linii.

Linia 11. Odpowiada za zapisanie aktualnego położenia żółwia na ekranie do listy listapkt. Jest to kompletny koordynat określonego punktu, w którym znajduje się koniec jednego odcinka.

W ten sposób powstaje, krok po kroku ciekawa konstrukcja matematyczna określana jako „sekwencja wykałaczkowa” (toothpick sequence). Sekwencja w swoim zachowaniu przypominająca fraktal. Poniżej kilka propozycji dodatkowych zadań dla czytelnika:

  • Pierwszym zadaniem może być policzenie ile dokładnie wykałaczek (odcinków) jest dodawanych do rysunku w każdym kolejnym kroku oraz z ilu dokładnie odcinków składa się obraz w danym kroku.
  • Drugim zadaniem dla czytelnika jest takie przerobienie programu, aby oszczędzić mocy procesora w wykonywaniu kolejnych kroków. Jak już wcześniej wspominałem, procedurę „testera” można przerwać od razu, bez wykonywania dalszych linii i sprawdzeń już po spełnieniu warunku z linii 5.
  • Kolejnym zadaniem jest zamienienie prostego odcinka na odcinek złamany w połowie, tworzący niepełny klin, tak jak w nawiasie trójkątnym <, pamiętając że w pierwszym kroku ustawimy odcinek na przykład tak: \/ aby w następnym uzyskać figurę zbliżoną do >\/< (o tyle o ile czcionka pozwala oddać efekt takiego układu połamanych odcinków).

Zostaw komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

osiemdziesiąt siedem − 85 =