Posts from 2007

Standard i Visual C++ 2005

2007-12-14 17:40

Zainspirowany pewnym wątkiem na forum Warsztatu, traktującym o rzekomych poważnych niezgodnościach między standardem C++ a kompilatorem zawartym w MS Visual C++ 2005, postanowiłem nieco dogłębniej zbadać tę sprawę. Jak dotąd z owych niezgodności kojarzyłem tylko brak wsparcia dla szablonów eksportowanych – czyli “magicznego” słówka export, pozwalającego na rozdzielanie kodu szablonowego na pliki nagłówkowe i pliki .cpp tak samo, jak zwykłego. Nie jest to zresztą wielkie wykroczenie, jako że kompilatory wspierające szablony eksportowane można pewnie policzyć na palcach jednej ręki, a sam mechanizm też nie należy do szeroko znanych.

Co z innymi niezgodnościami? Odwołanie do dokumentacji Visual C++ przynosi ich jeszcze kilka. Z ciekawszych – oprócz braku obsługi słowa export, o którym już wspomniałem – mogę wymienić:

  • Ułomność preprocesorowego operatora # (tzw. łańcuchującego – stringizing operator), który zawiedzie, jeśli zastosujemy go względem stałej napisowej zawierającej sekwencje ucieczki (ciągi rozpoczynające się od znaku backslash: \n, \t, itd.).
  • Możliwość, że obiekty nie posiadające żadnych pól będą dzieliły miejsce w pamięci (którego i tak nie zajmują) z innymi obiektami. Standard C++ wymaga natomiast, żeby każdy obiekt miał unikalną lokalizację w pamięci operacyjnej w trakcie całego swojego istnienia (obiektu, nie standardu C++ ;]).
  • Brak wykorzystania specyfikacji wyjątków (instrukcji throw (typ_wyjątku)). Innymi słowy, w Visual C++ nie sprawdza się, czy funkcja wyrzuciła wyjątek takiego samego typu, który wcześniej został w niej zadeklarowany.
  • Pewne ograniczenia liczbowe, jak choćby: liczbę poziomów zagnieżdżenia bloków kodu (VC++ obsługuje 64, standard rekomenduje 256), kwalifikatorów dostępu dla jednej nazwy (127 zamiast 256) czy parametrów szablonu (64 przeciwko 1024).

I to mniej więcej tyle. Być może kogoś zmartwi brak możliwości napisania algorytmu ze 100 zagnieżdżonymi pętlami lub szablonu z tyloma parametrami, ale osobiście mam co do tego pewne wątpliwości :)

Całkiem proste krzywe

2007-12-13 22:27

W grafice dąży się do tego, żeby rezultat był ładny. Jest to rzecz jasna pojęcie bardzo względne, ale wiadomo na przykład, że kształty opływowe są generalnie lepiej postrzegane niż kanciate. W projektowaniu królują więc linie krzywe, a jednym z ich rodzajów są krzywe wielomianowe stopnia trzeciego, zwane też kubicznymi.

Jak ich nazwa wskazuje, są one opisane wielomianami stopnia trzeciego, czyli ich równania parametryczne zawierają współczynniki w trzeciej potędze. Taki stopień wystarcza, by krzywe te były wystarczająco gładkie (zarówno w rozumieniu matematycznych – ciągłość pochodnych – jak i zwyczajnie “na oko”), a jednocześnie niezbyt kosztowne obliczeniowo. Ponadto można dla nich relatywnie łatwo określić zależność między poszczególnymi czterema parametrami a kształtem wynikowej figury.
Krzywa BezieraTo jednak zależy od tego, jaką reprezentację przyjmiemy. Mającą najciekawsze własności i pewnie najpopularniejszą jest reprezentacja Béziera, stworzona przez pewnego włoskiego inżyniera w latach 60. ubiegłego stulecia. Mogę przypuszczać, że niemal każdy średnio zorientowany programista czy grafik słyszał o tego rodzaju krzywych – chociażby dlatego, że narzędzie Krzywa w Paincie jest właśnie krzywą Béziera ;) Spotyka się je też zresztą w bardzo wielu miejscach, łącznie z tekstem (czcionki TrueType są opisane takimi właśnie splajnami).

