Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

ws1617:optimales_puppenhaus

Optimales Puppenhaus

von Cedric Mans, Lea Harz, Lucas Enzmann, Maike Herkenrath, Maleen Ahrens, Sönke Roos

Unser Projekt „Optimales Puppenhaus“ hat die dreidimensionale Generierung eines Hauses gemäß den Anforderungen des Nutzers (Baugrundfläche etc.) und gewisser Optimalitätskriterien zum Ziel. Als optimal wird beispielsweise die Ausrichtung der Fenster für einen vorteilhaften Lichteinfall, die Verhältnisse der Raummaße gemäß des goldenen Schnittes, aber auch ganz banale Dinge1) wie die Position von Schränken mit einem ausreichenden Abstand zueinander, sodass der eine Schrank nicht die Öffnung der Tür des anderen behindert, angesehen. Ziel des Projekts ist der 3D-Druck eines nach unseren Maßstäben optimalen kleinen Puppenhauses.

!!! WORK IN PROGRESS !!!

Dokumentation

Intro

Unser Projekt hat sich zum Ziel gesetzt den Grundriss einer Wohnung nach unterschiedlichen Kriterien zu optimieren. Die Natur dieser Kriterien waren uns zuerst unklar. Wir hatten unterschiedliche Ideen, wussten aber nicht welche technisch realisierbar waren. Die Ergebnisse sollten als 3D-Modell dargestellt werden um anschließend von einem 3D-Drucker realisiert werden zu können.

Projektplanung

In den ersten Wochen haben wir uns unterschiedliche Teilziele definiert, wovon sich aber die meisten als unrealistisch herausgestellt haben. Schlussendlich haben wir uns die drei dimensionale Visualisierung eines nach unterschiedlichen Kriterien optimierten Raumes als Endziel gesetzt. Als Zwischenziele haben wir uns die einzelnen Objekte vorgenommen, aus welche ein Raum sich zusammensetzt.

Nach einigen Wochen haben wir unsere größere Gruppe von sechs Personen, in zwei Untergruppen aufgeteilt. Die Triangulierungs-Gruppe war verantwortlich für die Konvertion eines generierten 3D-Objektes, in ein geometrisch vergleichbares Objekt, bestehend ausschließlich aus der Zusammensetzung flacher Dreiecke. Dadurch werden überflüssige Trennwände entfernt, wodurch es möglich wird das Objekt mittels eines 3D-Druckers auszudrucken.

Die andere Gruppe ist Verantwortlich für die Visualisierung und den Optimierungs-Algorithmus. Die Visualisierung besteht aus einer 3-Dimensionalen Darstellung der Wände welches mittels dem Modul „Vpython“ ermöglicht wird. Der Optimierungs-Algorithmus soll die Maße eines Raumes unter dem Einfluss unterschiedlicher Bedingungen und Einschränkungen berechnen. Für die Optimierung benutzen wird die „fmin_cobyla“ Funktion aus der „scipy.optimize“ Bibliothek. Für weitere mathematsiche Operationen benutzen wir das Modul „numpy“.

Projektverlauf

Visualisierung

Um einen Raum 3-Dimensional darzustellen, haben wir die einzelnen Bestandteile eines Raumen in unabhängige Objekte eingeteilt. Das einfachste Objekt ist das „Wall-Objekt“ aus dem kompliziertere Objekte wie das „Fensterwand-Objekt“ und das „Türwand-Objekt“ zusammengesetzt werden.

Wall-Objekt

from __future__ import division
from __future__ import print_function
from addons import dist # Distance between two points function
from visual import *
 
display(background=(0, 0, 0)) # Black Background
 
