Autor Wątek:  Opis formatu binarnego dla edytora scenerii - RSF  (Przeczytany 4338 razy)

0 użytkowników i 1 Gość przegląda ten wątek.

Offline Ra

  • Zasłużony dla Symulatora
  • Wiadomości: 6345
  • Ostatni gasi światło...
    • Zobacz profil
    • Instalator+Starter+Edytor
  • Otrzymane polubienia: 377
Opis formatu binarnego dla edytora scenerii - RSF
« dnia: 21 Stycznia 2009, 18:20:09 »
Będę tu opisywał szczegóły dotyczące zapisu formatu, nad którym aktualnie pracuję. Wątek będzie zamknięty i będę edytował wiadomości w przypadku zmian. Wszelkie uwagi dotyczące formatu proszę pisać poprzez PW albo w innych wątkach, np:
Profil pionowy trasy.
Ortofotomapa z geoserwer.pl jako teren
Dane wektorowe UMP
Kolejowa Mapa Polski

Format ma roboczą nazwę i rozszerzenie pliku RSF. Skrót oznacza "redundancyjny format scenerii". Związane jest to z tym, że informacje o obiektach zapisywane są z wielorakim wzajemnym powiązaniem. Format i jego opis jest bezpłatny do wszelkich zastosowań, a jego użycie we własnych programach jest dozwolone bez ograniczeń. Będzie również udostępniona biblioteka z klasami i głównymi operacjami.

W scenerii zapisanej w plikach MAX oraz SCM obiekty jedynie "przypadkiem" są w odpowiednich miejscach i nie ma "oczywistych" narzędzi, które sprawdzały by np. styczność torów w miejscu ich połączenia, współrzędną pionową semafora obok toru i jego odległość od osi, połączenie sąsiednich trójkątów terenu w jednym punkcie, albo rozmiary rozjazdu. Jeśli się wstawi źle, to będzie źle, a najprostszym sposobem poprawienia jest najczęściej usunięcie i ponowne wstawienie z użyciem szablonu - po wcześniejszym wypatrzeniu błędu. W formacie redundancyjnym RSF można zweryfikować zależności pomiędzy obiektami i poprawiać położenie obiektu tak, aby zostały one spełnione. Poprawić można na kilka sposobów, więc nie da się tego zrobić automatycznie (np. tor może być połączony z sąsiednimi, ale nie być równoległy do linii kierunkowej, albo być równoległy do linii kierunkowej, ale nie mieć wspólnych punktów z sąsiednimi, mimo formalnego połączenia ich w ciąg - metodę naprawienia takiego stanu trzeba dobierać indywidualnie w każdym przypadku).

Plik z zapisem scenerii jest zorganizowany w tabelę, o rekordach długości 128 bajtów. W każdym rekordzie jest informacja o jego typie (rodzaju obiektu). Rekordy mogą być łączone w grupy (np. zwrotnice zapisywane są zawsze w postaci 2 kolejnych rekordów). Rekordy mogą zawierać wskaźniki na inne rekordy, w postaci adresu liczonego względem początku pliku. Ponieważ długość rekordu jest stała, bity 0..6 wskaźnika mogą być używane do innych celów - np. w przypadku torów umieszczona jest informacja o tym, do którego końca sąsiedniego toru podłączony jest dany koniec toru (0 - początek, 1 - koniec, 3 - tor odchylony zwrotnicy). Przy usuwaniu obiektu, jego  rekord w tabeli zostaje oznaczony jako pusty i może być wykorzystany w przyszłości przez nowo dodany obiekt.

Pierwszy rekord pliku jest zarezerwowany na informacje o pliku. Zapisane są w nim współrzędne środka w PUWG 1992, służące do pobierania odpowiednich podkładów z Geoportalu, a także ostatnio ustawiona pozycja w edytorze i zbliżenie. Jednocześnie wskaźnik na ten rekord (liczba 0) oznacza brak połączenia z innym obiektem.

Współrzędne obiektów zapisywane są jako liczby stałoprzecinkowe, z dokładnością 1/8192m. Pozwala to ogarnąć kwadrat o boku 524km (1/4 PUWG 1992) z dokładnością prawie 0.1mm), czego nie dało by się zrobić czterobajtowym typem zmiennoprzecinkowym. Opcjonalnie można przeliczyć współrzędne na zmiennoprzecinkowe, ale wtedy traci się na dokładności (błąd rzędu 1mm w odległości 8km od osi układu współrzędnych - używane są 23 bity, a nie 31). Zapis stałoprzecinkowy umożliwia również używanie operacji przesunięcia bitowego >> w celu uzyskania współrzędnych ekranowych dla rozdzielczości 1000px/km i pochodnych. Współrzędna X narasta na wschód, Y na północ, a Z w górę.

