http://mkolkowski.lqnstudio.mydevil.net/ WIZUALIZACJA INFORMACJI TEKSTOWEJ- Implementacja w HTML5/JS Michał Kołkowski Promotor: prof. dr hab. Włodzisław Duch
SPIS TREŚCI W trakcie prezentacji omówię gotowe fragmenty kodu programu oraz bardziej szczegółowo niż ostatnio opowiem o konkretnie zastosowanych rozwiązaniach. 1.Zarządzanie projektem System kontroli wersji SVN Podział plików 2.Pobieranie danych z bazy WORDNET w postaci SQL Podpowiadanie słów oraz ich wyszukiwanie Pobieranie danych wybranego słowa 3.Interfejs użytkownika Dialogi, kontrolki oraz jquery UI Rozmiar Canvasa, skalowanie, tranzlacja 4.Algorytmy Wizualizacji Algorytm oparty na siłach Algorytmy pomocnicze Próba ulepszenia działania plan 5.Podsumowanie i wnioski
Zarządzanie projektem oraz podział plików
Zarządzanie projektem System kontroli wersji Pomaga śledzić zmiany w kodzie, przywracać różne wersje kodu i łączyć zmiany dokonane przez wielu programistów. Systemy kontroli dzielą się na scentralizowane(klientserwer) i rozporoszone(p2p) System scentralizowany(klient-serwer) Istnieje centralny serwer, do którego każdy wysyła swoje zmiany i pobiera z niego nowe wersje kodu o Wymaga stale dostępnego serwera. SVN(subversion), CVS
SVN - Subversion scentralizowany system Wolne, otwarte i wieloplatformowe oprogramowanie (Apache). Transkacje Zmiany zapisywane tylko gdy dokonano poprawnie wszystkich modyfikacji (minus CVS) Historia zmian plików/nazw Możliwość autoryzacji użytkowników. Archiwa publiczne i prywatne. Widać, który użytkownik co modyfikował. Dostęp przez serwer Apache lub dedykowany serwer(oddzielny protokół klient/serwer) Rozróżnienie plików binarnych (np. grafik, muzyki, kompilatów) Tworzenie rozgałęzień i tagowania
Web SVN przeglądanie kodu przez przeglądarkę Pozwala przeglądać różne wersję kodu/zmiany użytkowników z poziomu strony www. Łatwy dostęp do dziennika zmian. http://www.websvn.info/ - Jeden z wielu skryptów tego typu. Umożliwia wiele ciekawych opcji w tym przeglądanie, które linie kodu zostały przez kogo wstawione. Bardzo wygodne dla projektów o otwartym kodzie źródłowym. Nie wymaga pobierania i środowiska do wygodnego obejrzenia kodu. Moje repozytorium: https://repo.mydevil.net/svn/pub/lqnstudio/vsearch/ Do aktualizacji zmian używam domyślnie zainstalowanego pluginu Subversion w środowisku NetBeans
WebSVN oraz podział plików
Podział plików Główny katalog (/) index.php Zawiera szkielet strony i tekst powiadomień, ponieważ nie jest on wczytywany z bazy. Zawiera szkielet dialogów Importuje wszystkie pliki.js oraz.css (jest plikiem łączącym wszystko w całość) Arkusze stylów dla strony oraz dla biblioteki jquery-ui Katalog (/wordnet) getdataautocomplete.php skrypt PHP odpowiedzialny za podpowiadanie wyrazów. Zwraca do JS listę słów. getdatawordnet.php pobiera cały zestaw danych dla wpisanego przez użytkownika słowa wordnet30.sqlite Odchudzona wersja bazy Wordnet 3.0 w formacie sqlite
Podział plików cd. Katalog struct(/struct) zawiera klasy, w których przechowuje dane structt.js Plik zawiera definicje najważniejszych struktur przechowujących dane pobrane ze słownika wordnet oraz dane fizyczne dotyczące algorytmu rozkładu grafu. Istnieją tu również zdefiniowane struktury przechowujące preferencje programu. pvector.js klasa wektora 2D. Umożliwia operacje +/-/:/* oraz liczenie długości, normalizację, kopiowanie Katalog (/libs) zawiera biblioteki jquery, jquery-ui, jquery-cookie Katalog (/nbproject) zawiera ustawienia projektu środowiska NetBeans
Podział plików cd. Katalog (main/) Zawiera kod odpowiedzialny za działani interfejsu(przycisków/dialogów/formularzy), rysowanie grafiki na canvas oraz dostosowywanie rozmiaru do okna przeglądarki, skalowanie/translacje. Zawiera również kod wykonywania algorytmu rozkładu na strukturach/funkcjach zdefiniowanych w plikach z folderu /structt.js helpclasses.js niewielki plik zapewniający pobieranie kolorów dla części mowy oraz tłumaczenie symboli na nazwy positioning.js oferuje funkcjonalność rozkładu kołowego, która wspomaga główny algorytm.
Pobieranie danych
Pobieranie danych z bazy Wordnet w postaci SQLITE Realizacja w PHP Interfejs PDO umożliwia łatwą zmianę typu bazy, przy minimalnej zmianie kodu. Obsługuje bazy: MySQL, PostgreSQL, Oracle i SQLite. Pliki PHP zwracają dane w postaci JSON. Pobieranie tych danych realizuje funkcja getjson. Obiekt suggests to funkcja, która będzie obsługiwała dane.
Sugestia wpisywanych wyrazów Kod PHP Zwyczajnie tworzę tablicę słów zaczynających się od wpisanej przez użytkownika frazy. Do JS zostanie przekazany wydruk tej tablicy po przejściu przez funkcję json_encode. Spowoduje to, że będzie można odczytać te dane w JS jak obiekt JSON.
Wywołanie zapytania JSON po naciśnięciu znaku w formularzu. Opóźnienie 1.5s zapobiega Przerywaniu poprzednich zapytań oraz powoduje, że w przypadku szybkiego wpisania pierwszych znaków wyszukana zostanie cała wpisana fraza. Opcja autocomplete z biblioteki jquery-ui #searchedit odwołuje się do zwykłego pola formularza Funkcji autocomplete przekazuję tablicę danych tekstowych. Po wykonaniu funkcji autocomplete natychmiastowo pojawiają się podpowiedzi pod formularzem
Autocomplete podsumowanie Przedstawione rozwiązanie pomaga znaleźć potrzebne słowo tylko w przypadku wpisania jego znacznej części. LIMIT 5 jest konieczny do zachowania szybkości Algorytm celowo nie uwzględnia podciągu w środku słowa, ponieważ takich słów może być dużo, a mam wybrać tylko 5(możliwość wybrania ich, gdy nie ma żadnych innych) Możliwe do użycia bardziej zaawansowane mechanizmy: TLCS Najdłuższy wspólny podciąg najdłuższy nieprzerwany ciąg znaków w dwóch wyrazach Odległośc Levenshteina najmniejsza ilość operacji wstawienia/usunięcia/zamiany, aby uzyskać drugi wyraz Algorytmy te można wykorzystać w przypadku wypisywania dłuższej listy podobnych wyrazów. Oczywiście żaden z przedstawionych mechanizmów nie uwzględnia semantyki. Do tego może posłużyć właśnie WordNet.
Pobieranie danych wpisanego wyrazu Opiera się na tych samych technologiach co podpowiedzi. AJAX z użyciem funkcji getjson oraz PHP::PDO. Tabela words zawiera słowa Tabela senses tabela złączeniowa synsetów i słów Tabela synsets zawiera synsety wraz z definicją, częścią mowy i przykładami Jest to uproszczony model, gdyż można jeszcze uwzględnić typy relacji, przynależnośc do grup semantycznych itp.
Przed wysłaniem do skryptu zamieniam na małe litery I pozbywam się spacji przed/po ciągu xhr.abort() jeśli xhr będzie globalne pozwoli to nam na przerwanie zapytania w trakcie. Funkcja newgraph ma bardzo ważne i rozległe zadanie: Zatrzymanie algorytmu rozkładu i wyczyszczenie starych danych Rozdzielenie pobranych danych do przygotowanych struktur danych Początkowy rozkład Inicjalizacja i rozpoczęcie głównego algorytmu rozkładu Uzupełnienie zawartości Node Structure, czyli tekstowego spisu węzłów Jeśli danych nie udało się pobrać(np. Nie ma słowa w bazie) to ustawiamy zmienną globalną root = null. Wtedy pozostałe mechanizmy bedą wiedziały, że zaszła ta sytuacja. Wyświetlam też stosowny komunikat dla użytkownika.
Interfejs użytkownika i zastosowane rozwiązania
jquery UI Pozwala na bardzo łatwe uzyskanie ciekawych efektów w interfejsie użytkownika. Zawiera kilka ciekawych widżetów oraz szereg efektów i funkcjonalności(przemieszczanie) Elementy te można dowolnie modyfikować, zmieniać grafikę, parametry, ulepszać itp. Użyte przeze mnie widżety: Dialog, tabs, spinner, button, slider i autocomplete. http://jqueryui.com/demos/
Dialogi i tabsy Dialogi mają wiele opcji, które można modyfikować. Warto zwrócić uwagę na show/hide, gdzie należy podać typy animacji zdefiniowane w jqueryui. Do dialogu można też dowolnie dodawać buttony.
Slider do skalowania Wartości dyskretne od -1 do 7 oznaczają naprawdę skalowanie (.25,.5,.75,.85, 1.25, 1, 1.5, 2, 4) Oczywiście zmiana musi być dokonana w następnym odświeżeniu canvasa. Z tego powodu ustawiam zmienną globalną scale, do której będzie miała dostęp funkcja rysująca
Budowa funkcji rysującej - przykład
Wnioski Uzyskujemy element Canvas jeśli to możliwe Rysujemy na kontekście 2D, jeśli uda się go nam pobrać Rysunek odświeżamy w funkcji drawfunction(co przyjęty interwał) i tylko tu możemy rysować Przed nowym rysowaniem: Czyścimy Canvas wypełniając go kolorem tła Zapisujemy kontekst Dokonujemy translacji, skalowania lub zmiany rozdzielczości canvasa Po rysowaniu przywracamy kontekst rysowania
Przykład, podanie środka okręgu: Należy rozróżnić zmianę wielkości elementu canvas, a zmianę wielkości w klasie ctx.canvas. Pozycje następnych elementów uzgadniane są według punktu (cx/2,cy/2) z tego powodu będą dobrze ustawione niezależnie od rozmiaru. Ctx.scale(,) sprawia, że również rozmiar jest skalowany
Translacja przesunięcie canvas Zmienne globalne gtranslate_x/y przechowują obecną translację. W funkcji mousemovecanvas ustawiane jest przesunięcie. Pod warunkiem, że wcześniej w funkcji mousedowncanvas nie zostało wykrye kliknięcie żadnego elementu. Funkcja translate(x,y) przed rysowanie w drawfunction wystarczy, aby ta funkcja działała. Nie muszę już przesuwać elementów samodzielnie. Na zmienne gtranslate_x/y muszę jednak zwracać uwagę przy wykrywaniu kliknięcia elementu
Zapamiętuje w globalnych strukturach czy i jaki element trzymam. Dla tego elementu nie przeprowadzam algorytmu. Wykrywanie kliknięcia elementu Ważny jest sposób obliczania pozycji (real_x,real_y). Tak, aby działał on przy translacji/skalowaniu/zmianie rozmiaru canvasa.
Struktury danych oraz algorytm rozkładu
pvector Operacje na wektorach dwuwymiarowych Dzięki tej klasie łatwiej zrozumieć kod dotyczący algorytmu, gdyż nie trzeba się przejmować tymi działaniami
Node Reprezentuje pojedyńczy węzeł. Może to być słowo, korzeń lub synset. Posiada wiele dzieci oraz jednego rodzica(chwilowo) Pole type pozwala rozróżnić typ obiektu Pole hang==1 oznacza, że element będzie hakiem nie będzie reagował na działania algorytmu Pola vtension, vvelocity oraz vposition są typu pvector. Zawierają pola x,y. Wykorzystywane są w algorytmie rozkładu. Pole vposition to realna pozycja obiektu względem środka canvasa. coulombrepulsion() metoda, która pozwala obliczyć działanie siły elektrostatycznej na węzeł i dodać do vtenstion solveeuler() ostateczna część algorytmu. Ustala na podstawie zmian dokonanych przez sprężyny/elektrostatykę prędkość, a następnie oblicza przemieszczenie w czasie i nową pozycję.
Rozszerzenia Node Zastosowane w celu zwiększenia czytelności kodu. Definiują parametry Node tak, aby pasowały do konkretnego obiektu. Rozszerzają obiekt Node o właściwości potrzebne dla tego typu obiektu WAŻNE DO POPRZEDNIEGO SLAJDU: addchild() oprócz dodania nowego węzła tworzy sprężynę między obiektami oraz dodaje nowy obiekt do obiektu globalnego Director.
Spring Reprezentuje sprężynę, która jest fizycznym połączeniem dwóch obiektów. Spingiffy jest sprężystością przeznaczoną dla algorytmu rozkładu solve() używane w algorytmie rozkładu. Rozwiązuje problem sprężystości dwóch ciał. Modyfikuje w nich vtension.
Director Istnieje jeden taki obiekt dla całego programu Zbiera on wszystkie występujące na scenie elementy Pozwala szybciej i prościej wykonać niektóre algorytmy, niż gdybyśmy przeglądali strukturę drzewiastą. Zawiera pole root, które jest wskaźnikiem na korzeń. Gdy pole to jest == null wtedy program wie, że nie ma pobranych danych np. użytkownik nie podał jeszcze słowa nodes[] i springs[] to tablice obiektów getnode() pozwala pobrać obiekt bez ryzyka, że wyjdziemy po za tablicę. addnode(node) jest używane w metodzie addchild() obiektu Node i nie powinno być używane w celu dodania obiektu. sumsynsetnode(iffunction) oferuje ciekawą funkcjonalność liczenia ilości węzłów o wskazanych parametrach
Algorytm rozkładu
Zastosowany przeze mnie algorytm jest dość prosty: Rodzice są połączeni z dziećmi sprężynami. Wykorzystuję tu prawo Hooka. Sprężyny mają ustaloną długość, do której będą dążyły. Każdy węzeł oddziałuje z każdym innym węzłem odpychaniem elektrostatycznym. W zasadzie ponieważ przyjmuję, że ładunki są takie same to siła odpychania zależy od współczynnika oraz odległości między węzłami. Zależy nam, aby odległość ta nigdy nie była < 0.1 ponieważ mamy ją w mianowniku podniesioną do kwadratu i dałoby to ogromne siły. Dlatego do tej odległości zawsze dodaje 0.1, aby tą siłę zmniejszyć. ALGORYTM ROZKŁADU OPARTY NA SIŁACH
WYDAJNOŚĆ Sprężyny O(n) Elektrostatyka O(n^2) Na koniec w celu ustalenia zmian położenia w czasie używany jest algorytm Eulera
Algorytm sprężyny Spirng.solve() (u,v) E (springiffy)(d(p u, p v ) l u,v ) x v x u d(p u, p v )
Siły elektryczne Node.coulombRepulsion() (u,v) VxV (repulsion) 1 d(p u, p v ) Zastosowano dystans graniczny dla tej siły, aby algorytm wykonywał się rzadziej Zastosowano mnożenie tej siły *15 w celu rozciągania liny od korzenia(siła ta musi przeważyć sprężystość)
Euler Sposób rozwiązywania równań różniczkowych.
ALGORYTM ROZKŁADU KOŁOWEGO - pomocniczy Najpierw stosuję go podając środek jako (0,0), a następnie dla każdego sysnetu podając jako środek jego współrzędne. W ten sposób rozmieszczam najpierw synsety wokół korzenia, a potem lemmy wokół synsetów.
Algorytm oparty na siłach - całość Zeruje vtension dla każdego węzła Obliczam coulombrepulsion() dla każdego węzła.
Dla każdej sprężyny rozwiązuję algorytm sprężyny. Euler oraz liczenie enetgii kinetycznej układu. Zatrzymanie algorytmu. Odwołanie: setinterval(dt).
Przechowywanie opcji Obiekty odpowiedzialne za przechowywanie opcji. gdefaultoptions przechowuje opcje domyślne. Pobieranie opcji do menu wyboru opcji Zapis nowych opcji do obiektu galgorithmoptions
Podsumowanie Przedstawiłem dużą część używanych przeze mnie rozwiązań Dostępne obecnie technologie webowe pozwalają zrealizować wizualizację słownika Wordnet w dość prosty i efektowny sposób. W programie pojawi się jeszcze wiele zmian szczególnie związanych z wyświetleniem dodatkowych danych. Również layout projektu na razie pozostawia wiele do życzenia
Dziękuję za wysłuchanie prezentacji