class Wall(object):
 
	standard_wall_height = 3.0
 
	standard_wall_thickness = 0.2
 
	def __init__(self, position, ax, length, cl=(1,1,1)):
		self.position = position
		self.ax = ax
		self.cl = cl
		self.length = length
		self.box = box(pos = position, size = (length, self.standard_wall_height, self.standard_wall_thickness),
						 color = cl, axis = ax, opacity = 0.5)
		self.points = [
		(self.position[0] + 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[2] + 0.5 * self.standard_wall_thickness),
		(self.position[0] + 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[2] - 0.5 * self.standard_wall_thickness),
		(self.position[0] - 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[2] - 0.5 * self.standard_wall_thickness),
		(self.position[0] - 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[2] + 0.5 * self.standard_wall_thickness),
		(self.position[0] + 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[2] + 0.5 * self.standard_wall_thickness),
		(self.position[0] + 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[2] - 0.5 * self.standard_wall_thickness),
		(self.position[0] - 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[2] - 0.5 * self.standard_wall_thickness),
		(self.position[0] - 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[2] + 0.5 * self.standard_wall_thickness)
																																						]
		if self.ax == (0,0,1):
			self.points = [
			(self.position[2] + 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[0] + 0.5 * self.standard_wall_thickness),
			(self.position[2] + 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[0] - 0.5 * self.standard_wall_thickness),
			(self.position[2] - 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[0] - 0.5 * self.standard_wall_thickness),
			(self.position[2] - 0.5 * self.length, self.position[1] - 0.5 * self.standard_wall_height, self.position[0] + 0.5 * self.standard_wall_thickness),
			(self.position[2] + 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[0] + 0.5 * self.standard_wall_thickness),
			(self.position[2] + 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[0] - 0.5 * self.standard_wall_thickness),
			(self.position[2] - 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[0] - 0.5 * self.standard_wall_thickness),
			(self.position[2] - 0.5 * self.length, self.position[1] + 0.5 * self.standard_wall_height, self.position[0] + 0.5 * self.standard_wall_thickness)]
			for i in range(len(self.points)):
				self.points[i]= (self.points[i][2], self.points[i][1],self.points[i][0])
 
	def push(self, vector):	
		index = 0
		for point in self.points:
			self.points[index] = (point[0] + vector[0], point[1] + vector[1], point[2] + vector[2])
			index += 1
		self.box.pos = (self.position[0] + vector[0], self.position[1] + vector[1], self.position[2] + vector[2])
 
	def rotate(self, degree):
		index = 0
		for point in self.points:
			self.points[index] = (self.box.pos[0]+(point[0]-self.box.pos[0]) * math.cos(radians(degree)) - (point[2]-self.box.pos[2]) * math.sin(radians(degree)), point[1], 
			self.box.pos[2]+(point[0]-self.box.pos[0]) * math.sin(radians(degree)) + (point[2]-self.box.pos[2]) * math.cos(radians(degree)))
			index += 1
		self.box.rotate(angle=radians(degree),axis=(0,1,0),pos=self.position)
 
	def getPoints(self):
		return self.points

Ein Wall-Objekt ist im Prinzip nichts anderes als ein flacher Quader, welche sich die Koordinaten seiner acht Eckpunkte merkt. Beim initiieren eines Wall-Objektes muss ein Mittelpunkt, eine Achse und eine Länge der Wand angegeben werden. Mithilfe dieser Informationen können die entsprechenden Eckpunkte ausgerechnet werden. Darüberhinaus besitzt jedes Wall-Objekt auch noch die Methoden push(), rotate() und getPoints(). Die push() und rotate() Methoden bewegen das Objekt im Raum und berechnet die neue Positionen der Eckpunkte. Dadurch ist sich jedes Wall-Objekt seinen eigenen Punkten zu jeder Zeit und in jeder Position bewusst. Die getPoints() Methode erlaubte uns die Koordinaten jeder der Eckpunkte abzurufen.

Tuerwand-Objekt

from __future__ import division
from __future__ import print_function
from wall import Wall
from visual import *
from errors import *
 
display(background=(0, 0, 0)) # Black Background
 