Ciekawą cechą plików binarnych RSF jest możliwość ich łączenia poprzez zwykłe doklejenie pliku. Wymagane jest tylko, aby miały ten sam środek układu współrzędnych. Np. do pliku z torami da się dokleić pliki zawierające opisy semaforów, czy trójkątów terenu.


Rekordy w pliku zawierają zarówno pełną informację o obiektach używanych w symulacji, jak również obiekty pomocnicze, służące do weryfikowania zależności.

Podstawowymi obiektami pomocniczymi są linie kierunkowe. Są to linie proste na płaszczyźnie poziomej XY (czyli w planie), określone poprzez wektor wychodzący ze środka OXY układu współrzędnych i prostopadły do prostej (zobacz równanie normalne prostej). Przy czym kąt nachylenia wektora do osi OX zawiera się w przedziale <0°, 180°), a jego długość jest odległością prostej od OXY i może być ujemna. (Wariant z pełnym kątem i dodatnią długością wektora mógłby powodować pojawienie się lustrzanego odbicia torów po przemieszczeniu prostej kierunkowej na drugą stronę punktu OXY.) Każdy odcinek (tor, droga, linia lasu, zabudowy) powinien być równoległy do jakiejś linii kierunkowej. Odcinki nieuzależnione od linii kierunkowej mogą się nieznacznie przesuwać podczas przeliczania. Linie kierunkowe definiuje się poprzez:
 - specjalne odcinki linii kierunkowych, umieszczone równolegle do możliwie długich obiektów widocznych na zdjęciach lotniczych (linie kierunkowe główne) - linia prosta zawiera odcinek,
 - zwrotnice ustawione torem prostym równolegle do jakiejś linii kierunkowej (linie kierunkowe wtórne) - linia prosta jest odchylona o kąt określony przez zwrotnicę - najczęściej atan(1:9)=6.34° - i przechodząca przez koniec toru odchylonego,
 - odcinki samo-kierunkowe, mające zablokowany kąt oraz odległość od początku układu współrzędnych - linia zawiera odcinek.



W załączniku jest fragment stacji Bór Dolny. Główna linia kierunkowa przechodzi przez całą stację "na wylot" (parametry wektora prostopadłego: 112.96°, 249.4m). Według niej są poustawiane wszystkie tory równoległe, widoczne po lewej stronie. Za pomocą zaznaczonej na niebiesko zwrotnicy wyznaczona jest wtórna linia kierunkowa (106.62°=112.96°-6.34°, 733.7m) - zaznaczone na żółto tory są umieszczone na tej linii kierunkowej. Tor zaznaczony na zielono jest również równoległy do kierunkowej wyznaczonej przez niebieską zwrotnicę (wskazuje na to połączenie od niebieskiego kwadratu do zwrotnicy) - w tym przypadku jest umieszczony w odległości 5.85m od linii kierunkowej. Zwrotnica na prawo od zielonego toru jest wpasowana w przecięcie głównej linii kierunkowej, na której się znajduje z linią wyznaczającą oś zielonego toru (ale również można by ją ustawić jako wtórną kierunkową i wtedy uzależnić zielony tor od niej). Na lewo od zielonego toru znajduje się łuk o promieniu 300m, a za nim jest tor, którego kąta nie dało się wyznaczyć za pomocą którejś ze zwrotnic. Dlatego jest umieszczona pod nim druga główna linia kierunkowa dla tej stacji (104.05°, 931.4m). Różnica kątów między kierunkowymi wynosi 8.91° - nie da się tego załatwić zwrotnicą. Odcinek ten mógłby być ustawiony również jako samo-kierunkowy, ale samo-kierunkowe zostały wprowadzone później.
« Ostatnia zmiana: 31 Stycznia 2009, 23:19:53 wysłana przez Ra »
¯\_( ͡° ͜ʖ ͡°)_/¯ Ra

Polecam: kręgarz Wojciech Walczak, projekt masarni

Offline Ra

  • Zasłużony dla Symulatora
  • Wiadomości: 6345
  • Ostatni gasi światło...
    • Zobacz profil
    • Instalator+Starter+Edytor
  • Otrzymane polubienia: 377
Typy bazowe formatu
« Odpowiedź #1 dnia: 03 Listopada 2009, 00:01:08 »
typedef unsigned __int8 BinByte; //0..255
typedef signed __int8 BinTiny; //-128..127
typedef unsigned short int BinWord; //0..65535
typedef signed short int BinShort; //-32768..32768
typedef unsigned __int32 BinPtr; //wskaźnik rekordu
typedef float BinFloat; //cztery bajty