Dlaczego?… Jak wiadomo, krzywa trzeciego stopnia w postaci Béziera jest opisana czterema punktami: początkiem, końcem oraz dwoma punktami kontrolnymi, których położenie determinuje kształt krzywej. Współrzędne tych czterech punktów można podstawić do równania parametrycznego i dla wartości parametru przebiegających od 0 do 1 wyliczyć dowolny punkt leżący na krzywej.
W praktyce ta reprezentacja ma kilka zalet:

  • Dobrze widać, w jaki sposób położenie punktów kontrolnych wpływa na kształt krzywej. Wystarczy przez chwilę pobawić się choćby wspomnianym narzędziem z programu Paint, by zobaczyć, że punkty te dość intuicyjny sposób “wyciągają” krzywą, której kształt jest jakby wypadkową przyciągania dwóch “sił”.
  • Można dokładnie określić wielokąt otaczający krzywą: jest to czworokąt opisany na czterech punktach, które ją definiują. To oczywiście przydaje się do różnego rodzaju przycinania.
  • Aby przekształcić krzywą Béziera poprzez translację, obrót lub skalowanie, wystarczy przekształcić opisujące ją punkty. Taka transformacja jest więc bardzo tania, zwłaszcza w porównaniu z przekształcaniem każdego z wielu punktów, których użylibyśmy do przybliżania naszej krzywej.
  • Łatwo jest też sklejać krzywe Béziera za sobą, zachowując w punktach połączeń nie tylko ciągłość, ale również w miarę potrzeby gładkość taką samą, jak na całej długości sklejanych krzywych.

Krzywą Béziera można także w miarę szybko narysować, jako że przy stałym (i małym) kroku dla parametru równania przybliżone wyliczanie kolejnych punktów wymaga tylko dodawań. To aczkolwiek cecha wszystkich krzywych wielomianowych, podobnie też jak jedna z ich wad: niemożność dokładnego odwzorowania okręgu. W praktyce gdy ogranicza nasz rozdzielczość pikselowa, taka niedokładność nie jest jednak zbytnim problemem.

Tak więc krzywe Béziera są obecnie prawdopodobnie najlepszym sposobem reprezentowania linii krzywych zarówno na płaszczyźnie, jak i w przestrzeni. Ale oczywiście w przestrzeni trójwymiarowej moglibyśmy chcieć zobaczyć coś więcej, na przykład… płaty Béziera :) To już jednak temat na inną okazję. Na razie mogliśmy się przekonać, że wersje dwuwymiarowe są, jak na krzywe, całkiem proste ;]

Tags: ,
Author: Xion, posted under Programming » 5 comments

Przydatne ułatwienia dostępu

2007-12-12 23:37

Ponieważ nowoczesne systemy operacyjne – takie jak Windows – starają się być przyjazne dla wszystkich użytkowników, już od dawna wyposażone są w specjalne funkcje ułatwiające korzystanie z komputera osobom z różnymi rodzajami niepełnosprawności. Jest więc możliwość ustawienia większego kontrastu interfejsu użytkownika, włączenia śladu po kursorze, łatwiejszego wciskania skrótów klawiaturowych, i tak dalej.

Ikona Centrum ułatwień dostępu w Windows VistaChociaż większość tych możliwości zdaje się być przeznaczona wyłącznie dla wspomnianych użytkowników, to jednak przynajmniej dwie mogą być użyteczne dla wszystkich korzystających z Windows. Osobiście znam i często korzystam z dwóch takich, a mianowicie:

  • Wyświetlanie podkreśleń dla skrótów klawiaturowych w menu – takich jak np. Plik czy Wklej. Dawniej podkreślanie to było domyślnie włączone, ale ktoś pewnie kiedyś uznał, że psuje ono wygląd interfejsu i że w zasadzie nikt już klawiatury nie używa ;P Naturalnie jest to przekonanie całkowicie błędne, dlatego też uwidocznienie tychże skrótów jest bardzo dobrym pomysłem.
  • Funkcja KlawiszeMyszy. Jest to możliwość sterowania pozycją kursora myszy przy pomocy klawiszy strzałek z klawiatury numerycznej. Tym, co czyni tę funkcję przydatną, jest precyzja: jedno wciśnięcie przesunie bowiem kursor dokładnie o jeden piksel (w jednym z ośmiu kierunków), co ułatwia pracę z programami graficznymi albo rozmieszczanie elementów interfejsu użytkownika.