class Tuerwand(object):
 
	# Liste welche alle waende speichert
	walls = []
 
	standard_wall_height = Wall.standard_wall_height
 
	def __init__(self, position, axis, xlength, color = color.white, ratio = 0.3):
		self.position = position
		self.axis = axis
		self.xlength = xlength
		self.color = color
		self.ratio = ratio
		self.structure_wall()
 
	def getPoints(self):
		return [wall.getPoints() for wall in self.walls]		
 
	def structure_wall(self):
 
		door_width = 1.0
		door_height = 2.0
 
		remaining_length = self.xlength - door_width
 
		l_length = self.ratio * (remaining_length / (self.ratio + 1))
		r_length = remaining_length - l_length
 
 
		# Kreation der Wand im Falle Paralell zur x-Achse
		if self.axis == (1, 0, 0):
 
			self.position = (self.position[0], self.position[1], self.position[2])
 
			pos_1 = (self.position[0] + ((-self.xlength / 2) + (l_length / 2)), self.position[1], self.position[2])
			pos_2 = (pos_1[0] + (0.5 * l_length) + 0.5 * door_width, pos_1[1] + (-self.standard_wall_height/2 + door_height + 0.5 * (self.standard_wall_height - door_height)), pos_1[2])
			pos_3 = (pos_2[0] + 0.5 * door_width + 0.5 * r_length, pos_1[1], pos_2[2])
 
			self.walls.append(Wall(pos_1, self.axis, l_length, self.color))
 
			Wall.standard_wall_height = Wall.standard_wall_height - door_height
			self.walls.append(Wall(pos_2, self.axis, door_width, self.color))
			Wall.standard_wall_height = 3.0
 
			self.walls.append(Wall(pos_3, self.axis, r_length, self.color))
 
		# Kration des Raumes im Falle parallel zur z-Achse
		elif self.axis == (0, 0, 1):
 
			self.position = (self.position[0], self.position[1], self.position[2])
 
			pos_1 = (self.position[0], self.position[1], self.position[2] + ((-self.xlength / 2) + (l_length / 2)))
			pos_2 = (pos_1[0], pos_1[1] + (-self.standard_wall_height/2 + door_height + 0.5 * (self.standard_wall_height - door_height)), pos_1[2] + (0.5 * l_length) + 0.5 * door_width)
			pos_3 = (pos_1[0], pos_1[1], pos_2[2] + 0.5 * door_width + 0.5 * r_length)
 
			self.walls.append(Wall(pos_1, self.axis, l_length, self.color))
 
			Wall.standard_wall_height = Wall.standard_wall_height - door_height
			self.walls.append(Wall(pos_2, self.axis, door_width, self.color))
			Wall.standard_wall_height = 3.0
 
			self.walls.append(Wall(pos_3, self.axis, r_length, self.color))
 
		else:
			raise AxisError("Illegal Axis; Only X and Y Axis with the value 1 supported")			

Unser Tuerwand-Objekt wird aus drei Wall-Objekten zusammengesetzt. Da jedes Tuerwand Objekt aus Wall-Objekten zusammengesetzt ist, kann man über die „Wall.getPoints()“ Methode, auch alle Koordinaten der Eckpunkte des Tuerwand-Objektes abrufen. Beim innizieeren eines Tuerwand-Objektes muss die Position, die Achse und die Länge angegeben werden. Falls man die Position der Türen beeinflussen will, gibt es die Möglichkeit ein „ratio“ Parameter anzugeben. Der ratio-parameter gibt an, in welchem Verhältnis die Wand links von der Tür, zu der Wand rechts von der Tür steht. Würde man zum Beispiel eine Tür in der Mitte der Wand haben wollen, so müsste man ein ratio von 1 angeben. Wir haben uns für ein standard-ratio von 0.3 entschieden, für eine möglichst natürlich wirkende Türposition. Die methode structure_wall() ist verantwortlich für das kreeiren der einzelnen Wände in den korrekten Positionen und setzt das gesamte Tuerwand-Objekt zusammen.

Fenster-Objekt

from __future__ import division
from __future__ import print_function
from visual import *
from wall import Wall
 
display(background=(0, 0, 0))
 