struct BinPointHV
{//współrzędne względne punktów w [mm] do 32m od środka
 BinShort H;
 BinShort V;
};

class BinPointTHVA
{//współrzędne względne w lokalnym układzie
 BinShort H; //rośnie na prawo
 BinShort V; //rośnie w górę
 BinShort T; //rośnie do przodu, -32768..+32767mm
 BinShort A; //dodatkowy kąt obrotu w lewo (0..3600) oraz mnożnik 1..16
};

class BinPoint3
{//punkt w przestrzeni (kartezjańskiej lub krzywoliniowej)
 //Jednostka [1/8192m], co daje maksymalnie 262km od punktu zerowego.
public:
 int X,Y,Z;
 //metody tymczasowo nie są wyszczególnione
};

class Bin4Points
{//cztery punkty w przestrzeni 3D
public:
 BinPoint3 P[4];
 void __fastcall BezierSplit(Bin4Points *p);
};

Nazwy typów i klas formatu RSF zaczynają się od przedrostka Bin, oznaczającego scenerię zapisaną binarnie. Bazowe typy muszą mieć określoną ilość bajtów i kolejność bajtów w liczbach wielobajtowych.

Typy jednobajtowe: BinByte, BinTiny są używane do zapisu drobnych parametrów, takich jak skrajnia niwelety (jednostka: 1dm - od 0 do 25.5m), przechyłka (jednostka: 0.1° - od -12.8° do 12.7°), maksymalna prędkość (od 0 do 160km/h co 1km/h, potem co 5km/h aż do 630km/h) jak również innych liczb, których zakres zmienności nie przekroczy 8 bitów. Ponadto standardowy typ char służy do zapisu nazw obiektów.

Typy dwubajtowe: BinWord, BinShort są najczęściej używane do podania wymiaru obiektu w [mm], albo jego pozycji względnej. BinWord również określa typ obiektu.

Typy czterobajtowe: BinPtr, BinFloat, BinPointHV mają różne zastosowanie. BinPtr jest względnym wskaźnikiem na inny rekord, przy czym bity 0..6 mogą być używane do innych celów, najczęściej na wskazanie kierunku na liście dwukierunkowej oraz numeru kolejnego rekordu w obiekcie złożonym z kilku rekordów. BinFloat służy głównie do zapamiętania kąta, pod jakim obiekt jest ustawiony, może być użyty dla wartości zmiennoprzecinkowych. BinPointHV to względne współrzędne obiektu [mm]: pozioma (H) i pionowa (V). Ponadto standardowy int jest na ogół używany do zapisu współrzędnych obiektu w przestrzeni, numerowania obiektów i jako współrzędna wzdłużna na niwelecie.

Typ ośmiobajtowy BinPointTHVA służy do zapamiętania względnych współrzędnych obiektu [mm]: pozioma (H), pionowa (V), wzdłużna (T), kąt obrotu (A).

Typ dwunastobajtowy BinPoint3 używany jest do określania pozycji obiektu w przestrzeni 3D (obszarze edycyjnym o boku 520km).

Typ czterdziestoośmiobajtowy Bin4Points służy do operacji na krzywych Béziera, aktualnie do podziału krzywej na pół.

Wszystkie rekordy obiektów scenerii są dziedzicznymi struktury bazowej BinItem:
class BinManager;
class BinRecord;

class BinItem
{//ogólnie obiekt w rekordzie - długość 0x14
friend class BinManager;
protected:
 static BinManager *Manager;
public:
 BinByte Flags; //flagi rekordu: b0,b1=grupa,b6=1-Id jest kilometrażem
 BinByte Len; //długość użytkowa (początek komentarza) (129..255 - komentarz)
 union
 {//struktura dla typu rekordu
  BinWord Type; //rodzaj rekordu
  struct
  {BinByte TypeLo;
   BinByte TypeHi; //aby łatwiej porównywać
  };
 };
 BinPtr Path; //wskażnik na rekord właściciela
 int Id; //identyfikator w ramach listy; indeks kilometrażu [mm]
 BinPtr Prev,Next; //wskaźnik na następny i poprzedni
 void __fastcall Disconnect(int what=-1);
 bool __fastcall Join(int where,BinRecord *r,BinPtr what=0);
 bool __fastcall Join(int where,BinPtr what);
 BinRecord* __fastcall RecordPrev();
 BinRecord* __fastcall RecordNext();
 BinRecord* __fastcall Neighbour(bool &dir);
 BinRecord* __fastcall RecordPrev(BinWord t,BinWord m=0xFFFF);
 BinRecord* __fastcall RecordNext(BinWord t,BinWord m=0xFFFF);
 BinRecord* __fastcall RecordAddress(int p); //bezwzględny adres rekordu
 BinRecord* __fastcall RecordZero();
 BinPtr __fastcall RecordRelative();
 void __fastcall GroupSet(int g);
 int __fastcall GroupGet();
 bool __fastcall Sort(BinRecord *r);
 int __fastcall Count();
 void __fastcall Clear();
 void __fastcall SizeCheck(TRect *r=NULL);
 int __fastcall FileType(int p=0);
};
BinManager* BinItem::manager=NULL;

