Sierpinski-Dreieck

Code-Stücke können hier veröffentlicht werden.
Antworten
Crush
User
Beiträge: 44
Registriert: Montag 1. Mai 2006, 11:32

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
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

Öhm, was sind Image und ImageDraw für Module beziehungsweise, woher bekomm ich die? Ohne geht es schlecht...
Crush
User
Beiträge: 44
Registriert: Montag 1. Mai 2006, 11:32

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
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

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).
BlackJack

In `punkte()` braucht man `image` wegen diesem hässlichen ``exec`` Hack.
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

Ok, aber wenn man math nicht übergibt, braucht man image auch nicht übergeben ;)
BlackJack

Ups, ich ging nicht davon aus, das `image` global ist. Noch so eine Hässlichkeit.
Crush
User
Beiträge: 44
Registriert: Montag 1. Mai 2006, 11:32

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
Zuletzt geändert von Crush am Freitag 25. August 2006, 12:56, insgesamt 2-mal geändert.
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

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.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

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
Crush
User
Beiträge: 44
Registriert: Montag 1. Mai 2006, 11:32

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
Crush
User
Beiträge: 44
Registriert: Montag 1. Mai 2006, 11:32

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
Antworten