class Fensterwand(object):
 
	standard_wall_height = Wall.standard_wall_height
 
	walls = []
 
	def __init__(self, length, position, axis, window_size = 1/5, color = color.white, ratio = 1):
		self.length = length
		self.ratio = ratio
		self.position = position
		self.axis = axis
		self.window_size = window_size
		self.color = color
		self.structure_wall()
 
	def getPoints(self):
		return [wall.getPoints() for wall in self.walls]
 
	def structure_wall(self):
 
		fhr = 1 #Fensterhoeheratio
		fb = (Wall.standard_wall_height - fhr) / 2 #Fensterbankhoehe
 
		window_width = self.window_size * self.length
		window_height = (1/2) * self.standard_wall_height - fb
 
		remaining_length = self.length - window_width
 
		l_length = self.ratio * (remaining_length / (self.ratio + 1))
		r_length = remaining_length - l_length
 
		if self.axis == (1, 0, 0):
 
			# Bestimmung der Positionen
			self.pos_1 = (self.position[0] - 0.5 * window_width - 0.5 * l_length, self.position[1], self.position[2])
			self.pos_3 = (self.position[0] + 0.5 * window_width + 0.5 * r_length, self.position[1], self.position[2])
 
			self.pos_2 = (self.position[0], self.position[1] - 0.5 * fhr - 0.5 * fb, self.position[2])
			self.pos_4 = (self.position[0], self.position[1] + 0.5 * fhr + 0.5 * fb, self.position[2])
 
			self.pos_5 = self.position
 
			# Kreeiren der Waende mit gleicher Hoehe
			self.walls.append(Wall(self.pos_1, self.axis, l_length, self.color))
 
			# Kreeiren der Waende mit gleicher Hoehe
			self.walls.append(Wall(self.pos_3, self.axis, r_length, self.color))
 
			Wall.standard_wall_height = fb
 
			# Kreeiren der Waende mit einer hoehe abweichend vom Standard
			self.walls.append(Wall(self.pos_2, self.axis, window_width, self.color))
 
			# Kreeiren der Waende mit einer hoehe abweichend vom Standard
			self.walls.append(Wall(self.pos_4, self.axis, window_width, self.color))
 
			Wall.standard_wall_height = 3.0
 
		elif self.axis == (0, 0, 1):
 
			# Bestimmung der Positionen
			self.pos_1 = (self.position[0], self.position[1], self.position[2] - 0.5 * window_width - 0.5 * l_length)
			self.pos_3 = (self.position[0], self.position[1], self.position[2] + 0.5 * window_width + 0.5 * r_length)
 
			self.pos_2 = (self.position[0], self.position[1] - 0.5 * fhr - 0.5 * fb, self.position[2])
			self.pos_4 = (self.position[0], self.position[1] + 0.5 * fhr + 0.5 * fb, self.position[2])
 
			self.pos_5 = self.position
 
			# Kreeiren der Waende mit gleicher Hoehe
			self.walls.append(Wall(self.pos_1, self.axis, l_length, self.color))
 
			# Kreeiren der Waende mit gleicher Hoehe
			self.walls.append(Wall(self.pos_3, self.axis, r_length, self.color))
 
			Wall.standard_wall_height = fb
 
			# Kreeiren der Waende mit einer hoehe abweichend vom Standard
			self.walls.append(Wall(self.pos_2, self.axis, window_width, self.color))
 
			# Kreeiren der Waende mit einer hoehe abweichend vom Standard
			self.walls.append(Wall(self.pos_4, self.axis, window_width, self.color))
 
			Wall.standard_wall_height = 3.0
 
		else:
			print("Error: Invalid axis")

Ähnlich wie bei dem Tuerwand-Objekt, wird das Fensterwand-Objekt aus 4 unterschiedlichen Wall-Objekten zusammengesetzt. Beim initiieren eines Tuerwand-Objektes muss die Länge, Position und die Achse angegeben werden. Optional kann auch noch die Größe des Fensters wie auch seine Position mittel des ratio-Parameters angepasst werden.

Raum-Objekt

from tuerwand import Tuerwand
from fensterwand import Fensterwand
from errors import *
from visual import *
from sys import exit
 
 
display(background=(0, 0, 0)) # Black Background
 
class Room(object):
 
	def __init__(self, position=(0,0,0), xlength=10, zlength=5, door_pos=0, window_pos=0, color=color.white, door_ratio=0.3):
		self.position = vector(position)
		self.xlength = xlength 
		self.zlength = zlength 
		self.color = color
		self.door_pos = door_pos
		self.window_pos = window_pos
		self.door_ratio = door_ratio
		self.structure_room()
 
	def getPoints(self):
		return [wall.getPoints() for wall in [self.walls[key] for key in self.walls]]
 
	walls = {}
 
	def structure_room(self):
 
		self.zlength = self.zlength - Wall.standard_wall_thickness
 
		# Checks if fensterwand pos is equal to tuerwand pos
		if self.door_pos == self.window_pos and self.door_pos != 0:
			print("Error: Window position is equal to Door position")
			exit(0)
 
		# Marking the middle point
		sphere(radius = 0.3, color = color.red, pos = self.position)
 
		# Spawn parallel z-walls
		for i in range(2):
			self.walls[i] = Wall(self.position, (1, 0, 0), self.xlength - Wall.standard_wall_thickness, self.color)
 
		# Spawn parallel x-walls
		for i in range(2, 4):
			self.walls[i] = Wall(self.position, (1, 0, 0), self.zlength + Wall.standard_wall_thickness, self.color)
 
		# Push Parallel to x-axis
		for key in range(1, 3):
			self.walls[key - 1].push((0,0,(-1)**key * (0.5 * self.zlength)))
 
		# Push Parallel to z-axis
		for key in range(3, 5):
			self.walls[key - 1].push(((-1)**key * (0.5 * self.xlength),0,0))
			self.walls[key - 1].rotate(90)
 
		# Checks if a Door as been spawned
		door = False	
 
		# Door parallel to x-axis	
		for door_p in range(1, 3):
			if self.door_pos == door_p:
				temp_length = self.walls[door_p - 1].length
				pos_temp_wall = self.walls[door_p - 1].box.pos
				self.walls[door_p - 1].box.visible = False
				del self.walls[door_p - 1].box
				self.walls[door_p - 1] = Tuerwand(position=pos_temp_wall,axis=(1, 0, 0),xlength=temp_length,color=self.color,ratio=self.door_ratio)
				door = True
				break
 
		# Door parallel to z-axis	
		for door_p in range(3, 5):
			if self.door_pos == door_p: 
				temp_length = self.walls[door_p - 1].length
				pos_temp_wall = self.walls[door_p - 1].box.pos
				self.walls[door_p - 1].box.visible = False
				del self.walls[door_p - 1].box
				self.walls[door_p - 1] = Tuerwand(position=pos_temp_wall,axis=(0, 0, 1),xlength=temp_length, color=self.color, ratio=self.door_ratio)
				door = True
				break
 
		if not door and self.door_pos != 0:
			print("Error: No valid door parameter")
 
		# Checks if Windows have been spawned
		windows = False
 
		# Winows parallel to x-axis
		for window_p in range(1, 3):
			if self.window_pos == window_p:
				temp_length = self.walls[window_p - 1].length
				pos_temp_wall = self.walls[window_p - 1].box.pos
				self.walls[window_p - 1].box.visible = False
				del self.walls[window_p - 1].box
				self.walls[window_p - 1] = Fensterwand(length=temp_length, position=pos_temp_wall, axis=(1,0,0))
				windows = True
				break
 
		# Winows parallel to z-axis
		for window_p in range(3, 5):
			if self.window_pos == window_p:
				temp_length = self.walls[window_p - 1].length
				pos_temp_wall = self.walls[window_p - 1].box.pos
				self.walls[window_p - 1].box.visible = False
				del self.walls[window_p - 1].box
				self.walls[window_p - 1] = Fensterwand(length=temp_length, position=pos_temp_wall, axis=(0,0,1))
				windows = True
				break
 
		if not windows and self.window_pos != 0:
			print("Error: No valid window parameter")