Jak wszystkie ułatwienia dostępu, możemy je włączyć przy pomocy odpowiedniego apletu w Panelu sterowania: Centrum ułatwień dostępu w Windows Vista lub Ułatwienia dostępu w starszych wersjach systemu.

Tags: ,
Author: Xion, posted under Applications » 1 comment

PageRank i pozorne rozproszenie Internetu

2007-12-11 22:13

Znana skądinąd wyszukiwarka Google korzysta z pomysłowego systemu oceniania “wartości” stron zwanego PageRank. W skrócie polega on na obliczeniu średniej ważonej z liczby linków prowadzących do danego dokumentu w sieci WWW. Wagą każdego łącza jest z kolei wartość PageRank jego strony źródłowej. Wynika stąd, że linki pochodzące od “bardziej wartościowych” stron są też skuteczniejsze w podbijaniu rangi stronom, do których wskazują. Naturalnie Google nie publikuje szczegółów algorytmu, ale wiadomo na przykład, że są w nim wykorzystywane… wartości własne pewnej macierzy :)

Wartość PageRank na Google ToolbarTo, dlaczego PageRank zwrócił moją uwagę, to jego wartość dla tej strony w porównaniu z innymi. Otóż choćby dla takiego Warsztatu ocena jest zaledwie o jeden większa, co wydaje się bardzo dziwne, jako że różnica w dziennej odwiedzalności jest mniej więcej dwudziestokrotna (nietrudno się domyślić, na czyją korzyść ;))
Skąd taki efekt? Po prostu w Google skala PageRank jest logarytmiczna, więc przyrost o 1 punkt oznacza tak naprawdę zwiększenie wartości pewną ilość razy, nie zaś dodanie stałej wartości. To w sumie dość logiczne, jeżeli weźmie się pod uwagę fakt, że kilka(naście/set) najpopularniejszych stron w rodzaju portali czy Wikipedia z łatwością przekroczyłoby skale arytmetyczne.
Jednocześnie widać też, że Internet tylko z pozoru jest siecią rozproszoną.Pod względem łącza mają swoje punkty węzłowe – są nimi wielkie routery przekierowujące gigabity sieciowego ruchu z całych państw i kontynentów. Natomiast jeśli chodzi o sieć spiętą hiperłączami, także mamy pewne kluczowe punkty – czyli witryny oznaczone wysokim współczynnikiem PageRank.

Tags: ,
Author: Xion, posted under Internet » Comments Off on PageRank i pozorne rozproszenie Internetu

Dymki systemowego zasobnika

2007-12-10 19:09

Część paska zadań w systemie Windows obok zegara pokazującego aktualny czas to tak zwany zasobnik systemowy (system tray) lub obszar powiadomień. Tradycyjnie widnieją w nim ikonki tych aplikacji, które zasadniczo działają w tle i przez większość czasu nie wymagają interwencji użytkownika. W przeciwieństwie jednak do procesów usług, których zupełnie nie widać (o ile się nimi specjalnie nie zainteresujemy), programy te czasami wymagają naszej uwagi. I na różne sposoby mogą się starać ją zwrócić.

Możliwa jest chociażby zmiana wyglądu ikonki, polegająca na przykład na jej miganiu. Istnieje jednak wówczas ryzyko, że użytkownik tego po prostu nie zauważy – zwłaszcza, że począwszy od Windows XP ikony dłuższy czas nieaktywne są automatycznie przez system ukrywane. Poza tym zaprogramowanie migania ikony jest wbrew pozorom nieco kłopotliwe, bo całą tę animację trzeba zakodować ręcznie. A ponadto nie daje to żadnych możliwości przekazania, o co tak naprawdę naszemu programowi chodzi.
Na drugim końcu skali mieści się z kolei wyświetlenie standardowego okna komunikatu (message box). Wymaga to tylko wywołania jednej funkcji i daje gwarancję, że użytkownik nie przeoczy tej informacji. Aczkolwiek prawie pewnym skutkiem jest też podniesienie poziomu irytacji tegoż użytkownika, któremu nagle przerywamy pracę, by zasygnalizować jakąś błahostkę. Lepiej więc nie nadużywać tej metody i stosować ją do sytuacji naprawdę ważnych.

