Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ws1617:optimales_puppenhaus [2017/01/12 17:00] SoenkeRoos96 |
ws1617:optimales_puppenhaus [2017/04/24 11:52] (aktuell) workbench |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
====== Optimales Puppenhaus ====== | ====== Optimales Puppenhaus ====== | ||
+ | |||
+ | {{:ws1617:puooenh.jpg?200|}} {{ :ws1617:mataerial-3d-druck-1.jpg?200|}} | ||
Zeile 7: | Zeile 9: | ||
- | 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 Dinge 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. | + | 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 Dinge((Abandoned)) 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. | 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**__ | ||
+ | |||
+ | <code python> | ||
+ | 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 | ||
+ | </code> | ||
+ | |||
+ | 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**__ | ||
+ | <code python> | ||
+ | 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") | ||
+ | </code> | ||
+ | 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**__ | ||
+ | <code python> | ||
+ | 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") | ||
+ | </code> | ||
+ | |||
+ | Ä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**__ | ||
+ | |||
+ | <code python> | ||
+ | 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") | ||
+ | </code> | ||
+ | |||
+ | 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. | ||
+ | |||
+ | <code python> | ||
+ | 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) | ||
+ | </code> | ||
+ | |||
+ | == 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). | ||
+ | |||
+ | <code python> | ||
+ | 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) | ||
+ | </code> | ||
+ | |||
+ | 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. | ||
+ | <code python> | ||
+ | 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()) | ||
+ | </code> | ||
+ | |||
+ | __**Endergebniss**__ | ||
+ | |||
+ | Ruft man die Datei "main.py" in dem Visualisierungs Algorithmus auf, so erhält man eine Optimierte Visualisierung des Grundrisses. | ||
==== Nützliche Quellen: ==== | ==== Nützliche Quellen: ==== | ||
Zeile 15: | Zeile 668: | ||
* [[http://dl.acm.org/citation.cfm?id=1866203]] | * [[http://dl.acm.org/citation.cfm?id=1866203]] | ||
* [[http://www.cmu.edu/me/ddl/publications/2001-Michalek-MSThesis.pdf]] | * [[http://www.cmu.edu/me/ddl/publications/2001-Michalek-MSThesis.pdf]] | ||
+ | |||
==== Sitzungsprotokoll ==== | ==== Sitzungsprotokoll ==== | ||
Zeile 31: | Zeile 685: | ||
[[optimales_puppenhaus_12.01.17]] | [[optimales_puppenhaus_12.01.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_16.01.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_19.01.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_26.01.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_30.01.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_02.02.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_09.02.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_16.02.17]] | ||
+ | |||
+ | [[optimales_puppenhaus_23.02.17]] | ||