Nachdem die Grundbausteine eines Raumes etabliert sind, werden sie im Raum-Objekt zusammengefasst. Beim initiieren eines Raum-Objekts muss eine x-Länge des Raumes, eine y-Länge des Raumes und jeweils eine Angabe welche Wand ein Tüerwand-Objekt sein soll bzw. ein Fensterwand-Objekt. Die structure_room() Methode kreeirt die jeweiligen Objekt-Bausteine in den entsprechenden Positionen.

Die Wall.getPoints() Methode dient als Schnittstelle zwischen der Visualisierungs Gruppe und der Triangulisierungs Gruppe.

Main-Visualisierung

Nachdem wir uns auf ein Layout für die Grundrisse der Wohnung geeinigt haben, bemerkten wir schnell das unsere ursprüngliche Idee jeden einzelnen Raum als eigenes Raum-Objekt zu initiieren, ineffizient und unpraktisch war. Angenommen alle 5 Räume (4 Räume + Gang) würden als eigene Raum-Objekte passend im Koordinatensystem nebeneinander initialisiert werden, so würde in bestimmten Fällen mehr als eine Wand die selben Räume trennen, wodurch Wände unnötig überlappen. Deshalb entschieden wir uns den Raum aus den einzelnen Raum-Bausteinen zusammenzusetzen. So besteht ein Raum mit zwei Fenstern und einer Tür aus zwei Fensterwand-Objekte, ein Tuerwand-Objekt und ein Wall-Objekt.

Die getStructure() Funktion, ruft mithilfe eine Moduls namens „subprocesses32“ den Algorithmus auf (siehe unten) welche eine Datenstruktur liefert, in der die optimierten Maße der Wohnung gespeichert sind. Diese wird anschließend ausgelesen in der structureBuilding Funktion und die einzelnen Wände werden entsprechend initialisiert.

Mithilfe der „Pickle“-Bibliothek werden alle relevanten Punkte der Wohnung zusammengefasst in eine „points.pickle“ Datei. Die Datenstruktur, „points.pickle“, bestehend aus den Koordinaten aller Eckpunkte, wird anschließend von dem Triangulisierungs-Algorithmus triangulisiert.

from visual import *
from raum import Room
from wall import Wall
from tuerwand import Tuerwand
from fensterwand import Fensterwand
import subprocess32 as sp
import ast
from pickle import Pickler
 
def getStructure():
	unparseObject = sp.check_output("python ../Algorithmus/main.py", shell=True)
	parsedObject = ""
	relevant = False
	for char in unparseObject:
		if char == '}':
			parsedObject += char
			relevant = False
			break
		if char == '{':
			relevant = True
		if relevant:
			parsedObject += char
	return parsedObject
 
