piątek, 30 grudnia 2011

VPython: Zimowe fraktale

Jest zima, ale nie ma śniegu - coś na to poradzimy.

Wiele form występujących w przyrodzie posiada cechy samopodobieństwa - to znaczy że mały fragment jest podobny do całości. Jest to także cecha specyficzna fraktali, za pomocą których możemy opisać na przykład góry, rośliny i... płatki śniegu.

Śnieżka Kocha, fraktale Python Visual


Dzisiaj zaprezentuję jak korzystając z biblioteki Visual narysować śnieżkę Kocha. Początkującym radzę, żeby się nie zrażali, bo rzeczy związane z fraktalami, czy ogólnie z rekurencją nie dla każdego są jasne od samego początku, ale - będzie dobrze. Śnieżkę Kocha rysuje się w następujący sposób:
  1. Za pomocą linii rysujemy trójkąt równoboczny.
  2. Każdą z linii dzielimy na 3 części
  3. W miejscu środkowej części dodajemy dwie linie tak aby powstał trójkąt równoboczny.
  4. Usuwamy podstawę trójkąta
  5. Dla wszystkich istniejących linii, wykonujemy punkty od 2 do 5, do momentu aż się nam znudzi.
Na początek niezbędne formalności:

#!/usr/bin/env python
from __future__ import division (obsługa dzielenia niecałkowitego za pomocą znaku "/")
from visual import *


Tworzymy funkcję sniezka(), która:
  • Przyjmuje parametry:
    • etapy - ilość podziałów do wykonania
    • l - długość boku trójkąta
    • r - promień linii, którą rysujemy
  • Tworzy ogólną ramkę "gwiazdka", która ma w sobie zawierać cały fraktal
  • Tworzy 3 ramki dla każdej linii
  • Obraca ramki w taki sposób, by narysowane w nich obiekty tworzyły zamknięty trójkąt
  • Oblicza wysokość trójkąta, aby następnie narysować 3 linie w swoich ramkach
  • Generuje fraktale oparte na stworzonych liniach
  • Zwraca efekt końcowy
def sniezka(etapy,l,r):
    gwiazdka=frame()
    f1=frame(frame=gwiazdka)
    f2=frame(frame=gwiazdka)
    f3=frame(frame=gwiazdka)
    f2.rotate(angle=-2*pi/3, axis=(0,0,1))
    f3.rotate(angle=2*pi/3, axis=(0,0,1))
    h=(l)*3**(1/2)/2
    l1=curve(pos=((-l/2,h/3),(l/2,h/3)),frame=f1, radius=r)
    l2=curve(pos=((-l/2,h/3),(l/2,h/3)),frame=f2, radius=r)
    l3=curve(pos=((-l/2,h/3),(l/2,h/3)),frame=f3, radius=r)
    fract(l1,etapy,r,f1)
    fract(l2,etapy,r,f2)
    fract(l3,etapy,r,f3)
    return gwiazdka

Funkcja korzystała z funkcji generującej fraktale fract(), która wygląda następująco:

def fract(linia,etapy,r,f):   
    x1=linia.pos[0][0]
    y1=linia.pos[0][1]
    x2=linia.pos[1][0]
    y2=linia.pos[1][1]
    if etapy>0:
        rate(50) # opóźnienie w celu zrobienia animacji
        p1=(x1,y1,0)
        p2=(x1+(x2-x1)/3,y1+(y2-y1)/3,0)
        p3=(x2+(x1-x2)/3,y2+(y1-y2)/3,0)
        p4=(x2,y2,0)
        p5=obrocony(p3,p2,pi/3)
        c1=curve(frame=f, pos=(p1,p2),radius=r)
        c2=curve(frame=f, pos=(p2,p5),radius=r)
        c3=curve(frame=f, pos=(p5,p3),radius=r)
        c4=curve(frame=f, pos=(p3,p4),radius=r)
        linia.visible=False
        fract(c1,etapy-1,r,f)
        fract(c2,etapy-1,r,f)
        fract(c3,etapy-1,r,f)
        fract(c4,etapy-1,r,f)

 Funkcja działa w następujący sposób:
  1. Określa współrzędne początku i końca linii, której podziału ma dokonać
  2. Jeśli nie została wykonana określona ilość podziałów to:
    1. Ogranicza ilość wyświetlanych klatek do 50
    2. Oblicza współrzędne punktów podziałowych znajdujących się na linii
    3. Oblicza współrzędne punktu, który ma być wierzchołkiem nowego trójkąta, za pomocą funkcji obrocony()
    4. Usuwa starą linię
    5. Rysuje 4 nowe linie (2 boczne + 2 tworzące boki nowego trójkąta)
    6. Dokonuje podziałów nowo powstałych linii wywołując samą siebie (rekurencja)
Funkcja korzystała z funkcji obrocony(), która oblicza współrzędne punktu obróconego względem innego punktu o zadany kąt:

def obrocony(p,ps,L):
    X=p[0]
    Y=p[1]
    Xs=ps[0]
    Ys=ps[1]
    return ((X-Xs)*cos(L) - (Y-Ys)*sin(L) + Xs),((X-Xs)*sin(L) + (Y-Ys)*cos(L) + Ys),0

Parametry:
  • p - punkt do obrócenia
  • ps - punkt środkowy
  • L - kąt obrotu podany w radianach
Na koniec wywołujemy naszą śnieżynkę i możemy cieszyć się efektem:

sniezka(5,5,0.02)



Można by się pokusić o obracającą się snieżkę:

def rot(f):
    while True:
        f.rotate(angle=radians(1), axis=(0,1,0))
        rate(50)

s = sniezka(4,5,0.02)
rot(s)



Znalazłem nawet praktyczne zastosowanie dla wyprodukowanego śniegu:


    Brak komentarzy:

    Prześlij komentarz