Krzywa HILBERTa

Kod źródłowy: mixyh.c

Zapełnianie kwadratu krzywą łamaną

Najlepiej zobaczyć pierwsze kroki rekurencyjnej konstrukcji. Celem jest konstrukcja nierekurencyjnego algorytmu pozwalającego przekształcać numer załamania na krzywej na parę liczb określających jego położenie w dwóch wymiarach.


0 == lbn  
    \x 0
    y+--+
    0| 0|
     +--+

Przypadek 0 == lbn jest trywialny i nie wnosi wiele.


1 == lbn
    \x 0 1
    y+-----+
    0| 0-1 |
     |   | |
    1| 3-2 |
     +-----+

Przypadek 1 == lbn prezentuje podstawowy schemat pokrywania powierzchni krzywą. Możemy z niego między innymi zobaczyć, że fragment numer 3 krzywej znajmuje miejsce o  współrzędnych (x,y)[3]==(0,1).


2 == lbn
    \x  0  1  2  3
    y+-------------+
    0|  0  3--4--5 |
     |  |  |     | |
    1|  1--2  7--6 |
     |        |    |
    2| 14-13  8--9 |
     |  |  |     | |
    3| 15 12-11-10 |
     +-------------+

Przypadek 2 == lbn jest już bardzo pouczający. Widać z niego w jaki sposób następuje wypełnianie przestrzeni dwuwymiarowej przy zwiększeniu rozdzielczości. Dwukrotne zwiększenie rozdzielczości wymaga czterokrotnego zwiększenia długości krzywej pokrywającej powierzchnię kwadratu.

Weźmy jakikolwiek fragment krzywej, na przykład numer 12. Jego współrzędne wynoszą: (x,y)[12]=(1,3). Zapiszmy to używając dwójkowej reprezentacji liczb: (x,y)[1100]=(01,11).

Przygądnijmy się jeszcze jednemu, bardziej złożonemu przypadkowi, aby pojąć występujące to regularności:


3 == lbn
    \x  0  1  2  3  4  5  6  7
    y+-------------------------+
    0|  0--1 14-15-16 19-20-21 |
     |     |  |     |  |     | |
    1|  3--2 13-12 17-18 23-22 |
     |  |        |       |     |
    2|  4  7--8 11 30-29 24-25 |
     |  |  |  |  |  |  |     | |
    3|  5--6  9-10 31 28-27-26 |
     |              |          |
    4| 58-57 54-53 32 35-36-37 |
     |  |  |  |  |  |  |     | |
    5| 59 56-55 52 33-34 39-38 |
     |  |        |        |    |
    6| 60-61 50-51 46-45 40-41 |
     |     |  |     |  |     | |
    7| 63-62 49-48-47 44-43-42 |
     +-------------------------+

Tym razem weźmy fragment 49. Jego wsółrzędne wynoszą: (x,y)[49]==(2,7), w reprezentacji dwójkowej jest to: (x,y)[110001]==(010,111).

Fragment w większej rozdzielczościach był wybierany w tej samej ćwiartce (następnie szestnastce). Numer takiego fragmenty dziedziczy bardziej znaczące bity dwójkowej reprezentacji. Tylko dwa najmniej znaczące bity określają położenie fragmenty w  obszarze najdrobniejszej rozdzielczosci. To samo odnosi się do współrzednych fragmentu, tu tylko jeden najmniej znaczący bit każdej ze wsółrzędnych nie jest dziedziczony z mniejszej rozdzielczości.

Zatem, aby obliczyć wsółrzędne zadanego fragmentu krzywej o długości 2^(2*lbn), trzeba jego numer w reprezentacji dwójkowej podzielić na lbn dwubitowych części. Każda dwubitowa część opisuje położenie w ćwiartce na odpowiednim poziomie rozdzielczości.

Zadanie to realizuje ten fragment kodu:


	while(i < lbn) bt[i++] = (int)count & 0x03, count >>=2;

Dochodzimy tu do najtrudniejszej i najistotniejszej części algorytmu. Znaczwenie dwubitowej części (czyli położenie w ćwiartce) zalezy od położenia w mniejszej skali (czyli wartości bardziej znaczących części dwubitowych). Ta zależność jest zakodowana w dwóch tablicach. Tablica hilb opisuje cztery orientację schematu pokrywania.


	static short hilb[4][4] = {
		{ 0, 1, 3, 2 },
		{ 0, 2, 3, 1 },
		{ 3, 1, 0, 2 },
		{ 3, 2, 0, 1 }
	};

To znaczy:


	hilb[0]     hilb[1]     hilb[2]     hilb[3]

	0[0]--1[1]  0[0]  3[1]  2[0]--1[1]  2[0]--3[1] 
	      |     |     |     |     |     |
	3[2]--2[3]  1[2]--2[3]  3[2]  0[3]  1[2]--0[3]

Tablica turn opisuje zmianę orientacji ćwiartki względem orientacji zawierającej ją ćwiartki:


	static short turn[4] = { 1, 0, 0, 2 };

Liczby tablicy turn są indeksami wierszy tablicy hilb. Mówią, że przy 2==lbn orentacje komórki podstawowej są takie, jak opisują { turn[1], turn[0], turn[0], turn[2] }. Odtwarzanie orientacji realizuje ta linia kodu:


		short bb = bt[i];
		bb = bb[hilb[kk ^= turn[bb]]];

Wreszcie rozseparowanie bitów do współrzędnych:


		x = (x << 1) | (bb & 1);
		y = (y << 1) | (bb >> 1);


(C) Aleksander Nabagło