Projekty

Nowości w projektach

Opublikowany nowy projekt - Labirynt 4D

Opublikowałem dziś kolejny ze swoich projektów. W tytule notki napisałem, że nowy, ale nie jest to do końca prawda.

Historia

Labirynt 4D jest programem, którego pierwotną wersję stworzyłem około 10 lat temu. Mój kolega z liceum wpadł wówczas na pomysł, że dość zabawną grą mogłaby być strzelanka, w której świat jest 4-wymiarowy, ale naraz widać tylko 3 z nich. W jego wizji w każdym momencie 3 z 4 współrzędnych byłyby wybrane w celu określenia, która część świata ma być rysowana, a gracz mógłby w każdej chwili zmienić ten zestaw. To mogłoby prowadzić do zabawnych sytuacji, w których gracz widzi sunący na niego płaski przekrój innego gracza, który na moment staje się w pełni 3-wymiarowy (przez chwilowe "wskoczenie" w jego przestrzeń), zabija go i znowu się spłaszcza. Postanowiłem wtedy stworzyć "proof-of-concept" czegoś takiego, jednak uznałem, że ciekawsze od dyskretnego przełączania zbiorów współrzędnych będzie obracanie widocznego przekroju w sposób ciągły.

(więcej…)

Ułatwianie zabawy Ithkuilem

Ostatnie dni poświęciłem ulepszaniu dawno stworzonego narzędzia, mającego na celu ułatwienie ludziom bawienia się Ithkuilem. Ale po kolei.

Ithkuil

Ithkuil to sztuczny język, którego autorem jest John Quijada. Sztuczne języki kojarzą się zwykle z zabawami dzieci (sam tworzyłem swoje języki w wieku ok. 10-12 lat), jednak w tym przypadku trudno być dalej od prawdy. Jakkolwiek trzeba przyznać, że trudno znaleźć dla Ithkuila jakieś praktyczne zastosowanie, jest to w moim odczuciu język niezwykle interesujący.

W Ithkuilu nacisk kładziony jest na przekazanie jak największej ilości informacji w jak najkrótszej wypowiedzi. W efekcie język ten posiada 45 spółgłosek i 13 samogłosek i niemalże każda głoska w słowie niesie osobny fragment informacji. Jak to zostało osiągnięte?

Ithkuil posiada dwie główne klasy słów - ang. formatives i adjuncts. Formatives to słowa pełniące rolę rzeczowników lub czasowników, adjuncts natomiast pełnią rolę dodatkowych określeń oraz zaimków osobowych. Skupmy się na formatives. Każde z nich składa się z rdzenia, niosącego główne znaczenie (np. "dźwięk wydawany ustami"), który następnie jest "odmieniany" przez ponad 20 różnych kategorii gramatycznych za pomocą doklejanych przyrostków i przedrostków. Np. rdzeń "dźwięk wydawany ustami" (-l-) możemy odmienić, dopisując z przodu "e-" -> "el-", otrzymując "wypowiedź". Aby otrzymać najmniejsze możliwe słowo, musimy dokleić jeszcze co najmniej jedną samogłoskę i spółgłoskę -> "elal". "a" określa przypadek "oblique", będący przypadkiem neutralnym, nie niosącym żadnej szczególnej informacji. Końcówka "-l" z kolei określa, że mamy na myśli pojedynczy obiekt, funkcjonujący osobno, rozpatrujemy go w całości i chodzi nam o sam obiekt, a nie np. jego wyobrażenie. W ten sposób "elal" oznacza właśnie "wypowiedź".

(więcej…)

Geometria różniczkowa w Ruście

W ciągu ostatnich paru tygodni pracowałem nieco nad biblioteką, która pozwalałaby na prowadzenie obliczeń w zakresie geometrii różniczkowej w Ruście. Przez geometrię różniczkową mam tu na myśli głównie rachunek tensorowy, tylko w krzywych przestrzeniach bądź czasoprzestrzeniach. Coś tego typu stworzyłem już swego czasu w C++, postanowiłem jednak spróbować wykorzystać niektóre możliwości Rusta do stworzenia ulepszonej wersji w tym języku.

Co Rust mógłby zrobić lepiej?

Tensory do celów obliczeń najwygodniej zapisywać jako tablice liczb. Problem w tym, że sprowadzenie tensora do liczb wymaga wybrania układu współrzędnych w przestrzeni, w której ten tensor jest zdefiniowany. Różne operacje, np. dodawanie dwóch tensorów, mają sens jedynie wtedy, kiedy tensory biorące w niej udział są zdefiniowane w tym samym układzie współrzędnych. Jedyną możliwością zakodowania tej zasady w C++ było zakodowanie układu współrzędnych jako właściwości obiektu tensora i sprawdzanie w kodzie różnych operatorów, czy wszystko się zgadza. W ten sposób, jeśli zrobimy w kodzie jakiś błąd, program wychwyci go w trakcie wykonania.

Ok, czyli błędy były wykrywalne, co tu można zrobić lepiej? Ano można byłoby zrobić tak, żeby tensory zapisane w różnych układach współrzędnych miały nie tylko różną wartość jednej właściwości, ale były wręcz obiektami różnych typów. W ten sposób o błędzie może nas powiadomić jeszcze kompilator, zanim program w ogóle zostanie przekształcony w postać wykonywalną. W C++ nie byłoby to praktyczne, natomiast system typów Rusta pozwala to zrobić o wiele ciekawiej.
EDIT: Zwrócono mi uwagę, że szablony w C++ też pozwalają na coś takiego. Tym niemniej, zrobienie tego w Ruście było ciekawym eksperymentem :)

