LAB 06 - 18.11.2009 V minulém cvičení jsme se seznámili se základy systému Processing. Dnes si vytvoříme výchozí program pro interaktivní vizualizaci velké sítě. Konečným cílem je načíst velkou síť z datového souboru, zobrazit ji v hyperbolickém zobrazení (vybraný uzel a okolí podrobně, ostatné uzly okrajově) a docílit interaktivity tohoto zobrazení (změna uzlu s detailním zobrazením, vizualizace zvolených vlastností dat v síti). Základní kód vytváří proměnné pro uložení vrcholů a hran obecného grafu. Tyto proměnné naplní náhodně vygenerovanými údaji v části setup() a vykreslí je v části draw(). Spustěte a nastudujte následující kód. Změňte výchozí hodnoty některých proměnných a vyhledejte v kódu, kde a jak jsou použity. --- BEGIN PROGRAM --- /* Interactive graph visualization */ // init values for selected variables int nodes = 5; //number of graph nodes int bg = 226; //background color int fgs = 255; //foreground - line color color fgf = color(100,100,200); // foreground - fill color int size = 20; //size of node symbol //initialize nodes //X and Y positions of nodes float[] posx = new float[nodes]; float[] posy = new float[nodes]; //matrix with values of 1 for nodes connected by edges int[][] edge = new int[nodes][nodes]; void setup() { size(400, 400); smooth(); fill(fgf); // make this a randomly connected graph, for every pair of nodes i and j float val = 0; for (int i = 0; i < nodes; i++) { for(int j=i+1; j 0.5) { edge[i][j] = 1; } else { edge[i][j] = 0; } } } // create random layout //for every node for (int i = 0; i < nodes; i++) { posx[i] = random(0+size,width-size); posy[i] = random(0+size,height-size); } } void draw() { background(bg); stroke(0); //draw the nodes for (int i = 0; i < nodes; i++) { ellipse(posx[i], posy[i], 20, 20); } stroke(fgs); //draw the edges for (int i = 0; i < nodes; i++) { for(int j=i+1; j 0) { line(posx[i], posy[i], posx[j], posy[j]); } } } } --- END PROGRAM --- V dalších krocích budeme manipulovat s rozložením grafu v prostoru. Při náhodném rozložení uzlů se mohou některé uzly překrývat. Zavedeme proto funkce, které budou měnit polohu uzlů (posx[] a posy[]). Potřeba změny polohy se bude odvíjet od svzájemné vzdálenosti uzlů. Zavedeme proto funkci dist(), kterou budeme vzdálenost zjišťovat. float dist(int i, int j) { return sqrt(sq(posx[i]-posx[j]) + sq(posy[i]-posy[j])); } Následně zavedeme samotnou změny polohy jako funkci adjust() void adjust(int i, int j, int min, int max) { if(dist(i,j) > max) { attract(i,j); } if(dist(i,j) < min) { repulse(i,j); } } Ta zabezpečí, že pokud jsou uzly příliš blízko, zavolá se repulse() a pokud jsou zbytečně daleko, zavolá se attract(). Tyto funkce změní vzdálenost podle nastavení proměnné speed (větší hodnoty budou měnit polohu rychleji). void attract(int i, int j) { float txi, txj, tyi, tyj; txi = posx[i] + speed * (posx[j]-posx[i]); txj = posx[j] + speed * (posx[i]-posx[j]); tyi = posy[i] + speed * (posy[j]-posy[i]); tyj = posy[j] + speed * (posy[i]-posy[j]); posx[i] = txi; posx[j] = txj; posy[i] = tyi; posy[j] = tyj; } void repulse(int i, int j) { float txi, txj, tyi, tyj; txi = posx[i] - speed * (posx[j]-posx[i]); txj = posx[j] - speed * (posx[i]-posx[j]); tyi = posy[i] - speed * (posy[j]-posy[i]); tyj = posy[j] - speed * (posy[i]-posy[j]); posx[i] = txi; posx[j] = txj; posy[i] = tyi; posy[j] = tyj; } Teď zůstává jenom zabezpečit vložení volání adjust do cyklu draw(), výsledkem je kód for (int i = 0; i < nodes; i++) { for(int j=i+1; j 0) { line(posx[i], posy[i], posx[j], posy[j]); adjust(i, j, mindiste, maxdiste); } adjust(i, j, mindist, maxdist); } } Proměnné i a j označují uzly, pro které se funkce adjust() volá, mindist a maxdist jsou limitní vzdálenosti uzlů. Jiné hodnoty použijeme pro uzly spojené hranou a jiné pro ostatní uzly. Úpravy proveďte a otestujte. Následující kód obsahuje několik dalších úprav. Oddělili jsme funkce draw_nodes a draw_edges() pro větši přehlednost kódu. Vykreslování uzlů jsme upravili tak, aby jejich velikost odpovídala proměnné weight, do které se při tvorbě grafu ukládá počet hran vycházejících z daného uzlu. --- BEGIN PROGRAM --- /* Interactive graph visualization */ // init values for selected variables int nodes = 20; float edgep = 0.1; int bg = 226; int fgs = 255; color fgf = color(100,100,200); int size = 20; int maxdist = 400; int maxdiste = 200; int mindist = 40; int mindiste = 25; float speed = 0.01; //initialize nodes float[] posx = new float[nodes]; float[] posy = new float[nodes]; int[][] edge = new int[nodes][nodes]; int[] weight = new int[nodes]; void setup() { size(400, 400); smooth(); fill(fgf); // make this a randomly connected graph float val = 0; for (int i = 0; i < nodes; i++) { weight[i] = 0; for(int j=i+1; j 0) { line(posx[i], posy[i], posx[j], posy[j]); adjust(i, j, mindiste, maxdiste); } adjust(i, j, mindist, maxdist); } } } void adjust(int i, int j, int min, int max) { if(dist(i,j) > max) { attract(i,j); } if(dist(i,j) < min) { repulse(i,j); } } float dist(int i, int j) { return sqrt(sq(posx[i]-posx[j]) + sq(posy[i]-posy[j])); } void attract(int i, int j) { float txi, txj, tyi, tyj; txi = posx[i] + speed * (posx[j]-posx[i]); txj = posx[j] + speed * (posx[i]-posx[j]); tyi = posy[i] + speed * (posy[j]-posy[i]); tyj = posy[j] + speed * (posy[i]-posy[j]); posx[i] = txi; posx[j] = txj; posy[i] = tyi; posy[j] = tyj; } void repulse(int i, int j) { float txi, txj, tyi, tyj; txi = posx[i] - speed * (posx[j]-posx[i]); txj = posx[j] - speed * (posx[i]-posx[j]); tyi = posy[i] - speed * (posy[j]-posy[i]); tyj = posy[j] - speed * (posy[i]-posy[j]); posx[i] = txi; posx[j] = txj; posy[i] = tyi; posy[j] = tyj; } --- END PROGRAM --- Další úkol: 1. Předělejte dnešní kód do úkolu z minulého týdne, z uzlů se stanou instance objektu uzel a z některých funkcí jeho metody. 2. Změňte funkci adjust() tak, aby jsme docílili odlišné rozmístění uzlů, a to takové, kde uzly s největší vahou budou ležet uprostřed diagramu a uzly s menší vahou ve vnější části. 3. Zaveďte novou funkci pro interaktivitu, která po kliknutí myši vybarví odlišnou barvou nejbližší uzel (k místu kliknutí) a vypíše se text s pořadovým číslem uzlu. Při dalším kliknutí myši se zbarvení zruší. Konkrétní způsob a implementace je na vás, výsledky předveďte kolegům s krátkým kometářem příští týden. Nejlepší verzi pak převezmeme jako základ pro další práci.