Seite 1 von 1

Sierpinski-Dreieck

Verfasst: Donnerstag 24. August 2006, 18:14
von Crush
Hallo

Heute lernte ich in der Schule einiges über das Sierpinski-Dreieck. Danach hatten wir einen "Sierpinski-Dreiecks-Generator" mit Visual Basic geschrieben. Das hatte mir dann nicht besonders gefallen, also habe ich das Skript in Python neu geschrieben und noch etwas optimiert. Ist zwar nicht viel und ich wüsste nicht, wem es etwas nutzen könnte, trotzdem poste ich es mal (das Unterforum heisst ja nicht umsonst "Codesnippets"). :) Falls irgendwas grobes (Stilistisch oder so) drin ist wäre ich ausserdem froh, darauf hingewiesen zu werden. :D

Code: Alles auswählen

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

import Image
import ImageDraw
import random
import sys

# Gegebene Daten
groesse = (800, 800)
image = Image.new("1", groesse, color=255)
data = ((0,image.size[1]),
		(image.size[0],image.size[1]),
		(image.size[0]/2, 0)
		)
x, y = (0.4 * image.size[0], 0.8 * image.size[1])
math = ['x, y  = (x/2, (image.size[1] + y)/2)',
			'x, y = (((image.size[0]/2)+x)/2, y/2)',
			'x, y = ((image.size[0]+x)/2, (image.size[1]+y)/2)']

anzahl = 10000

draw = ImageDraw.Draw(image)

def dreieck(image):
	draw.line((data[0], data[1]),fill=0)
	draw.line((data[1], data[2]),fill=0)
	draw.line((data[0], data[2]),fill=0)
	return True

def punkte(image):
	draw.point((x,y), fill=0)

	for i in range(anzahl):
		b = random.randrange(0,3)
		exec math[b]
		draw.point((x,y), fill=0)

dreieck(image)
punkte(image)
image.show()
Crush

Verfasst: Donnerstag 24. August 2006, 19:43
von Nirven
Öhm, was sind Image und ImageDraw für Module beziehungsweise, woher bekomm ich die? Ohne geht es schlecht...

Verfasst: Donnerstag 24. August 2006, 19:56
von Crush
Nirven hat geschrieben:Öhm, was sind Image und ImageDraw für Module beziehungsweise, woher bekomm ich die? Ohne geht es schlecht...
achso, sorry, vergass ich zu erwähnen: Image und ImageDraw kommen aus der "Python Imaging Library (PIL)" und ist hier erhältich: http://www.pythonware.com/products/pil/

Crush

Verfasst: Freitag 25. August 2006, 07:28
von Nirven
Ja, damit funktioniert es.

Die Funktionsaufrufe sind allerings etwas komisch. Du übergibst an beide Funktionen image, obwohl das als einziges in keinem von beidem benötigt wird. So wie es aufgebaut ist funktioniert es trotzdem, aber dann kannst du dir auch die Übergabe von image sparen.

Besser wäre es wohl, die richtigen Variablen zu übergeben (draw und data für dreieck() sowie draw, anzahl, x, y, und math für punkte).

Verfasst: Freitag 25. August 2006, 10:40
von BlackJack
In `punkte()` braucht man `image` wegen diesem hässlichen ``exec`` Hack.

Verfasst: Freitag 25. August 2006, 10:42
von Nirven
Ok, aber wenn man math nicht übergibt, braucht man image auch nicht übergeben ;)

Verfasst: Freitag 25. August 2006, 10:46
von BlackJack
Ups, ich ging nicht davon aus, das `image` global ist. Noch so eine Hässlichkeit.

Verfasst: Freitag 25. August 2006, 11:59
von Crush
So ich habe das nun nochmals angeschaut und nach euren Hinweisen verändert. Es war etwas blöd, für das neu erstellte Bild sowie für das zu weitergebende "image" zu verwenden. Das habe ich jetzt geändert. Ausserdem werden nun auch die anderen Dinge an die Funktionen gegeben (ich habe auch die namen etwas verändert ...).

Bezüglich exec-Hack: wie könnte ich das anders lösen? Mir fällt dazu nichts ein ausser drei "if b == 1/2/3" Blöcke, was ich etwas aufwändiger finde... das geht doch sicher irgendwie einfacher :)

Code: Alles auswählen

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

import Image
import ImageDraw
import random
import sys

#----------------------------------------------------------
def show(image):
	image.show()
	return True

def dreieck(image, data, draw):
	draw.line((data[0], data[1]),fill=0)
	draw.line((data[1], data[2]),fill=0)
	draw.line((data[0], data[2]),fill=0)
	return True

def punkte(image, draw, (x,y), math, i):
	draw.point((x,y), fill=0)

	for i in range(i):
		b = random.randrange(0,3)
		x,y = math[b](x,y,image)
		draw.point((x,y), fill=0)

def draw(image):
	return ImageDraw.Draw(image)
#----------------------------------------------------------

bild = Image.new('1', (800, 800), color=255)

anzahl = 100000