points = []
 
def StructureBuilding(data):
 
	Y_HEIGHT = 0.5 * Wall.standard_wall_height
 
	x_len = data[1][0][0] + data[2][0][0]
	z_len = data[0][0][1] + data[1][0][1]
 
	mp = (0.5 * x_len, Y_HEIGHT, 0.5 *  z_len)
 
	aussenwaende = []
 
	aw_0_0_mp = (0.5 * data[0][0][0] + 0.25 * Wall.standard_wall_thickness, Y_HEIGHT, 0)
	aussenwaende.append(Fensterwand(length=data[0][0][0] - 0.5 * Wall.standard_wall_thickness, position=aw_0_0_mp, axis=(1,0,0), window_size=1/2))
	points.append(aussenwaende[0].getPoints())
 
	aw_1_mp = (0, Y_HEIGHT, 0.5 * z_len)
	aussenwaende.append(Tuerwand(position=aw_1_mp, axis=(0,0,1), xlength=z_len + Wall.standard_wall_thickness, ratio=50))
	points.append(aussenwaende[1].getPoints())
 
	aw_2_mp = (0.5 * x_len, Y_HEIGHT, z_len)
	aussenwaende.append(Wall(position=aw_2_mp,ax=(1,0,0),length=x_len - Wall.standard_wall_thickness))
	points.append(aussenwaende[2].getPoints())
 
	aw_3_1_mp = (x_len, Y_HEIGHT, data[2][0][1] + 0.5 * data[2][0][1])
	aussenwaende.append(Fensterwand(data[2][0][1] + Wall.standard_wall_thickness, aw_3_1_mp, axis = (0,0,1), window_size=1/2))
	points.append(aussenwaende[3].getPoints())
 
	aw_3_0_mp = (x_len, Y_HEIGHT, 0.5 * data[3][0][1])
	aussenwaende.append(Fensterwand(data[3][0][1] + Wall.standard_wall_thickness, aw_3_0_mp, axis=(0,0,1), window_size=1/2))
	points.append(aussenwaende[4].getPoints())
 
	aw_0_2_mp = (data[1][0][0] + 0.5 * data[3][0][0], Y_HEIGHT, 0)
	aussenwaende.append(Fensterwand(length=data[3][0][0], position=aw_0_2_mp, axis=(1,0,0), window_size=1/2))
	points.append(aussenwaende[5].getPoints())
 
	aw_0_1_mp = (data[0][0][0] + 0.5 * data[4][0][0], Y_HEIGHT, 0)
	aussenwaende.append(Fensterwand(data[4][0][0], aw_0_1_mp, axis=(1,0,0), window_size=1/2))
	points.append(aussenwaende[6].getPoints())
 
	wall_2_0_mp = (0.5 * data[0][0][0], Y_HEIGHT, data[0][0][1])
	wall_2_0 = Tuerwand(position=wall_2_0_mp,axis=(1,0,0),xlength=data[0][0][0] - Wall.standard_wall_thickness, ratio=1)
	points.append(wall_2_0.getPoints())
 
	wall_2_1_mp = (data[0][0][0] + 0.5 * data[4][0][0], Y_HEIGHT, data[0][0][1])
	wall_2_1 = Tuerwand(position=wall_2_1_mp,axis=(1,0,0),xlength=data[4][0][0] - Wall.standard_wall_thickness, ratio=1)
	points.append(wall_2_1.getPoints())
 
	wall_3_mp = (data[1][0][0] + 0.5 * data[2][0][0], Y_HEIGHT, data[3][0][1])
	wall_3 = Tuerwand(position=wall_3_mp,axis=(1,0,0),xlength=data[3][0][0] - Wall.standard_wall_thickness)
	points.append(wall_3.getPoints())
 
	wall_1_mp = (data[0][0][0], Y_HEIGHT, data[0][0][1] * 0.5 + 0.5 * Wall.standard_wall_thickness)
	wall_1 = Wall(position=wall_1_mp,ax=(1,0,0),length=data[0][0][1])
	wall_1.rotate(90)
	points.append(wall_1.getPoints())
 
	wall_4_0_mp = (data[1][0][0], Y_HEIGHT, 0.5 * data[4][0][1] + 0.5 * Wall.standard_wall_thickness)
	wall_4_0 = Wall(position=wall_4_0_mp, ax=(1,0,0), length=data[4][0][1])
	wall_4_0.rotate(90)
	points.append(wall_4_0.getPoints())
 
	wall_4_1_mp = (data[1][0][0], Y_HEIGHT, data[4][0][1] + 0.5 * data[1][0][1])
	wall_4_1 = Tuerwand(position=wall_4_1_mp,axis=(0,0,1),xlength=data[1][0][1] - Wall.standard_wall_thickness)
	points.append(wall_4_1.getPoints())
 
	Wall.standard_wall_height = 0.2
	Wall.standard_wall_thickness = z_len + 0.2
 
	boden_pos = (mp[0], -0.1, mp[2])	
	boden = Wall(position=boden_pos, ax=(1,0,0), length=x_len + 0.2)
 
	Wall.standard_wall_height = 3.0
	Wall.standard_wall_thickness = 0.2
 
