Flying memes

Midpoint displacement algorithm

Grazie all’appuntamento settimanale con i Ruby Quiz oggi sono venuto a conoscenza del Midpoint displacement algorithm, utilissimo per la generazione di una spezzata che simuli la silhouette di una catena montuosa.

Questo algoritmo basa la sua operatività essenzialmente su di un array contenente le altezze dei punti che compongono la spezzata. Tale array all’inizio presenterà solamente due valori (capo e coda dell’intero profilo che vogliamo creare), entrambi settati a 0.

Il cuore del Midpoint displacement algorithm si basa sull’iterazione sull’array sopra indicato calcolando per ogni coppia di punti un nuovo elemento contenente la media delle altezze dei due punti vicini variata di un certo valore random. Eseguendo quest’ultimo passaggio un numero sensibile di volte otterremo una linea spezzata molto simile alla seguente:

midpoint displacement alghoritm - rough version

midpoint displacement alghoritm - rough version

Il risultato appena mostrato pur non essendo del tutto deprecabile mostra comunque una linea che difficilmente si adatterebbe al profilo di una catena montuosa (forse delle Dolomiti); quello che ora dobbiamo fare è ‘smussare’ gli sbalzi di altezza tra i vari segmenti e il modo migliore per ottenere questo risultato è sicuramente quello di ridurre il range di valori dell’apporto random ad ogni interazione.

Prima di mostrare il risultato finale vorrei condividere il codice sorgente dell’applicativo (scritto in Ruby); il programma si divide in due sezioni: la prima genera l’array delle altezze, la seconda lo stampa utilizzando Shoes.


require 'enumerator'

# ==== PRIMA PARTE  ====
# calcolo delle altezze

segments = [0.0,0.0]; midpoints = []
max_randomness = 40
times = 5
app = {:width=>600, :height=>200}
boundary = 10

while times > 0  do
  segments.each_with_index do |b,i|
    next if i == 0; a = segments[i-1]
    midpoints << ((a + b / 2.0) + (rand(max_randomness) - (max_randomness / 2.0)))
  end
  segments = segments.zip(midpoints).flatten.compact
  max_randomness -= 5; times -=1;
end

segment_width = (app[:width] - boundary*2) / segments.size.to_f

# ==== SECONDA PARTE ====
# stampa delle altezze con shoes 

Shoes.app (:title=>'Dreaming mountains', :resizable=>false, :width=>app[:width], :height=>app[:height]) do
  stroke white
  strokewidth 2
  background black

  segments.each_with_index do |b,i|
    next if i == 0; a = segments[i-1]
    line (f = (segment_width*(i-1) + boundary)), a + app[:height]/2.0, (f + segment_width), b + app[:height]/2.0
  end
end

Ed ecco il risultato dell’algoritmo appena mostrato, notate come la spezzata sia meno ‘spigolosa’ della precedente.

Midpoint displacement algorithm with Ruby and Shoes

Midpoint displacement algorithm with Ruby and Shoes

Alla prossima!

Tags: , ,