#rechnungen = ['x, y  = (x/2, (image.size[1] + y)/2)',
#		'x, y = (((image.size[0]/2)+x)/2, y/2)',
#		'x, y = ((image.size[0]+x)/2, (image.size[1]+y)/2)']

rechnungen = [lambda x,y,image: (x/2, (image.size[1] + y)/2),
		lambda x,y,image: (((image.size[0]/2)+x)/2, y/2),
		lambda x,y,image: ((image.size[0]+x)/2, (image.size[1]+y)/2)]

daten = ((0, bild.size[1]),
	(bild.size[0], bild.size[1]),
	(bild.size[0]/2, 0))

startx, starty = 0.4*bild.size[0], 0.8*bild.size[1]


p = draw(bild)
dreieck(bild, daten, p)
punkte(bild, p, (startx, starty), rechnungen, anzahl)
show(bild)
Crush

edit: in der Funktion punkte() habe ich in der for Schleife das "anzahl" durch "i" ersetzt (siehe Post unten).

edit2: Ergänzungen

Verfasst: Freitag 25. August 2006, 12:10
von Nirven
In punkte() wird zwar anzahl übergeben, aber dann nicht benutzt. Du nimmst i als Referenz auf anzahl, benutzt aber anzahl in der Funktion.

Müsste also

Code: Alles auswählen

def punkte(image, draw, (x,y), math, anzahl):
heißen.

Verfasst: Freitag 25. August 2006, 12:41
von rayo
Hi

Damit du kein exec brauchst würde ich dir folgendes Vorschlagen:

Code: Alles auswählen

rechnungen = [lambda x,y,image: (x/2, (image.size[1] + y)/2),
        lambda x,y,image: (((image.size[0]/2)+x)/2, y/2),
        lambda x,y,image: ((image.size[0]+x)/2, (image.size[1]+y)/2)]

def punkte(image, draw, (x,y), math, i):
    draw.point((x,y), fill=0)

    for i in range(i):
        b = random.randrange(0,3)
        x,y = math[b](x,y,image)
        draw.point((x,y), fill=0) 
somit hast du eine Liste von 3 Funktionen die du dann in der Funktion punkte nur noch aufrufen musst.

Gruss

Verfasst: Freitag 25. August 2006, 12:57
von Crush
danke rayo, hatte zwar auch schon über lambda's gelesen und diese angewendet, doch hier sind sie mir einfach nicht eingefallen. und Nirven, deinen Hinweis habe ich auch zur Kenntnis genommen und ergänzt. Jetzt sieht das ganze schon ein wenig angenehmer aus. :)

Crush

Verfasst: Freitag 15. September 2006, 18:06
von Crush
Hallo miteinander,

Hier nun eine Möglichkeit, die Dreiecke direkt zu zeichnen und nicht wie beim "Chaos-Game" oben mithilfe von zufälligen Punkten. Kritik nehme ich gerne entgegen, da ich noch nicht lange in Python programmiere. Das Skript setzt, wie schon das vorherige, die PIL voraus, welche man unter http://www.pythonware.com/products/pil/ beziehen kann.

Hier nun der Code:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
sierpinski.py -- Modul um mittels PIL ein Sierpinski-Dreieck beliebiger
Stufentiefe zu erzeugen.
'''

import Image
import ImageDraw

def ground(data, draw):
    '''
    Diese funktion zeichnet das Grunddreieck aufgrund der übergegeben
    Daten ("data").
    '''
    draw.line((data[0], data[1]), fill=0)
    draw.line((data[1], data[2]), fill=0)
    draw.line((data[0], data[2]), fill=0)
    return True

def do_stages(data, stages, draw, i):
    '''
    Mit jedem Aufruf dieser funktion werden neue Punkte berechnet und
    ein Dreieck gezeichnet. Falls die erwünschte Tiefe noch nicht erreicht
    ist, ruft sich die Funktion dreimal rekursiv auf, um weitere Stufen und
    Dreiecke zu zeichnen.
    '''
    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))
    
    draw.line((data[0], data[1]), fill=0)
    draw.line((data[1], data[2]), fill=0)
    draw.line((data[0], data[2]), fill=0)
    
    i += 1
    if i <= stages:
        do_stages((data[0], data2[0], data2[2]), stages, draw, i)
        do_stages((data[1], data2[0], data2[1]), stages, draw, i)
        do_stages((data[2], data2[1], data2[2]), stages, draw, i)

def draw(image):
    return ImageDraw.Draw(image)

def show(image):
    'Zeige Bild'
    image.show()
    return True

# Startpunkte (x-Koordinate, y-Koordinate)
daten = ((0, 800),
        (800, 800),
        (400, 0))

# Differenz ergibt anzahl Stufen, die gezeichnet werden.
stufen = (0, 6)

# Bild erstellen, "Zeichner" definieren.
bild = Image.new('1', (daten[1]), color=255)
drawer = draw(bild)

if __name__ == '__main__':
    ground(daten, drawer)
    do_stages(daten, stufen[1], drawer, stufen[0])
    #bild.save('foo.png')
    show(bild)
Gruss Crush