Dymek balonowyW zwykłych przypadkach bardzo dobrze natomiast sprawdza się wynalazek wprowadzony w Windows 2000, czyli tzw. dymki balonowe (balloon tips). Faktycznie wyglądają one podobnie do dymków w komiksach i pojawiają się tuż nad paskiem zadań, wskazując na ikonę, której dotyczą. Mogą przekazywać sam tekst, ale można też opatrzyć je tytułem i jedną z czterech systemowych ikon – zupełnie jak zwykłe okna komunikatu. W przeciwieństwie jednak do nich, dymki nie wyskakują znienacka i nie wymagają żadnego potwierdzania, gdyż po pewnym (ustalonym) czasie same znikają.
Niestety, niewiele aplikacji używa dymków balonowych – a szkoda, bo to jeden z ciekawszych elementów interfejsu użytkownika wprowadzonych w nowszych wersjach Windows. Prawdę mówiąc sądzę, że nawet w przypadku programów, które domyślnie “nie siedzą” w zasobniku, wykorzystanie dymków jest lepsze w przypadku zdarzeń zachodzących w tle niż np. miganie paskiem tytułowym okna. To mniej stresu i więcej informacji dla użytkownika :)

Pozostaje jeszcze kwestia programistyczna, czyli jak taki dymek wyświetlić. W Windows Forms jest to proste i wymaga na przykład wywołania metody ShowBalloonTip komponentu NotifyIcon. W klasycznym Windows API jest nieco gorzej, gdyż robimy to tę samą funkcją Shell_NotifyIcon, którą wykorzystuje się do każdej innej operacji na ikonce w trayu. Dla wygody najlepiej więc opakować ją we własną klasę.

Tags:
Author: Xion, posted under Programming » Comments Off on Dymki systemowego zasobnika

Koder, istota niepokorna

2007-12-09 21:52

Tę notkę mógłbym zacząć znanym stwierdzeniem: A miało być tak pięknie :) Przecież to w końcu na studiach człowiek zaczyna w pełni kierować własną edukacją i może zająć się poznawaniem tych zagadnień, które go naprawdę ciekawią. Koniec z wyjaśnianiem, dlaczego Słowacki wielkim poetą był i zapamiętywaniem szczegółów budowy mięśni poprzecznie-prążkowanych. Nareszcie przychodzi czas na bardziej przydatne i interesujące umiejętności – czyli programowanie, rzecz jasna.

A jak wygląda przełożenie tej teorii na praktykę? Otóż po dwóch latach mogę stwierdzić, że… dość kiepsko :) W istocie mamy tu do czynienia z wielkim dylematem – może nie natury filozoficznej, ale jednak całkiem ciężkiego kalibru.
Gdy bowiem zajmujemy się kodowaniem czysto hobbystycznie, automatycznie wydaje się nam ono o wiele rzędów wielkości atrakcyjniejsze niż to, nad czym musimy się skupiać przy okazji edukacji szkolnej. Działa tu pewnie prawo człowieka jako istoty przekornej, lubiącej podążać własnymi ścieżkami. Jednocześnie jednak tęskni się do wyidealizowanego czasu przyszłego, kiedy w końcu swoje zainteresowania będzie można realizować także w trakcie nauki i pracy.
Najwyraźniej wspominane prawo jest tak silne, że gdy ów czas w końcu przychodzi, zaczynamy lepiej wspominać wcześniejszy okres. Nie oznacza to oczywiście, że wolałbym dalej “przymusowo” uczyć się czegoś zupełnie niezwiązanego z szeroko pojętą informatyką; wręcz przeciwnie, nie zamieniłbym aktualnego kierunku studiów na żaden inny :) Problem w tym, że gdy ktoś zaczyna nas programowania uczyć i egzekwować wyniki, traci ono część swojej ‘magii’; podejrzewam, że dotyczy to chyba każdej dziedziny. Ponadto nie da się ukryć, że spełnianie stawianych wymagań oznacza konieczność napisania sporych ilości kodu i ostatecznie na własne kodowanie pozostaje mniej czasu i ochoty.