StructureBuilding(ast.literal_eval(getStructure()))
 
with open("points.pickle", 'w') as f:
	p = Pickler(f)
	p.dump(points)
Algorithmus

Für die Optimierung bedienten wir uns der „scipy.optimize“ Bibliothek in der die Funktion „fmin_cobyla“ beinhaltet ist. fmin_cobyla minimisiert den Wert einer Funktion mittels Einschränkungen und Linearer Approxmiation (Constrained Optimization By Linear Approximation - COBYLA).

from __future__ import print_function
from scipy.optimize import fmin_cobyla
import numpy as np
 
def constrains():
	cons = []
	eps = 0
	GRUND_X = 10.0
	GRUND_Y = 5.0
	cons.append(lambda v:v[0] - eps) #(x0, y0, x1, y1, x2, y2, x3, y3, x4, y4)
	cons.append(lambda v:v[1] - eps) #(0,   1,  2 ,  3,  4,  5,  6, 7,  8,  9)
	cons.append(lambda v:v[2] - eps)
	cons.append(lambda v:v[3] - eps)
	cons.append(lambda v:v[4] - eps)
	cons.append(lambda v:v[5] - eps)
	cons.append(lambda v:v[6] - eps)
	cons.append(lambda v:v[7] - eps)
	cons.append(lambda v:v[8] - eps)
	cons.append(lambda v:v[9] - eps)
	cons.append(lambda v: -(v[1] + v[3] - GRUND_Y)) 
	cons.append(lambda v: -(v[9] + v[3] - GRUND_Y))
	cons.append(lambda v: -(v[7] + v[5] - GRUND_Y))
	cons.append(lambda v: -(v[0] + v[8] + v[6] - GRUND_X))
	cons.append(lambda v: -(v[0] + v[8] + v[4] - GRUND_X))
	cons.append(lambda v: -(v[2] + v[4] - GRUND_X))
	cons.append(lambda v: -(v[3] - 1.25))
	return cons
 
def flaeche_0(v):
	return v[0] * v[1]
 
def flaeche_1(v):
	return v[2] * v[3]
 
def flaeche_2(v):
	return v[4] * v[5]
 
def flaeche_3(v):
	return v[6] * v[7]
 
def flaeche_4(v):
	return v[8] * v[9]
 
 
def quadratizitaet_0(v):
	return ((v[0] * v[1]) / (v[0]*v[0] + v[1]*v[1]))
 
def quadratizitaet_1(v):
	return ((v[2] * v[3]) / (v[2]*v[2] + v[3]*v[3]))
 
def quadratizitaet_2(v):
	return ((v[4] * v[5]) / (v[4]*v[4] + v[5]*v[5]))
 
def quadratizitaet_3(v):
	return ((v[6] * v[7]) / (v[6]*v[6] + v[7]*v[7]))
 
def quadratizitaet_4(v):
	return ((v[8] * v[9]) / (v[8]*v[8] + v[9]*v[9]))
 
 
def goldener_schnitt_0(v):
	g = 1 / ((1 + np.sqrt(5)) / 2)
	return np.power(((v[0] / v[1]) - g), 2) * np.power((v[1] / v[0]) - g, 2)
 
def goldener_schnitt_1(v):
	g = 1 / ((1 + np.sqrt(5)) / 2)
	return np.power(((v[2] / v[3]) - g), 2) * np.power((v[3] / v[2]) - g, 2)
 
def goldener_schnitt_2(v):
	g = 1 / ((1 + np.sqrt(5)) / 2)
	return np.power(((v[4] / v[5]) - g), 2) * np.power((v[5] / v[4]) - g, 2)
 
def goldener_schnitt_3(v):
	g = 1 / ((1 + np.sqrt(5)) / 2)
	return np.power(((v[6] / v[7]) - g), 2) * np.power((v[7] / v[6]) - g, 2)
 