Obiekt Manager klasy BinManager odpowiada za wczytanie, zapis i obsługę pliku RSF umieszonego w pamięci (w tym dodawanie nowych rekordów). Ze względu na sposób jego podłączenia do klasy obiektu scenerii, nie jest możliwe jednoczesne korzystanie z dwóch plików RSF (a przynajmniej wymaga to zmiany wskaźnika na obiekt zarządzający).

Klasa BinRecord pozwala na dostęp do różnych typów obiektów, gdy mamy zwykły wskaźnik do obiektu, zwykle używana w postaci wskaźnika BinRecord*. Ze względu na zapis obiektów w postaci tabeli BinRecord[] nie jest możliwe użycie funkcji wirtualnych.

Zmienna Flags służy do zapisu ogólnych informacji o obiekcie. Aktualnie wykorzystywane są bity 0 i 1 do zapamiętania przydziału obiektu do grupy (0-poza grupą, 1-ogranicznik rozlania grupy, 2-w grupie, 3-nie używane) oraz bit 6 do zmiany priorytetu zmiennej Id. Ustawiony bit 7 ma oznaczać, że obiekt zajmuje dwa rekordy, ale nie jest to używane (obiekty złożone z kilku rekordów, np. zwrotnice, funkcjonują na innej zasadzie).

Zmienna Len nie jest aktualnie używana, jej przeznaczeniem jest bycie względnym wskaźnikiem na początek komentarza do obiektu.

Zmienne Type, TypeLo, TypeHi określają typ obiektu, unia jest użyta głównie w celu uproszczenia operacji sprawdzania typu obiektu.

Wskaźnik Path wskazuje na obiekt nadrzędny do danego, np. dla torów nadrzędne są niwelety, dla niwelet przekroje poprzeczne, a dla przekrojów również niwelety. Dla semaforów i słupów nadrzędne są tory, a dla budynków mogą być to również niwelety.

Zmienna Id na ogół określa pozycję (indeks kilometrażu) obiektu na niwelecie. Jeśli bit 6 w zmiennej Flags został ustawiony, podana wartość Id jest ważniejsza niż wartość uzyskana z wyliczenia aktualnego rzutu prostopadłego obiektu na niweletę. Wartość ujemna Id oznacza na ogół obrócienie obiektu o 180° (obiekt jest wtedy tyłem do wzrostu kilometrażu).

Wskaźniki Prev oraz Next wskazują na sąsiednie obiekty tego samego typu, umieszczone na liście dwukierunkowej. Służą one między innymi do logicznego łączenia torów, w przypadku obiektu słupa istnienie Next jest równoznaczne z połączeniem słupów drutem.

Metody klasy BinItem: Disconnect(), Join() służą do rozłączania i logicznego łączenia obiektów, z kolei RecordPrev(), RecordNext(), Neighbour() służą do uzyskania wskaźników na sąsiednie rekordy. Metoda Sort() wstawia obiekt na odpowiednie miejsce do listy dwukierunkowej (np. służy do sortowania przekrojów poprzecznych oraz plików).

Metoda RecordAddress() zamienia podany wskaźnik względny BinPtr na bezwzględny BinRecord*, a metoda RecordRelative() zwraca wskaźnik względny aktualnego obiektu.

Metoda RecordZero() zwraca adres na rekord zerowy pliku (ze specyficznymi informacjami).

Metody GroupSet() i GroupGet() używane są do operacji na grupach obiektów.

Metoda Count() zwraca ilość obiektów w pliku, metoda Clear() usuwa obiekt bez uwzględniania powiązań z innymi obiektami. SizeCheck() zapisuje rozmiary scenerii do zerowego rekordu, co ma służyć ustawieniu pasków przewijania w oknie edycyjnym.

Metoda FileType() służy do określania rodzaju plików powiązanych z obiektem. Na przykład dla toru będą to dwie tekstury (szyn i podsypki), dla semafora pierwszy będzie plik INC itd. Wartości są pobierane z wbudowanej tabeli własności typów, na podstawie zawartości pola Type.
¯\_( ͡° ͜ʖ ͡°)_/¯ Ra

Polecam: kręgarz Wojciech Walczak, projekt masarni