Fraktalny algorytm generowania terenu 3D w Javascript
Wiele obiektów w przyrodzie ma kształt fraktalu, przykładowo liść paproci, chmury, czy też linia na granicy wody i piasku (nad rzeką/morzem). Nie są to dosłownie fraktale ale mają jedną z ich głównych cech - dowolnie duże powiększenie kształtem wciąż przypomina pierwotny (liść paprotki składa się z mniejszych listków a one z takich samych ale jeszcze mniejszych..).
Podobnie jest z terenem - mamy duże góry, na które nałożone są mniejsze itp.. Można by wykorzystać algorytm fraktalny do wygenerowania takiego kształtu. Służy do tego m.in. algorytm Diamond-square. Poniżej zrzut ze skryptu omówionego później wykorzystującego ten algorytm:
Działa to tak, iż bierzemy kwadrat, jego wierzchołki mogą mieć ustaloną bądź losową wysokość. Obliczamy wysokość środka kwadratu jako średnią wierzchołków plus małe zaburzenie. Wysokość środków boków kwadratu obliczamy jako średnią wysokości końców plus małe zaburzenie. Zaburzenie maleje wraz ze z ilością iteracji (im pod większym powiększeniem oglądamy teren, tym wypukłości są mniejsze - taki skrót myślowy).
Od razu powiem, że w mojej implementacji jest chyba błąd (a nie umiem go znaleźć), bo wynik wychodzi IMHO kiepski. Ogólnie to można to przejrzeć poklikać, zobaczyć OOP w JavaScript czy wykorzystać to do swoich celów ale kod w takiej postaci jak jest teraz jest do dupy :] Nie mówmy o wpadkach: fajnie wyszło to, że klase z algorytmem można podmienić i wygenerować teren wg. innego algorytmu (już mi się nie chciało pisać).
Kod napisany jest w obiektowo w JavaScript'cie, bo to się wydawało najprostsze do tego modelu, a do tego czytelnik może przetestować skrypt online. Do rysowania wykorzystany jest tag <canvas>, więc IE nie poradzi sobie z tym skryptem. Diagram UML klas wygląda tak:
Tym razem nie będzie listingów klas, natomiast będzie bliżej omówiony diagram klas. Teoretycznie to powinno wystarczyć do zrozumienia skryptu.
Klasa Screen odpowiedzialna jest za rysowanie, rzutowanie punktów, czyszczenie ekranu. Anyway ta klasa powinna być singletonem chyba.
- canvasID - by rysować po tagu canvas musimy się do niego dostać, najprościej poprzez JavaScriptowe getElementById, to jest właśnie ID tagu, w którym chcemy rysować
- width, height - opss, nie poprawiłem diagramu, to wymiary tagu na którym rysujemy, w kodzie teraz zamiast tego jest a, czyli dł boku (rysujemy na kwadracie, bo tak jest łatwiej).
- azimuth, elevation, scaleFactor, near, nearToObj - z 3D musimy rzutować obiekty na 2D, by móc je wyświetlać na ekranie (canvas może obsługiwać 3d ale nie znalazłem bliższych szczegółów, więc jest to zakodowane żywcem). Algorytm rzutowania, te wszystkie sinusy etc. jest z gdzieś z neta wygrzebany.
- step - o ile przesuwamy się klikając na góra/dół, prawo/lewo
Terrain to nasz algorytm, podmieniając obiekt tej klasy na obiekt innej można generować teren wg innego algorytmu. Zastanawiałem się, czy to algorytm ma ekran, czy ekran algorytm, tzn który obiekt powinien zawierać który.. Doszłem do wniosku, że to ekran ma algorytm.
- len - ilość pkt wzdłuż i wszerz (czyli w sumie pkt. na mapie jest len*len).
- str - jaki może być rozrzut skrajnych 4 pkt. Bez sensu ta zmienna jest :)
- r, d0 - wygrzebane gdzieś z neta, do wzoru jak powinnien się zmieniać wsp. jaki może byc losowo dodany do punktu wraz z iteracją.
Point3D powinien dziedziczyć po Point2D, ale w JavaScript mi to nie wychodziło, więc są zakodowane żywcem. Pierwsza z tych klas służy do reprezentowania punktów na mapie, w rzeczywistej przestrzeni. Druga jest pomocnicza, by np. narysować linię na ekranie. Klasa Edge symbolizuje krawędź między dwoma punktami w przestrzeni 3D. Jakbyśmy nie rysowali krawędzi to by była tylko bezładna kupa kropek :)
To pierwszy raz, gdy stykam się z OOP w JavaScript i wrażenia są pozytywne. Jest inaczej niż np. w Javie czy w Perlu ale szybko można się połapać. Poniżej pokazany jest działający skrypt:
| up | ||
| left | start | right |
| down |
Poniżej znajduje się spis materiałów do pobrania. Kod testowałe pod Firefox/2.0.0.8. Tak offtopowo na koniec: no ostatni model czegoś na dłuższy czas tutaj, będzie bardziej konkretnie teraz (a niedługo o stream'ach, C i UDP ;))
- Kod źródłowy - jako, że to JS, nie trzeba kompilatora, wystarczy przeglądarka :)
- Diagram klas - plik źródłowy, do otwarcia np. w Umbrello
EDIT: kod przeszedł mały update by był bardziej zgodny z XHTML strict.. opss nie jest :/