def goldener_schnitt_4(v):
	g = 1 / ((1 + np.sqrt(5)) / 2)
	return np.power(((v[8] / v[9]) - g), 2) * np.power((v[9] / v[8]) - g, 2)	
 
 
def flaeche_ins(v):
	return -np.prod([flaeche_0(v), flaeche_1(v), flaeche_2(v), flaeche_3(v), flaeche_4(v)])
 
def quadratizitaet_ins(v):
	return -np.prod([quadratizitaet_0(v), quadratizitaet_1(v), quadratizitaet_2(v), quadratizitaet_3(v), quadratizitaet_4(v)])
 
def goldener_schnitt_ins(v):
	return -np.prod([goldener_schnitt_0(v), goldener_schnitt_1(v), goldener_schnitt_2(v), goldener_schnitt_3(v), goldener_schnitt_4(v)])
 
def target(v):
	w1 = 1.0/10.0 * 1 # Flaeche
	w2 = 10.0/10.0 * 0 # Quadratizitaet // * 0 dar widerspruechlich mit Goldener Schnitt
	w3 = 9.0/10.0 * 1 # Goldener Schnitt
 
	return (w1 * flaeche_ins(v) + w2 * quadratizitaet_ins(v) + w3 * goldener_schnitt_ins(v))
 
def RaumAlgo():
	cons = constrains()
	return fmin_cobyla(target,[2.1]*10,cons)

fmin_cobyla benötigt eine Hauptfunktion welche minimiert wird und Bedingungen welche die Hauptfunktion entsprechend einschränkt.

Die Funktion „constraints()“ liefert eine Liste aller einschränkenden Bedingungen. In der constraints() Funktion wird, unter anderem, dass Intervall festgelegt in der fmin_cobyla optimieren darf. Zum Beispiel darf keiner der Räume vertikal größer werden als die Vertikal angebene Grundfläche oder der Gang darf nicht kleiner werden als die Gang Mindestgröße, bei uns festgelegt auf 1.25 Einheiten.

Unsere Hauptfunktion „target()“ wird zusammengesetzt aus drei Optimierungskriterien bezogen auf jeden einzelnen Raum des Grundrisses. Die Optimierungskriterien bestehen aus der Größtmöglichen Fläche (Maximal mögliche Größe für jeden Raum), bestmöglichste Quadratizität (Bestmögliches Quadratisches Seitenverhältnis des Raumes) und dem goldenen Schnitt (Seitenverhältnis des goldenen Schnittes). Für jeden Raum existiert eine Funktion welche jedes dieser Kriterien für den entsprechenden Raum berechnet. Diese werden anschließend in separaten Funktionen zusammengefasst und in der Hauptfunktion „target()“ mit anpassbarem Gewicht, der „RaumAlgo()“ Funktion übergeben. „RaumAlgo()“ ruft den Optimierungs Algorithmus fmin_cobyla auf und gibt dessen Ergebnisse zurück.

Die Datei „main.py“ in dem Algorithmus Verzeichniss dient dem Programm als Kontaktstelle zwischen dem Algorithmus und der Visualisierung.

from __future__ import print_function
import numpy
from algo import RaumAlgo
import copy
 
struktur = {
			0 : [(1, 2), (4, 3)],
			1 : [(4, 0), (0,4), (2, 3)],
			2 : [(3, 0), (4, 1), (1, 1)],
			3 : [(4, 1), (2, 2)],
			4 : [(0, 1), (1, 2), (2, 3), (3, 3)]
			}
 
def getListFromNumpyArray():
	raumAlgoListe = list(RaumAlgo())
	filtered = []
	for char in raumAlgoListe:
		if type(char) is numpy.float64:
			filtered.append(char)
	return filtered
 
def createStructure():
	structure = copy.deepcopy(struktur)
	data_raw = getListFromNumpyArray()
	data = [(data_raw[0], data_raw[1]), (data_raw[2], data_raw[3]), (data_raw[4], data_raw[5]), (data_raw[6], data_raw[7]), (data_raw[8], data_raw[9])]
	for room in range(5):
		structure[room].insert(0, data[room])	
	return structure
 
print(createStructure())

Endergebniss

Ruft man die Datei „main.py“ in dem Visualisierungs Algorithmus auf, so erhält man eine Optimierte Visualisierung des Grundrisses.

Nützliche Quellen:

Sitzungsprotokoll

1) Abandoned
ws1617/optimales_puppenhaus.txt · Zuletzt geändert: 2017/04/24 11:52 von workbench