(więcej…)

Wersja w Ruście dogoniła wersję w C

Trochę to zajęło, ale Rustowa wersja kodu generującego położenia galaktyk w końcu osiągnęła poziom funkcjonalności wersji w C. Przy okazji zebrałem sporo ciekawych doświadczeń, którymi się teraz podzielę.

Rust vs inne języki

Programowanie w Ruście zupełnie nie przypomina programowania w innych językach, z którymi do tej pory miałem styczność (czyli zasadniczo rodzina C i Python). Jedyne, co trochę je przypominało, to eksperymentowanie z Haskellem, ale to głównie pod względem kompletnego niedopasowania mojej intuicji do języka (choć Rust ma sporo elementów funkcyjnych, ale jak będzie wspomniane później, nie należy z nimi przesadzać...).

(więcej…)

Postępy w projekcie Universe

W ciągu ostatnich kilku dni poczyniłem trochę postępów w przepisywaniu projektu Universe w języku Rust.

Przed zabraniem się za właściwy projekt musiałem przygotować pod niego trochę gruntu. Ze względu na wielkość liczb pojawiających się w programie, korzystałem z bibliotek GMP i MPFR, pozwalających na wykonywanie obliczeń na dowolnie dużych liczbach. Oprócz tego, korzystałem z algorytmu xxhash. Problem? Wszystkie 3 elementy były napisane w języku C.

Problem nie był szczególnie trudny do pokonania, gdyż jedną z zalet Rusta jest łatwość łączenia go z bibliotekami napisanymi w C. Konieczne było jedynie znalezienie lub stworzenie modułów, które ułatwią takie połączenie. Podjąłem więc poszukiwania.

Najpierw skupiłem się na GMP i MPFR. Do biblioteki GMP znalazłem 2 moduły, do MPFR - jeden (stworzony przez autora jednego z modułów do GMP). Po przyjrzeniu się im, uznałem, że łatwiejszy w użyciu będzie moduł GMP nie mający odpowiednika do MPFR, co zostawiło mnie z zadaniem napisania własnego. Ponadto moduł GMP był stary i nie kompilował się w najnowszej wersji języka.

Zabrałem się więc do pracy. Naprawienie modułu GMP okazało się żmudne, ale nie przedstawiające sobą większych trudności. Stworzenie modułu do MPFR podobnie, choć tutaj żmudności było jeszcze więcej (pisanie wielu podobnych funkcji, których jedynym zadaniem jest wywołanie ich odpowiedników z C...). Oba moduły są na GitHubie (klik, klik). Poprawioną bibliotekę GMP podesłałem autorowi oryginału, jednak okazał się on nie być zainteresowany dalszym utrzymywaniem jej kodu.

Potem przyszła kolej na xxhash. Tu, podobnie jak w przypadku GMP, moduł istniał, lecz się nie kompilował. Chwila pracy pozwoliła poprawić błędy i otrzymać działającą wersję biblioteki.

Mam w tej chwili zatem działające moduły, od których projekt jest zależny. Zacząłem również przepisywać kod samego projektu, jednak na razie gotowy jest tylko niewielki jego fragment. Wiadomości o dalszych postępach - (oby) wkrótce.

Przemyślenia dotyczące zakrzywiania światła

Doznałem wczoraj oświecenia jak usunąć artefakt z symulatora czarnej dziury.

Problem

artifact

W obecnym symulatorze czarnej dziury istnieje jeden brzydki aspekt generowanego obrazu. Dokładnie 90 stopni od kierunku do czarnej dziury powstaje artefakt graficzny - rozmazany pasek błędnie obliczanych pikseli (patrz obrazek po prawej). Powód tkwi zaszyty głęboko w mechanizmie generowania obrazu.

W skrócie wygląda to tak - nie da się w rozsądnym czasie obliczyć koloru każdego piksela przez raytracing. Korzystając zatem z symetrii czarnej dziury Schwarzschilda, stworzyłem sobie gotową tabelę zakrzywień promieni. Dzięki symetrii, każdy promień mogę opisać jednym parametrem - w uproszczeniu, tym, w jakiej odległości mija on czarną dziurę w najbliższym punkcie toru (dokładniej: parametrem zderzenia). Do odchylenia potrzebuję jeszcze drugiego parametru, a mianowicie odległości od czarnej dziury, z której go wysyłam/odbieram - im większą część pełnej trasy promień ma do przebycia, tym większe odchylenie.

Tak stabelaryzowane dane trafiały do karty graficznej. Później, w trakcie rysowania, każdy piksel był przeliczany na kierunek wysyłania promienia, a ten na parametr zderzenia. Odległość była znana niezależnie od tego. Odpowiednie odchylenie było odczytywane z tabeli i właściwy kolor nadawany pikselowi.

W teorii wszystko jest fajnie, ale pojawił się jeden problem - parametry zderzenia promieni wysłanych w różne kierunki bliskie 90 stopni od czarnej dziury są prawie identyczne. To daje niemal identyczne wartości odchylenia, co przekłada się na ten sam kolor piksela. Powstaje brzydki pasek.

(więcej…)