Geometrisch konstruierbare Fraktale

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Crush
User
Beiträge: 44
Registriert: Montag 1. Mai 2006, 11:32

Hallo miteinander

Ich habe mich ein wenig mit geometrisch konstruierbaren Fraktalen auseinandergesetzt (wie z.B. das Sierpinski-Dreieck in einem anderen Thread). In der "fractals.py" befinden sich die Klassen der einzelnen Fraktale. Die Instanzen habe ich dann jedoch in der Datei "test_fractals.py" erstellt. da mir das ganze so übersichtlicher erschien. Natürlich könnte man die Instanzen auch direkt in der "fractals.py" erstellen...

Voraussetzung ist hier die Python Imaging Library (PIL), die unter http://www.pythonware.com/products/pil/ erhältlich ist. Entwickelt wurde das ganze unter Python 2.4, getestet mit den Versionen 2.4 und 2.5.

Um die verschiedenen Fraktale zu generieren kann man die einzelnen Blöcke in der "test_fractals.py" ent-kommentieren (wie heisst das Gegenteil von "auskommentieren? :D). So wie ich das jetzt poste wird mit dem Aufruf von "test_fractals.py" eine Kochsche Schneeflocke mit der Stufentiefe 3 generiert.

fractals.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#      fractals.py
#
#      Copyright 2006 Crush
#
#      This program is free software; you can redistribute it and/or modify
#      it under the terms of the GNU General Public License as published by
#      the Free Software Foundation; either version 2 of the License, or
#      (at your option) any later version.
#
#      This program is distributed in the hope that it will be useful,
#      but WITHOUT ANY WARRANTY; without even the implied warranty of
#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#      GNU General Public License for more details.
#
#      You should have received a copy of the GNU General Public License
#      along with this program; if not, write to the Free Software Foundation,
#      Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#

'''
fractals.py -- foobar
'''

import Image
import ImageDraw

class Fractal:
    '''
    Die Klasse Fractal stellt die Grundmethoden fuer die verschiedenen
    Subklassen bereit.
    '''
    def __init__(self, size, data):
        self.data = data
        self.image = Image.new('1', size, color = 255)
        self.draw = ImageDraw.Draw(self.image)
    
    #def new_image(self):
    #    '''
    #    Mit dieser Methode wird ein neues Bild mit den Spezifikationen aus
    #    self.data[1] erstellt.
    #    '''
    #    self.image = Image.new('', (self.data[1]), color = 255)
        
    def save(self, filename):
        '''
        Speichern des Bildes.
        '''
        self.image.save(filename)
        print 'saved as: %s' % filename
        return True
    
    def show(self):
        '''docstring'''
        self.image.show()
        return True

class Sierpinski(Fractal):
    '''
    Klasse, die ein Sierpinski-Dreieck berechnen und zeichnen kann.
    '''
    def __init__(self, size, data):
        Fractal.__init__(self, size, data)
    
    def generate(self, n):
        '''
        Erstellen des Fraktals mit der Rekursionstiefe n.
        '''
        self.draw.line((self.data[0], self.data[1]), fill=0)
        self.draw.line((self.data[1], self.data[2]), fill=0)
        self.draw.line((self.data[0], self.data[2]), fill=0)
        
        if n > 0:
            self.stages(self.data, 0, n)
    
    def stages(self, data, i, n):
        '''docstring'''
        x1 = (data[0][0] + data[1][0]) / 2.
        y1 = (data[0][1] + data[1][1]) / 2.
        
        x2 = (data[1][0] + data[2][0]) / 2.
        y2 = (data[1][1] + data[2][1]) / 2.
        
        x3 = (data[2][0] + data[0][0]) / 2.
        y3 = (data[2][1] + data[0][1]) / 2.
        
        data2 = ((x1, y1), (x2, y2), (x3, y3))
        
        self.draw.line((data[1], data[2]), fill=0)
        
        i += 1
        if not i-1 == n:
            self.stages((data[0], data2[0], data2[2]), i, n)
            self.stages((data[1], data2[0], data2[1]), i, n)
            self.stages((data[2], data2[1], data2[2]), i, n)
        
        
        return True

class Peano(Fractal):
    '''
    Die Klasse Peano zeichnet eine Peano-Kurve.
    '''
    def __init__(self, size, data):
        Fractal.__init__(self, size, data)

        
    def generate(self, n):
        '''docstring'''
        #self.n = n
        self.draw.line((self.data[0], self.data[1]), fill=0)
        
        if n:
            self.stages(self.data, 0, n)
    
    def calc_points(self, data):
        '''
        So sind die Punkte beschriftet (ASCII-Art, Monospace-Font verwenden!):
        
        
              p3----------p5
                |        |
                |        |
        p1   p34|        |p56     p2
        ---------------------------
                |        |
                |        |
                |        |
              p4----------p6
        '''
        
        math = (
            #p34
            (lambda x, y: (2./3.)*x + (1./3.)*y),
            #p3, p5
            (
                (lambda x, y, z: x - y + z),
                (lambda x, y, z: x + y - z)
            ),
            #p4, p6
            (
                (lambda x, y, z: x + y - z),
                (lambda x, y, z: x - y + z)
            ),
            #p56
            (lambda x, y: (1./3.)*x + (2./3.)*y)
        )
        
        (p1x, p1y), (p2x, p2y) = data
        
        p34x = math[0](p1x, p2x)
        p34y = math[0](p1y, p2y)
        
        p3x = math[1][0](p34x, p34y, p1y)
        p3y = math[1][1](p34y, p34x, p1x)
        
        p4x = math[2][0](p34x, p34y, p1y)
        p4y = math[2][1](p34y, p34x, p1x)
        
        p56x = math[3](p1x, p2x)
        p56y = math[3](p1y, p2y)
        
        p5x = math[2][0](p56x, p56y, p2y)
        p5y = math[2][1](p56y, p56x, p2x)
        
        p6x = math[1][0](p56x, p56y, p2y)
        p6y = math[1][1](p56y, p56x, p2x)
        
        p1, p2 = (p1x, p1y), (p2x, p2y)
        
        p34, p56 = (p34x, p34y), (p56x, p56y)
        
        p3, p4, p5, p6 = (p3x, p3y), (p4x, p4y), (p5x, p5y), (p6x, p6y)
        
        return (p1, p2, p34, p56, p3, p4, p5, p6)
    
    def stages(self, data, i, n):
        '''docstring'''
        p1, p2, p34, p56, p3, p4, p5, p6 = self.calc_points(data)
        
        self.draw.line((p3, p4), fill=0)
        self.draw.line((p3, p5), fill=0)
        self.draw.line((p4, p6), fill=0)
        self.draw.line((p5, p6), fill=0)
        
        i += 1
        if not i == n:
            self.stages((p1, p34), i, n)
            self.stages((p34, p3), i, n)
            self.stages((p34, p4), i, n)
            self.stages((p34, p56), i, n)
            self.stages((p56, p5), i, n)
            self.stages((p56, p6), i, n)
            self.stages((p3, p5), i, n)
            self.stages((p4, p6), i, n)
            self.stages((p2, p56), i, n)
            
        return True

class KochCurve(Fractal):
    '''docstring'''
    def __init__(self, size, data):
        Fractal.__init__(self, size, data)
    
    def generate(self, n):
        '''docstring'''
        
        self.draw.line((self.data[0], self.data[1]), fill=0)
        
        if n > 0:
            self.stages(self.data, 0, n)
    
    def calc_points(self, data):
        '''docstring'''
        c = (3**(1./2.))*(1./2.)
        math = (
            #p3
            (
                lambda x, y: (1./2.)*x + (1./2.)*y
            ),
            #p4
            (
                lambda x, y: (2./3.)*x + (1./3.)*y
            ),
            #p6
            (
                lambda x, y: (1./3.)*x + (2./3.)*y
            ),
            #p5
            (
                (lambda x, y, z: x + c*(y - z)),
                (lambda x, y, z: x - c*(y - z))
            )   
        )
        
        (p1x, p1y), (p2x, p2y) = data
        
        p3x = math[0](p1x, p2x)
        p3y = math[0](p1y, p2y)
        
        p4x = math[1](p1x, p2x)
        p4y = math[1](p1y, p2y)
        
        p6x = math[2](p1x, p2x)
        p6y = math[2](p1y, p2y)
        
        p5x = math[3][0](p3x, p6y, p4y)
        p5y = math[3][1](p3y, p6x, p4x)
        
        p1, p2 = (p1x, p1y), (p2x, p2y)
        
        p4, p5, p6 = (p4x, p4y), (p5x, p5y), (p6x, p6y)
        
        return (p1, p2, p4, p5, p6)
    
    def stages(self, data, i, n):
        '''docstring'''
        p1, p2, p4, p5, p6 = self.calc_points(data)
        
        self.draw.line((p1, p2), fill=255)
        self.draw.line((p1, p4), fill=0)
        self.draw.line((p6, p2), fill=0)
        self.draw.line((p4, p5), fill=0)
        self.draw.line((p5, p6), fill=0)

        i += 1
        if not i == n:
            self.stages((p1, p4), i, n)
            self.stages((p4, p5), i, n)
            self.stages((p5, p6), i, n)
            self.stages((p6, p2), i, n)

        return True

class KochSnowflake(Fractal, KochCurve):
    '''docstring'''
    def __init__(self, size, data):
        KochCurve.__init__(self, size, data)

    def generate(self, n):
        '''docstring'''
        
        self.draw.line((self.data[0], self.data[1]), fill=0)
        self.draw.line((self.data[0], self.data[2]), fill=0)
        self.draw.line((self.data[1], self.data[2]), fill=0)
        
        if n:
            self.stages((self.data[1], self.data[0]), 0, n)
            self.stages((self.data[2], self.data[1]), 0, n)
            self.stages((self.data[0], self.data[2]), 0, n)
test_fractals.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import fractals

x = 1000
y = (3**(1./2.))/2 * x

size_sp = x, int(y+1)

data_sp = ((0, y),
        (x, y),
        ((x/2), y-(x/2)*3**(0.5)))

size_pe = 900, 900
data_pe = ((100,300), (800, 700))


kx = 1200
ky = kx/2

data_k = ((0, ky/2), (kx, ky))

size_ks = (1000,)*2
x, y = ((3/4.)*size_ks[0],)*2
data_ks = ((100, y-100),
        (x+100, y-100),
        ((x/2)+100, -100+y-(x/2)*3**(0.5)))

if __name__ == '__main__':
    #sp = fractals.Sierpinski(size_sp, data_sp)
    #sp.generate(3)
    #sp.show()
    
    #pe = fractals.Peano(size_pe, data_pe)
    #pe.generate(3)
    #pe.show()
    
    #k = fractals.KochCurve((kx, ky), data_k)
    #k.generate(3)
    #k.show()
    
    ks = fractals.KochSnowflake(size_ks, data_ks)
    ks.generate(3)
    ks.show()
    
    print 'done'
Kritik nehme ich gerne entgegen (da ich noch nicht lange programmiere und daher froh bin, auf Fehler aufmerksam gemacht zu werden).

Ach ja: Dokumentiert ist das ganze noch nicht wirklich toll, teilweise habe ich einfach ein "'''docstring'''" eingefügt, das mich daran erinnern sollte, das irgendwann mal noch richtig zu dokumentieren ...

Crush

Edit:
hier noch ein Bild um das beschriebene zu veranschaulichen:
Bild
thelittlebug
User
Beiträge: 188
Registriert: Donnerstag 20. Juli 2006, 20:46
Wohnort: Wien
Kontaktdaten:

hi du,

das zerpflücken des python codes lass ich mal den leuten die python koennen :) ich will dir nur sagen das zumindest die letzten beiden systeme mit einem allgemeinen lindenmayer system flexibler zu erstellen sind.
für nähere infos zum lindenmayer system folge diesem link unauffällig ;)
http://www.fh-lueneburg.de/mathe-lehram ... fs/ifs.htm

das sollte grob geschätzt genausoviel code brauchen, kann aber wesentlich mehr fraktale berechnen.

lgherby

€: grad ausprobiert, 15 zeilen code, feig wie ich bin hab ich ein turtle zum zeichnen bemüht :)
Antworten