Nelle scorse lezioni abbiamo visto come far muovere degli oggetti (che rappresentano i tweet) incolonnati a seconda dell'appartenenza ad alcuni slot (che rappresentano la lingua in cui i tweet sono scritti). Dobbiamo ancora risolvere il problema delle collisioni.
La tattica potrebbe incentrarsi sul controllare la posizione del tweet corrente rispetto a tutti i tweet precedenti sul suo slot di appartenenza cercando una eventuale sovrapposizione. In caso positivo potremmo allontanare tra loro i tweet risolvendo in questo modo la collisione.
Per prima cosa operiamo sulla classe Slot
aggiungendo una funzione inc
che accetta un tweet in ingresso e lo accoda ad un array, ecco come appare la classe dopo queste modifiche:
// la classe che rappresenta la lingua
class Slot{
String language;
int index;
int pos_x;
ArrayList tw; // array di tweet associati a questo slot
Slot(lan){
language = lan;
index = slots.size();
prev_slot = slots.get(index-1);
tw = new ArrayList(); //inizializzo l'array
pos_x = ( prev_slot == null ? 0 : prev_slot.get_pos_x(prev_slot.last_col) ) + box_w + slot_gap; slots.add(this);
}
void inc(tweet){
tw.add(tweet); // aggiungo il tweet
}
int get_pos_x(){ return pos_x;}
}
Bene, adesso creiamo una variabile slot_index
che memorizzi, per ogni tweet, la sua posizione nello slot di appartenenza, modifichiamo quindi la classe Tweet
:
class Tweet{
HashMap tweet;
int index;
Slot slot;
float pos_y, vel_y;
int slot_index // conterrà la posizione del tweet nello slot di appartenenza
Tweet(HashMap data, i){
tweet = data;
pos_y = brick_start; vel_y = 0;
slot = getSlot(data.iso_language_code) || new Slot(data.iso_language_code);
index = i;
slot.inc(this); // aggiungo il tweet allo slot
slot_index = slot.tw.size() - 1; // memorizzo la posizione del tweet nello slot
}
// ... resto della classe ...
Ora riscriviamo il metodo draw
della classe Tweet
in modo che effettui il controllo di sovrapposizione di cui abbiamo precedentemente parlato e, in caso di collisione, aggiusti la posizione del tweet.
void draw(){
boolean update = true; // true = incrementa la posizione; false = stai fermo
for(int k=0; k < slot_index; k++){ // per ogni tweet precedente nello stesso slot
Tweet tw = (Tweet) slot.tw.get(k);
if( abs( pos_y - tw.pos_y) < (box_h+2)){ // se esiste sovrapposizione
pos_y = tw.pos_y - (box_h+1); //allontana il tweet eliminando la collisione
update = false; // non incrementare la posizione del tweet
break; // esci dal ciclo
}
}
rect(slot.get_pos_x(), pos_y, box_w, box_h); // disegna il tweet in ogni caso
if(update && pos_y < brick_end) { // se update=true e se non ha raggiunto il fondo
vel_y += gravita; //aggiorna la velocità
pos_y += vel_y; // aggiorna la posizione
}
}
Aggiorniamo il progetto all'interno del browser e osserviamo il risultato:
Se lasciamo scorrere l'animazione per un sufficiente periodo di tempo notiamo però che non esiste nessun meccanismo che impedisca ad una colonna di crescere a dismisura, al punto di evadere dai confini del
Canvas.
Per risolvere questo problema è sufficiente implementare un ulteriore controllo sullo slot che spezzi la colonna, raggiunto un determinato numero di elementi. Iniziamo dalla classe Slot
aggiungendo una variabile last_col
per memorizzare il numero di colonne già utilizzate dallo slot:
class Slot{
String language;
int index;
int pos_x, last_col;
ArrayList tw;
Slot(lan){
last_col = 0; // inizializziamo la variabile a 0
// ... resto della classe ...
All'aggiunta di ogni tweet dobbiamo ora controllare se sia o meno necessario aggiungere una nuova colonna allo slot e, in caso affermativo, incrementare last_col
e spostare la posizione di tutti gli slot successivi a questo per far spazio alla nuova colonna. Riscriviamo quindi il metodo inc
facendo in modo che applichi questo algoritmo e ritorni al tweet un indice della colonna nel quale è stato posizionato:
int inc(tweet){
// impostiamo il numero di tweet per colonna a 20
if(floor(tw.size() / 20) > last_col) { // ci servono più colonne delle attuali?
for(int k = index+1; k < slots.size(); k++ ) // per ogni slot successivo a questo
slots.get(k).pos_x += (box_w + col_gap); // incrementiamo la pos_x
last_col ++; // incrementiamo last_col
}
tw.add(tweet);
return last_col; // ritorniamo al tweet la sua colonna di appartenenza
}
Avendo introdotto questo nuovo concetto di 'colonna di appartenenza' dobbiamo modificare la funzione
get_pos_x
in modo che tenga conto della colonna nella quale è stato posizionato il tweet:
int get_pos_x(col){
return pos_x + (box_w + col_gap) * col;
}
Ora non ci resta che modificare la classe Tweet
in modo che tenga conto della 'colonna di appartenenza'. Creiamo quindi una variabile per lo scopo ('col_x
') e valorizziamola all'interno del costruttore:
class Tweet{
// ... tutte le precedenti dichiarazioni ...
int col_x; // conterrà l'indice della colonna assegnata a questo tweet
Tweet(HashMap data, i) {
tweet = data;
pos_y = brick_start;
vel_y = 0;
slot = getSlot(data.iso_language_code) || new Slot(data.iso_language_code);
index = i;
col_x = slot.inc(this); // memorizzo la colonna
slot_index = slot.tw.size() -1;
}
// ... resto della classe ...
Infine dobbiamo tenere conto di 'col_x
' all'interno del metodo draw
della classe Tweet
, in particolare è necessario modificare la chiamata a get_pos_x
e aggiungere un controllo sulla collisione che verifichi che i due tweet si trovino sulla medesima colonna:
void draw(){
boolean update = true;
for(int k=0; k < slot_index; k++){
Tweet tw = (Tweet) slot.tw.get(k); // aggiungiamo un check sulla colonna
if( abs( pos_y - tw.pos_y) < (box_h+2) && col_x == tw.col_x){
pos_y = tw.pos_y - (box_h+1);
update = false;
break;
}
} // passiamo la colonna alla funzione
get_pos_x rect(slot.get_pos_x(col_x), pos_y, box_w, box_h);
if(update && pos_y < brick_end){
vel_y += gravita;
pos_y += vel_y;
}
}
Visualizziamo il progetto all'interno del browser per verificare il corretto funzionamento di questo ultimo
avanzamento:
Con questo abbiamo concluso legata alla dinamica dell'infografica, ora possiamo passare alla
gestione del colore.