Wnioski są dwa. Pierwszy to – że użyję wychodzącego już na szczęście z mody sformułowania – oczywista oczywistość: każdy etap nauki czy pracy ma swoje dobre i złe strony, i do każdego trzeba się odpowiednio dostosować. Drugi zaś jest taki, że zawsze wierzymy, iż w przyszłości będzie lepiej. W tym przypadku faktycznie się to sprawdza, lecz chyba nigdy nie dzieje się to w sposób całkowicie zgodny z naszymi oczekiwaniami.
Wypada tylko się tym pogodzić i dalej robić swoje. Dla mnie też na pewno będzie to lepsze niż pisanie pseudofilozoficznych notek ;)


Author: Xion, posted under Programming, Studies, Thoughts » 8 comments

Rozpoznawanie możliwości procesora

2007-12-08 17:39

Niektóre funkcje dobrze jest pisać w asemblerze. Tak, wiem że dzisiaj – w epoce języków (zbyt) wysokiego poziomu – brzmi to dziwnie, ale to prawda. To najprostszy sposób na poprawienie wydajności często wykonywanych operacji, np. kalkulacji z użyciem wektorów i macierzy.
Rzecz w tym, że korzystając bezpośrednio z zaawansowanych możliwości oferowanych przez współczesne procesory, jednocześnie uzależniamy się od nich. Przykładowo, transformację wektora przez macierz można naturalnie po prostu przetłumaczyć z odpowiedniego wzoru na instrukcje jednostki zmiennoprzecinkowej i uzyskać kod działający na każdym procesorze. Jeżeli jednak użyjemy np. SSE2, możemy uzyskać kilkakrotny wzrost wydajności – lecz wówczas nasza funkcja będzie działała tylko na nowszych procesorach.

Najlepiej byłoby więc mieć kilka wersji takiej funkcji i wybierać odpowiednią dla procesora pracującego na danej maszynie. Jak jednak wykryć, co potrafi dana jednostka? Otóż z pomocą przychodzi nam system operacyjny. W Windows na przykład istnieje funkcja o wiele mówiącej nazwie IsProcessorFeaturePresent, przy pomocy której możemy sprawdzić obecność rozszerzeń MMX, 3DNow!, SSE i SSE2.
Oczywiście, takiego sprawdzenia należy dokonać raz na początku działania programu. Jeśli jednak po prostu zapiszemy jego rezultat w formie globalnych flag boolowskich, to ich odczytywanie np. przy każdym dodawaniu wektorów będzie nie tylko kłopotliwe, ale i nieefektywne.

Lepszym rozwiązaniem jest stworzenie odpowiedniej liczby globalnych wskaźników na funkcje, inicjowanych w czasie uruchamiania programu; tak jak poniżej:

  1. typedef VEC3 (*Vec3Func)(const VEC3*, const VEC3*);
  2. Vec3Func Vec3_Add, Vec3_Sub, Vec3_Cross;
  3.  
  4. // sprawdzenie obecności rozszerzenia SSE2
  5. if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE))
  6. {
  7.    Vec3_Add = Vec3_Add_SSE2;
  8.    Vec3_Sub = Vec3_Sub_SSE2;
  9.    Vec3_Cross = Vec3_Cross_SSE2;
  10. }
  11. else
  12. {
  13.    // w przypadku jego braku, używamy funkcji zakodowanych
  14.    // na zwykłej jednostce zmiennoprzecinkowej
  15.    Vec3_Add = Vec3_Add_FPU;
  16.    Vec3_Sub = Vec3_Sub_FPU;
  17.    Vec3_Cross = Vec3_Cross_FPU;
  18. }

Dzięki temu zarówno w kodzie asemblerowym poszczególnych wersji (którego litościwie nie pokażę ;D), jak i wywołaniach, nie widać żadnego śladu po ‘magii’ wyboru funkcji dostosowanej do procesora. Narzut to rozwiązanie to naturalnie jedna dereferencja wskaźnika więcej; sprawdzanie flag (porównaniami i skokami) trwałoby znacznie dłużej.

Tags: , ,
Author: Xion, posted under Programming » 5 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.