Leniwce.com | blog technologiczny
Wszechświat na ekranie, czyli słowo o skalowaniu
Kuba Krakowski, 2009-07-15 16:09:10
kategoria: C#, Inne

Chyba prędzej czy później każdy, kto choć trochę programuje ma ochotę/musi/powinien narysować coś na ekranie: może to być wykres funkcji matematycznej, czy też jakieś dane do zobrazowania. Siadamy do komputera, zaczynamy pisać kod, dochodzimy do fragmentu, w którym należy wskazać współrzędne punktu do postawienia na formie naszego projektu i ... zatrzymujemy się.

 

W tym właśnie momencie uświadamiamy sobie, że mamy kilka problemów do rozwiązania:

  •     nasza funkcja (np. sinus) przyjmuje wartości od -1 to 1, natomiast współrzędne ekranowe są tylko nieujemne;
  •     w świecie rzeczywistym najczęściej operujemy na układach współrzędnych, w ktorych oś OY jest skierowana ‘do góry’, czyli dokładnie odwrotnie niż to jest na naszym ekranie;
  •     zakres wartości danych do zwizualizowania jest w zakresie np. [2450,9500], co jest niemożliwe do bezpośredniego odwzorowania na ekranie.


Wszystkie te kwestie sprowadzają się do pytania : jak zmieścić obszar matematyczny na obszarze fizycznym naszego ekranu?  Domyślamy się, że trzeba jakoś ‘przetłumaczyć’ współrzędne ze świata naszej funkcji na ekranowe. Często spotykam w źródłach, że autor mnoży współrzędne przez jakąś liczbę, a potem cos jeszcze dodaje i nie wiadomo, dlaczego akurat takie liczby dobrał . W prostych przypadkach można je ustalić metodą prób i błędów. Ja chciałem natomiast mieć rozwiązanie uniwersalne – sprawdzające się w możliwie największej liczbie przypadków i wydaje się,  że cel zostal osiągnięty.

Kilka słów niezbędnej teorii


Jeśli chcemy przeskalować przedzial X=[x1,x2] na X’= [x3,x4], to musimy ten pierwszy rozciągnąć/ścisnąć  i/lub przesunąć w lewo/prawo – ujmuje to wzór:  x’= A x+ B, gdzie współczynnik A wyraża zmianę ‘szerokości’ przedziału, a B – przesunięcie.

Współczynniki A i B znajdziemy z dwóch naturalnych warunków:

Lewy koniec (x1) przedziału X ma przejść na lewy koniec (x3) przedzialu X’ i analogicznie prawe końce.

Zapisując ogólną zależność x’=Ax+B  dla tych szczególnych przypadków dostajemy układ równań:

 
x3=A x1+B
x4=A x2+B
 

którego rozwiązaniem jest

 
A= -(x4-x3)/(x1-x2)
B= -(x2x3-x1x4)/(x1-x2)
 

Mam nadzieję, że teraz będzie jasne nasze postępowanie w przypadku 2-wymiarowym.  Przekształcamy parę punktów (xr,yr) na (xs,ys) zgodnie ze wzorem:

 
 
xs=A xr+B
ys=C yr+D
 

Podobnie jak poprzednio – A,B,C i D wyliczamy z  żądań, by odpowiednie boki obszarów przechodziły na siebie:


- lewy bok przestrzeni matematycznej na lewy przestrzeni ekranowej:

 
xs0=A xrmin+B
 

-prawy bok na prawy bok:

 
xs0+swidth=A xrmax+B
 

-górna krawędź na górną krawędź:

 
ys0=C yrmax+D
 

-dolna krawędź na dolną:

 
ys0+sheight=C yrmin+D
 

Rozwiązując układ tych 4 równań (leniwce mogą użyć darmowego oprogramowania w rodzaju wxMaxima czy też komercyjnego Mathematica) otrzymujemy:

 
A=  swidth/(-xrmax+xrmin)
B= -(swidth xrmin-xrmax xs0+xrmin xs0)/(xrmax-xrmin)
C= -sheight/(yrmax-yrmin)
D=  (sheight yrmax)/(yrmax-yrmin)
 

Analogicznie obliczamy współczynniki przy przechodzeniu z przestrzeni ekranu do przestrzeni matematycznej:

 
xr=E xs+F
yr=G ys+H
 

otrzymując:

 
E = -((-xrmax + xrmin) / swidth);
F = -((-swidth * xrmin + xrmax * xs0 - xrmin * xs0) / swidth);
G = -((yrmax - yrmin) / sheight);
H = -((-sheight * yrmax - yrmax * ys0 + yrmin * ys0) / sheight);
 

Implementacja

Pora teraz odnieść jakieś praktyczne korzyści z naszych obliczeń. Napisałem małą klasę Scaling, która ma za zadanie realizować opisane wyżej przekształcenia ekran<->przesteń matematyczna.
Użycie jej we własnym projekcie wymaga dodania odpowiedniej referencji.

W konstruktorze:

 
Scaling(double xrmin, double xrmax, double yrmin, double yrmax,int xs0, int ys0, int swidth, int sheight)
 

ustalamy obszary:

  • rzeczywisty - określony przez zakres współrzędnych x: [xrmin,xrmax] oraz y: [yrmin,yrmax]
  • ekranowy - określony przez lewy górny róg (xs0,ys0) oraz szerokość swidth i wysokość sheight

Metody:

 
int getScreenX(double xr)
int getScreenY(double yr)
 

zwracają x-ową i y-ową współrzędną ekranową odpowiadającą matematycznym xr oraz yr.

 
double getRealX(int xs)
double getRealY(int ys)
 

robią dokładnie odwrotną operację - zwracają wartości z przestrzeni matematycznej odpowiadające współrzędnym ekranowym xs i ys

Ponieważ polecenia graficzne .NET często wymagają podania rozpiętości poziomej i pionowej jakiegoś obiektu (np. elipsy) - pomocne mogą być metody:

 
int getScreenDX(double x0, double x1)
int getScreenDY(double y0, double y1)
 

które zwracają długości ekranowe odpowiadające długościom rzeczywistym.

Na koniec, żeby ewentualnym niedowiarkom udowodnić działanie tego wszystkiego, zrzut ekranu z aplikacji, której kod źródłowy można ściągnąc stąd.

Powiązane artykuły
Atraktor Lorenza (2011-08-29)
O związku motyli z pogodą... (2011-08-26)
Równoległy może więcej(?) - czyli kilka słów o Parallel.For (2011-02-06)
Święta, święta - czas pochwalić się (fraktalną) choinką (2010-12-25)
Własny wygaszacz ekranu (2010-06-03)
3.1415926535897932385... (2010-05-19)
Metody rozszerzające (2010-05-07)
GPS w lekkostrawnym sosie podany (2009-08-05)
OleDbConnection – Excel jako baza danych(C#) (2009-07-13)
Animacje 3D (OpenGL) (2009-07-05)


Komentarze


PawelB [2009-08-12 14:27:41]
Pozdrowienia dla autora. Gratuluję pasji i wiedzy. Liczymy na kolejne dogłębnie analizowane problemy :-)

Dodaj komentarz:
Autor:*

WWW:

Treść:*

Wprowadź kod zabezpieczający*:


        * - pola wymagane
Kategorie
C# (13)
Inne (6)
Java (3)
Matlab (1)
OpenGL (1)
PHP (2)


Najnowsze wpisy

Ostatnie komentarze