Negli anni 90 quando i miei amici matematici leggevano (Ri)creazioni al calcolatore di Dewdney (vedi anche pag. 107 del libro The Magic Machine a handbook of computer sorcery A.K.Dewdney 1990 oppure Le Scienze n 222 1987 pag 98 e succ) andava di moda programmare il proprio algoritmo di morphing e di creazioni di paesaggi frattali
L'algoritmo che mi avevano spiegato era
1) partire da un triangolo
2) cercare un punto casuale interno al triangolo
3) uniire i vertici del triangolo con il punto casuale interno ed iterare sui nuovi tre triangoli
All'epoca non ero riuscito a fare il mio codice...oggi ho deciso di chiedera all'AI. la cosa interessante del codice proposto e' l'algoritmo con cui viene cercato il punto casuale interno nella funzione random_point_inside. Vengono generati due numeri casuali compresi tra 0 e 1. Se la somma e' maggiore di 1 viene ridotta perche' il sistema esplora in realta' rettangoli, se la somma e' maggiore di 1 si e' selezionato un punto non nel triangolo
Un altro algoritmo (diamond square) e' basato su una griglia regolare lavorando sui vertici. Il modello e' frattale peri il metodo con cui viene calcolata la altezza
import numpy as np
import matplotlib.pyplot as plt
def midpoint_displacement(size, roughness=0.7):
"""
Genera un terreno frattale 2D (matrice di elevazione Z)
utilizzando l'algoritmo Diamond-Square semplificato (Midpoint Displacement).
:param size: Il lato della griglia, deve essere (2^n) + 1.
:param roughness: Controlla la "frattalità" (0=liscio, alto=irregolare).
:return: Una matrice NumPy di elevazioni (Z).
"""
# Inizializza la griglia Z con zeri
Z = np.zeros((size, size), dtype=float)
# 1. Inizializzazione degli angoli
# Assegna valori casuali iniziali ai quattro angoli per dare il via al processo
Z[0, 0] = np.random.rand() * 10
Z[0, size - 1] = np.random.rand() * 10
Z[size - 1, 0] = np.random.rand() * 10
Z[size - 1, size - 1] = np.random.rand() * 10
step = size - 1
rand_range = 10.0 # Range iniziale della casualità
# Processo iterativo
while step > 1:
half = step // 2
# Quadrati (Diamond Step)
# Calcola il punto medio di ogni quadrato (i, j)
for i in range(0, size - 1, step):
for j in range(0, size - 1, step):
# Media dei quattro angoli del quadrato
avg = (Z[i, j] + Z[i + step, j] + Z[i, j + step] + Z[i + step, j + step]) / 4.0
# Aggiunge casualità al centro del quadrato
Z[i + half, j + half] = avg + (np.random.rand() - 0.5) * 2 * rand_range
# Diamanti (Square Step)
# Calcola i punti medi dei bordi (punti 'diamante')
for i in range(0, size - 1, half):
for j in range((i + half) % step, size - 1, step):
# Punti da mediare (gestione bordi per non sforare l'array)
punti = [
Z[(i - half + size) % size, j], # Sopra
Z[(i + half) % size, j], # Sotto
Z[i, (j - half + size) % size], # Sinistra
Z[i, (j + half) % size] # Destra
]
# Media dei quattro vicini (quelli che esistono)
avg = np.mean(punti)
# Aggiunge casualità al punto medio del bordo
Z[i, j] = avg + (np.random.rand() - 0.5) * 2 * rand_range
# Questo codice semplificato è per i bordi interni; un'implementazione
# completa dovrebbe gestire meglio gli indici di confine
# Riduce l'ampiezza di casualità per la prossima iterazione (l'algoritmo frattale)
rand_range *= 2**(-roughness)
step = half
return Z
def visualize_wireframe(Z):
"""
Visualizza la matrice di elevazione Z come un wireframe 3D.
"""
size = Z.shape[0]
# Creazione delle coordinate X e Y
x = np.linspace(0, 1, size)
y = np.linspace(0, 1, size)
X, Y = np.meshgrid(x, y)
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# Plot del wireframe
ax.plot_wireframe(X, Y, Z, color='brown', linewidth=0.3)
# Impostazioni estetiche
ax.set_title(f'Montagna Frattale Wireframe (Roughness={ROUGHNESS})')
ax.set_xlabel('Asse X')
ax.set_ylabel('Asse Y')
ax.set_zlabel('Elevazione Z')
# Regola gli assi Z per centrare meglio la montagna
z_min, z_max = np.min(Z), np.max(Z)
z_range = z_max - z_min
ax.set_zlim(z_min - z_range * 0.1, z_max + z_range * 0.1)
plt.show()
# --- Parametri di Esecuzione ---
# La dimensione (size) DEVE essere (2^n) + 1, ad esempio: 33, 65, 129, 257.
# 65 è un buon compromesso tra dettaglio e velocità.
SIZE = 65
ROUGHNESS = 0.85 # Valori più alti (es. 0.9) danno montagne più "spigolose"
# 2. Generazione del Frattale e Visualizzazione
elevation_data = midpoint_displacement(SIZE, ROUGHNESS)
visualize_wireframe(elevation_data)


Nessun commento:
Posta un commento