Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ws1920:pmcd_25.03.2020 [2020/03/25 14:39] Zetraeder angelegt |
ws1920:pmcd_25.03.2020 [2020/04/01 17:18] (aktuell) Zetraeder |
||
---|---|---|---|
Zeile 4: | Zeile 4: | ||
**25.03.2020** | **25.03.2020** | ||
- | [[ws1920:scutoids|Home]] | + | [[ws1920:scutoids|Home]] | [[ws1920:pm_25.03.2020|Text]] | [[ws1920:pmcd_01.04.2020|>>]] |
Aktuelle Programmversion Stand 14:39 Uhr | Aktuelle Programmversion Stand 14:39 Uhr | ||
Zeile 10: | Zeile 10: | ||
<code python> | <code python> | ||
+ | import bpy | ||
+ | from random import random, randint | ||
+ | from math import * | ||
+ | import numpy as np | ||
+ | import time | ||
+ | from bpy_extras.mesh_utils import ngon_tessellate | ||
+ | |||
+ | |||
+ | # | ||
+ | # | ||
+ | # | ||
+ | # KLASSEN | ||
+ | |||
+ | class Sphere(object): | ||
+ | # Klasse, die spherische Objekte beschreibt, die für das Kugelkriterium | ||
+ | # der Delaunay-Tatraedisierung notwendig sind | ||
+ | pos = np.array([0,0,0]) # Mittelpunkt der Sphere | ||
+ | radius = 0 # Radius der Sphere | ||
+ | epsilon = 0.001 # epsilon-Bereich für das Kugelkriterium | ||
+ | | ||
+ | def __init__(self, pos, radius): | ||
+ | # eine Sphere wird immer mit einer Position und einem Radius initialisiert | ||
+ | self.pos = pos | ||
+ | self.radius = radius | ||
+ | | ||
+ | def punktInKugel(self, p): | ||
+ | # diese Funktion stellt fest, ob sich ein Punkt innerhalb der Sphere befindet | ||
+ | # (Funktion des Kugelkriteriums) | ||
+ | if (abstandPunkte(self.pos, p) < self.radius - self.epsilon): | ||
+ | return True | ||
+ | else: | ||
+ | return False | ||
+ | | ||
+ | class Tetraeder(object): | ||
+ | # Klasse, die Tetraedische Objekte beschreibt, für die Delaunay-Tetraedisierung | ||
+ | verts = [] # vertecies (Eckpunkte) des Tetraeders | ||
+ | faces = [] # faces (Flächen) des Tetraeders (enthält Flächen-Listen, | ||
+ | # die aus Ketten von Indexen der vertecie-Liste bestehen) | ||
+ | sp = np.array([0,0,0]) # Schwerpunkt (= Mittelpunkt der Umkugel) -> Diese Punkte bilden die vertecies für das Voronoi-Diagramm!! | ||
+ | name = "tetraeder" # ... | ||
+ | umkugel = Sphere(np.array([0,0,0]), 0) # Spherenobjekt, auf dessen Oberfläche alle vertecies des Tetraeders liegen | ||
+ | | ||
+ | def __init__(self, verts, name): | ||
+ | # Ein Tetraeder wird immer mit 4 Eckpunkten, vier Flächen und einem Namen initialisiert. | ||
+ | # Außerdem berechnet der Code hier automatisch die resultierende Umkugel und den daraus | ||
+ | # resultierenden Schwerpunkt. | ||
+ | self.name = name | ||
+ | self.verts = verts | ||
+ | self.faces = [(0,1,2),(1,2,3),(2,3,0),(3,0,1)] # jedes Tetraeder hat immer dieselben 4 faces... | ||
+ | if (self.schwerpunkt()[0] != None): | ||
+ | self.sp = self.schwerpunkt() | ||
+ | self.umkugel = Sphere(self.sp,abstandPunkte(self.sp,self.verts[0])) | ||
+ | else: | ||
+ | self.sp = np.array([None,None,None]) | ||
+ | | ||
+ | def schwerpunkt(self): | ||
+ | # Funktion errechnet den Schwerpunkt des Tetraeders, indem es zwischen je | ||
+ | # drei Eckpunkten Dreiecke aufspannt, die Schwerpunkte dieser errechnet, dann die Flächennormalen | ||
+ | # an diesen Punkten ansetzt und den Schnittpunkt der darasu resultierenden Geraden berechnet. | ||
+ | # Dieser Punkt ist dann der Schwerpunkt des Tetraeders (bzw. der Mittelpunkt der Umkugel) | ||
+ | v1 = np.subtract(self.verts[1],self.verts[0]) | ||
+ | v2 = np.subtract(self.verts[2],self.verts[0]) | ||
+ | v3 = np.subtract(self.verts[3],self.verts[0]) | ||
+ | | ||
+ | sp = tetraSchwerpunkt(self.verts[0],v1,v2,v3) | ||
+ | return sp # gibt den Schwerpunkt des Tetraeders zurück... | ||
+ | | ||
+ | | ||
+ | def selfRender(self, ker, col, tetra): | ||
+ | # falls innerhalb der Umkugel des Tetraeders kein Zellfremder Kern enthalten ist, wird das | ||
+ | # Tetraeder als Meshobjekt generiert, in dem 3D View angezeigt und der Liste "tetra" hinzugefügt. | ||
+ | # diese verfügt somit zum Schluss über eine Ansammlung von Referenzen aller zur Delaunay-Tetraedisierung | ||
+ | # gehörenden Tetraeder. | ||
+ | frei = True | ||
+ | for m in range(0, len(ker)): | ||
+ | if (self.umkugel.punktInKugel(ker[m]) == True): | ||
+ | frei = False | ||
+ | if (frei == True): | ||
+ | tetraeder = generateObj("Tetraeder", t.verts, t.faces, (0,0,0), col) | ||
+ | tetraeder.parent = grp_delaunay | ||
+ | tetra.append(self) | ||
+ | | ||
+ | class Gerade(object): | ||
+ | # Klasse, die Geradenformeln mithilfe von Stützvektor/Ortsvektor OV und | ||
+ | # Richtungsvektor RV beschreibt. Das Objektattribut Kerne beschreibt, durch welche | ||
+ | # beiden Kerne die Gerade verläuft | ||
+ | ov = (0,0,0) | ||
+ | rv = (0,0,0) | ||
+ | kerne = [0,0] | ||
+ | | ||
+ | def __init__(self,ov,rv,kerne = [0,0]): | ||
+ | # Jede Gerade wird mit einem Ortsvektor und Richtungsvektor initialisiert. Da nicht alle Funktionen | ||
+ | # Geraden durch Kerne ziehen, wird das Attribut standardmäßig auf 0,0 gessetzt | ||
+ | self.ov = ov | ||
+ | self.rv = rv | ||
+ | self.kerne = kerne | ||
+ | | ||
+ | def punktAn(self,k): | ||
+ | # Gibt die Koordinaten eines Punktes auf der Geraden an, der mit der Koordinatenform berechnet wird | ||
+ | # (im Detail: Stützvektor + Richtungsvektor*k) | ||
+ | punkt = np.add(self.ov,(self.rv[0]*k,self.rv[1]*k,self.rv[2]*k)) | ||
+ | return punkt | ||
+ | |||
+ | |||
+ | # | ||
+ | # | ||
+ | # | ||
+ | # FUNKTIONEN | ||
+ | |||
+ | def dreieckSchwerpunkt(pos,a,b): | ||
+ | # Berechnet mithilfe von drei Punkten den Schwerpunkt eines Dreieckes | ||
+ | # unter Berücksichtigung der dreidimensionalen Lage des Dreieckes | ||
+ | | ||
+ | g1 = Gerade(pos,a) | ||
+ | g2 = Gerade(pos,b) | ||
+ | | ||
+ | c = kreuzProdukt(a,b) | ||
+ | | ||
+ | d = kreuzProdukt(a,c) | ||
+ | e = kreuzProdukt(b,c) | ||
+ | | ||
+ | g3 = Gerade(g1.punktAn(.5), np.divide(d, 40)) | ||
+ | g4 = Gerade(g2.punktAn(.5), np.divide(e, 40)) | ||
+ | | ||
+ | schnitt = schnittPunkt(g3,g4) | ||
+ | return schnitt | ||
+ | |||
+ | def tetraSchwerpunkt(pos, v1, v2, v3): | ||
+ | # berechnet den Schwerpunkt des Tetraeders mit Hilfe von drei Richtungsvektoren, die gemeinsam | ||
+ | # zwei Dreiecke aufspannen (ein Vektor liegt genau an der Grenze zwischen beiden Dreiecken) | ||
+ | | ||
+ | sp1 = dreieckSchwerpunkt(pos, v1, v2) | ||
+ | sp2 = dreieckSchwerpunkt(pos, v1, v3) | ||
+ | | ||
+ | # Es wird nach None-Type untersucht, da "falsche Dreiecke" sich einschleichen könnten | ||
+ | # Dies sind zum Beispiel jene Dreiecke, deren Innenfläche 0 beträgt - somit wäre eine Seite gleich 0 | ||
+ | # und es existiert kein Richtungsvektor, der sie beschreiben könnte | ||
+ | if (sp1[0] != None and sp2[0] != None): | ||
+ | g1 = Gerade(sp1, kreuzProdukt(v1, v2)) | ||
+ | g2 = Gerade(sp2, kreuzProdukt(v1, v3)) | ||
+ | | ||
+ | schnitt = schnittPunkt(g1, g2) | ||
+ | return schnitt | ||
+ | else: return np.array([None,None,None]) | ||
+ | |||
+ | def schnittTest(g1,g2): | ||
+ | # testet, ob zwei Geraden sich schneiden und gibt gegebenfalls den Faktor aus, der in die Koordinatenform | ||
+ | # der einen Geraden zur Berechnung des Schnittpunktes eingesetzt werden kann | ||
+ | a = [[g2.rv[0],-g1.rv[0]],[g2.rv[1],-g1.rv[1]]] | ||
+ | b = [[g1.ov[0]-g2.ov[0]],[g1.ov[1]-g2.ov[1]]] | ||
+ | | ||
+ | c = [[g2.rv[2],-g1.rv[2]],[g2.rv[1],-g1.rv[1]]] | ||
+ | d = [[g1.ov[2]-g2.ov[2]],[g1.ov[1]-g2.ov[1]]] | ||
+ | | ||
+ | e = [[g2.rv[2],-g1.rv[2]],[g2.rv[0],-g1.rv[0]]] | ||
+ | f = [[g1.ov[2]-g2.ov[2]],[g1.ov[0]-g2.ov[0]]] | ||
+ | | ||
+ | if (np.linalg.det(a) != 0): | ||
+ | return np.linalg.solve(a,b)[0][0] | ||
+ | elif (np.linalg.det(c) != 0): | ||
+ | | ||
+ | return np.linalg.solve(c,d)[0][0] | ||
+ | elif (np.linalg.det(e) != 0): | ||
+ | | ||
+ | return np.linalg.solve(e,f)[0][0] | ||
+ | else: return None | ||
+ | |||
+ | def schnittPunkt(g1,g2): | ||
+ | # setzt das Ergebnis des Schnitttests in die Koordinatengleichung der einen Gerade ein | ||
+ | # (mithilfe der Funktion "Punkt an") und gibt den Schnittpunkt von g1 und g2 aus | ||
+ | z = schnittTest(g1,g2) | ||
+ | if (z != None): | ||
+ | return g2.punktAn(z) | ||
+ | else: return np.array([None,None,None]) | ||
+ | |||
+ | def abstandPunkte(p1, p2): | ||
+ | # gibt den Abstand zweier Punkte im dreidimensionalen Raum aus (Satz des Pythagoras) | ||
+ | a = sqrt(((p1[0]-p2[0])**2) + ((p1[1]-p2[1])**2) + ((p1[2]-p2[2])**2)) | ||
+ | return a | ||
+ | |||
+ | def zeichnePunkt(pos, size = 0.2, name = "Punkt"): | ||
+ | # zeichnet einen kleinen Würfel an der übergebenen Stelle, sodass dieser für Debugging-Zwecke | ||
+ | # später leicht gefunden und nachvollzogen werden kann (wird in dieser Programmversion nicht mehr benötigt) | ||
+ | # Größe kann angepasst werden | ||
+ | | ||
+ | size = size/2 | ||
+ | verts = [(size,size,size),(-size,size,size),(-size,-size,size),(size,-size,size) | ||
+ | ,(size,size,-size),(-size,size,-size),(-size,-size,-size),(size,-size,-size)] | ||
+ | faces = [(0,1,2,3),(4,5,6,7),(0,1,5,4),(1,2,6,5),(2,3,7,6),(3,0,4,7)] | ||
+ | punkt = generateObj(name,verts,faces,pos) | ||
+ | punkt.parent = grp_kerne | ||
+ | | ||
+ | def kreuzProdukt(a,b): | ||
+ | # Bildet das Kreuzprodukt der beiden übergebenen Parameter | ||
+ | c = (a[1]*b[2]-a[2]*b[1] , a[2]*b[0]-a[0]*b[2] , a[0]*b[1]-a[1]*b[0]) | ||
+ | return c | ||
+ | | ||
+ | def genKerne(anzahl, x_max, y_max, z_max): | ||
+ | # Generiert in der übergebenen Menge zufällige Punkte und gibt diese in einer Liste zurück. | ||
+ | # Die drei anderen Parameter sind für die Begrenzung des Raumes, in dem sich die Punkte befinden sollen | ||
+ | ker = [] | ||
+ | | ||
+ | # Framing | ||
+ | #signum = [-1, 1] | ||
+ | #for x in signum: | ||
+ | # for y in signum: | ||
+ | # for z in signum: | ||
+ | # r = randint(0,1) | ||
+ | # ker.append(np.array([x*x_max*r,y*y_max*r,z*z_max*r])) | ||
+ | #anzahl = anzahl - 8 | ||
+ | #if (anzahl <= 0): return ker | ||
+ | | ||
+ | for n in range(0, anzahl): | ||
+ | x = randint(-x_max, x_max)*((random()/(1+random()))) | ||
+ | y = randint(-y_max, y_max)*((random()/(1+random()))) | ||
+ | z = randint(-z_max, z_max)*((random()/(1+random()))) | ||
+ | if (len(ker) != 0): | ||
+ | neu = True | ||
+ | # verhindert, dass kein Punkt doppelt generiert wird... hätte man auch mit if element in liste | ||
+ | # machen können, verbesserungswürdig | ||
+ | for m in range(0, len(ker)): | ||
+ | if (x == ker[m][0] and y == ker[m][1] and z == ker[m][2]): | ||
+ | neu = False | ||
+ | if (neu == True): | ||
+ | ker.append([x,y,z]) | ||
+ | zeichnePunkt(ker[n], 1) | ||
+ | else: | ||
+ | n = n-1 | ||
+ | else: | ||
+ | ker.append([x,y,z]) | ||
+ | zeichnePunkt(ker[n], 1) | ||
+ | return ker | ||
+ | |||
+ | |||
+ | # AB HIER ERSTELLUNG DER ZELLWÄNDE | ||
+ | |||
+ | def radianBetweenVectors(v1, v2, n): | ||
+ | # berechnet den Zwischenwinkel zweier Vektoren unter Beachtung eines Normalenvektors | ||
+ | # (später wird für n die Flächennormale der Zellwand eingesetzt, welche die Verbindung der | ||
+ | # beiden Knotenpunkte ist, zwischen denen sich die Zellwand befindet). | ||
+ | | ||
+ | a = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] | ||
+ | b = v1[0]*v2[1]*n[2] + v2[0]*n[1]*v1[2] + n[0]*v1[1]*v2[2] - v1[2]*v2[1]*n[0] - v2[2]*n[1]*v1[0] - n[2]*v1[1]*v2[0] | ||
+ | | ||
+ | rad = np.arctan2(b, a) | ||
+ | if (rad < 0): rad = (2*np.pi) +rad | ||
+ | | ||
+ | deg = rad*180/np.pi | ||
+ | | ||
+ | return deg | ||
+ | |||
+ | |||
+ | def polySortList(v_list, normal): | ||
+ | # Funktion bekommt eine Liste von Punkten (mehr oder weniger in einer Ebene liegend) | ||
+ | # übergeben und sortiert diese, sodass sie im Kreis herum nacheinander ind er Liste | ||
+ | # angeordnet werden. Für die korrekte Drehrichtung braucht diese Funktion den Normalenvektor | ||
+ | # der Ebene, um den sie dreht. | ||
+ | | ||
+ | # Könnte man wahrscheinlich deutlich kürzer schreiben, überarbeitungswürdig! | ||
+ | | ||
+ | # Fall die Vertexliste aus 3 Elementen besteht, handelt es sich um ein Dreieck und die Reihnefolge ist egal. | ||
+ | if (len(v_list) == 3): | ||
+ | return v_list | ||
+ | | ||
+ | geordnet = [] # Zielliste, die zum Schluss die geordneten vertecie-Koordinaten als numpy-arrays enthält | ||
+ | | ||
+ | start = v_list[0] # Funktion merkt sich, bei welchem Punkt sie anfängt -> weiß dann, wann sie einmal im Kreis herum iteriert ist | ||
+ | aktu = start # aktu beinhaltet die Vertex-Daten des aktuellen Eckpunktes | ||
+ | vv = np.subtract(v_list[0], v_list[1]) # Vergleichswinkel (zeigt immer aus dem Kreis raus), somit kann immer eine einzige Reihenfolge generiert werden | ||
+ | | ||
+ | geordnet.append(start) | ||
+ | | ||
+ | while True: | ||
+ | # loop start values | ||
+ | min_angle = 360 | ||
+ | min_stelle = 0 | ||
+ | | ||
+ | # suche nach nächstem Punkt | ||
+ | for v in range(0, len(v_list)): | ||
+ | if (aktu[0] != v_list[v][0] or aktu[1] != v_list[v][1] or aktu[2] != v_list[v][2]): | ||
+ | dir = np.subtract(v_list[v], aktu) | ||
+ | deg = radianBetweenVectors(vv, dir, normal) | ||
+ | | ||
+ | if (deg < min_angle): | ||
+ | min_angle = deg | ||
+ | min_stelle = v | ||
+ | | ||
+ | geordnet.append(v_list[min_stelle]) | ||
+ | vv = np.subtract(v_list[min_stelle], aktu) | ||
+ | | ||
+ | aktu = v_list[min_stelle] | ||
+ | | ||
+ | if (aktu[0] == start[0] and aktu[1] == start[1] and aktu[2] == start[2]): break | ||
+ | return geordnet | ||
+ | |||
+ | def genPoly(liste, normal): | ||
+ | # generiert aus einer Liste von durcheinander angeordneten Polygon-Koordinaten ein Mesh-Flächenobjekt | ||
+ | # VORSICHT: diese Funktion wird nur bei konvexen Polygonen zu sinnstiftenden Ergebnissen führen! | ||
+ | | ||
+ | geordnet = polySortList(liste, normal) | ||
+ | f = [] # face-Datenliste, die angibt, welche Eckpunkte in welcher Reihenfolge verwendet werden sollen | ||
+ | | ||
+ | for i in range(0, len(geordnet)): | ||
+ | f.append(i) | ||
+ | | ||
+ | # Eigenart der generateObj-Funktion, benötigt geschachtelte Liste [[faces]], um richtig zu funktionieren | ||
+ | facelist = [] | ||
+ | facelist.append(f) | ||
+ | | ||
+ | generateObj("Zellwand", geordnet, facelist) | ||
+ | |||
+ | def generateObj(name, verts = [], faces = [], loc = np.array([0,0,0]), col = ( 1.0, 1.0, 1.0, 1.0 )): | ||
+ | # generiert ein 3D-MeshObjekt mit den Übergebenen Parametern (Name, Eckpunkte, Seiten) | ||
+ | # und gibt dieses im 3D Viewport aus | ||
+ | | ||
+ | mymesh = bpy.data.meshes.new(name) | ||
+ | myobject = bpy.data.objects.new(name,mymesh) | ||
+ | |||
+ | mymat = bpy.data.materials.new("Mesh_mat") | ||
+ | mymat.diffuse_color = col | ||
+ | mymesh.materials.append(mymat) | ||
+ | | ||
+ | myobject.location = loc | ||
+ | bpy.context.collection.objects.link(myobject) | ||
+ | |||
+ | mymesh.from_pydata(verts,[],faces) | ||
+ | mymesh.update(calc_edges=True) | ||
+ | | ||
+ | return myobject | ||
+ | |||
+ | |||
+ | # ZUORDNUNG DER WÄNDE ZU KOLLEKTIONEN (funkioniert lieder noch nicht komplett...) | ||
+ | def createCollection(name): | ||
+ | col = bpy.data.collections.new(name) | ||
+ | bpy.context.scene.collection.children.link(col) | ||
+ | return col | ||
+ | |||
+ | def linkToCollection(obj, col): | ||
+ | col.objects.link(obj) | ||
+ | |||
+ | # LISTEN DURCHSUCHEN | ||
+ | def inListeAnStelle(element, liste): | ||
+ | # durchsucht eine Liste nach einem Vektor und gibt den entsprechenden Index aus, falls es enthalten ist | ||
+ | if element in liste: | ||
+ | for i in range(0, len(liste)): | ||
+ | if (element == liste[i]): return i | ||
+ | return None | ||
+ | |||
+ | def inListeAnStelleNP(element, liste): | ||
+ | # durchsucht eine Liste von np.arrays nach einem np.array und gibt die Stelle aus, | ||
+ | # falls dieser enthalten ist | ||
+ | for i in range(0, len(liste)): | ||
+ | if (np.array_equal(element, liste[i])): return i | ||
+ | return None | ||
+ | |||
+ | # SCUTOID TEST!! | ||
+ | |||
+ | def watertightTest(faces): | ||
+ | # Testet, ob das Objekt mit der übergebenen Face-Liste watertight ist, also keinerlei Löcher aufweist. | ||
+ | # wenn jede Edge mindestens zu genau zwei Flächen zugeordnet ist, ist eine Mesh watertight (in diesem Fall) | ||
+ | | ||
+ | for face in faces: | ||
+ | | ||
+ | for a in range(0, len(face)): | ||
+ | | ||
+ | if (a == len(face)-1): b = 0 | ||
+ | else: b = a+1 | ||
+ | | ||
+ | enthalten = False | ||
+ | for face02 in faces: | ||
+ | if (face != face02): | ||
+ | if ((face[a] in face02) and (face[b] in face02)): enthalten = True | ||
+ | if (enthalten == False): | ||
+ | return False | ||
+ | | ||
+ | return True | ||
+ | |||
+ | def grenzenFlächenAneinander(f1, f2): | ||
+ | # Funktion bekommt zwei Flächen übergeben und ermittelt, ob diese direkt aneinander grenzen. | ||
+ | for a in range(0, len(f1)): | ||
+ | for b in range(a+1, len(f1)): | ||
+ | | ||
+ | if ((f1[a] in f2) and (f1[b] in f2)): | ||
+ | return True | ||
+ | |||
+ | def ScutoidTest(faces): | ||
+ | # Diese Funktion testet, ob ein Meshobjekt ein Scutoid ist oder nicht. Hierzu bekommt es eine Liste aller | ||
+ | # faces übermittelt und testet, ob es eine Kombination aus zwei Flächen gibt (diese beiden dürfen sich nicht berühren, | ||
+ | # sind die Deckflächen des "Prismas"), an die jeweils beide jede andere Fläche anschließt | ||
+ | # (bis auf eine Fläche, diese ist die Dreiecksfläche) | ||
+ | | ||
+ | # das kleinstmöglichste Scutoid hat eine Dreiecksgrundgläche und eine vierecksgrundfläche und hat somit 6 Seiten | ||
+ | #if (len(faces) < 6): return False | ||
+ | | ||
+ | | ||
+ | for a in range(0, len(faces)): | ||
+ | for b in range(a+1, len(faces)): | ||
+ | | ||
+ | scutellum = 0 | ||
+ | #index = 0 | ||
+ | if not (grenzenFlächenAneinander(faces[a], faces[b])): | ||
+ | print("berühren sich nicht") | ||
+ | for g in faces: | ||
+ | if not (grenzenFlächenAneinander(g, faces[a]) == True and grenzenFlächenAneinander(g, faces[b]) == True): | ||
+ | #index = g | ||
+ | scutellum = scutellum + 1 | ||
+ | |||
+ | if (scutellum == 1): #and len(faces[g]) == 3): | ||
+ | return True | ||
+ | | ||
+ | return False | ||
+ | | ||
+ | | ||
+ | | ||
+ | # | ||
+ | # | ||
+ | # | ||
+ | # CODE FÜR DIE ZELLGENERIERUNG | ||
+ | |||
+ | |||
+ | # setup groups | ||
+ | grp_alles = generateObj('Zellgeneration.000') | ||
+ | |||
+ | grp_delaunay = generateObj('Delaunay') | ||
+ | grp_delaunay.parent = grp_alles | ||
+ | grp_kerne = generateObj('Kerne') | ||
+ | grp_kerne.parent = grp_alles | ||
+ | |||
+ | grp_randzellen = generateObj('Randzellen') | ||
+ | grp_randzellen.parent = grp_alles | ||
+ | grp_zellen = generateObj('Zellen') | ||
+ | grp_zellen.parent = grp_alles | ||
+ | grp_scutoids = generateObj('Scutoids') | ||
+ | grp_scutoids.parent = grp_alles | ||
+ | |||
+ | print(">>", grp_alles.name) | ||
+ | |||
+ | # setup Größen | ||
+ | x_max = 100 | ||
+ | y_max = 100 | ||
+ | z_max = 100 | ||
+ | anzahlKerne = 25 # Minimum 8 wegen Framing | ||
+ | |||
+ | #del_col = (1.0, 0.0, 0.0, 1) | ||
+ | tetra = [] | ||
+ | |||
+ | # Kernliste wird generiert | ||
+ | ker = genKerne(anzahlKerne, x_max, y_max, z_max) | ||
+ | |||
+ | # Generiert auf Basis der Kernliste die Delaunay-Tetraedisierung | ||
+ | for i in range(0, len(ker)): | ||
+ | for j in range(i+1, len(ker)): | ||
+ | for k in range(j+1, len(ker)): | ||
+ | for l in range(k+1, len(ker)): | ||
+ | | ||
+ | t = Tetraeder([ker[i], ker[j], ker[k], ker[l]],"Tetraeder") | ||
+ | | ||
+ | # random red color, weil es nice aussieht... | ||
+ | green_tint = randint(0, 6)/100 | ||
+ | blue_tint = randint(0,6)/100 | ||
+ | del_col = (1.0, green_tint, blue_tint, 1.0) | ||
+ | t.selfRender(ker, del_col, tetra) | ||
+ | |||
+ | # erstellt die notwendigen Sammellisten, die die entsprechenden Information über ALLE finalen Zellen enthalten | ||
+ | vertex = [[] for dummy in range(anzahlKerne)] # liste alle vertecies, index markiert die Zellnummer, zu der sie gehören | ||
+ | faces = [[] for dummy in range(anzahlKerne)] # liste alle faces, index markiert die Zellnummer, zu der sie gehören | ||
+ | |||
+ | # Generiert auf Basis der Delaunay-Tetraedisierung das Voronoi-Diagramm | ||
+ | for u in range(0, len(ker)): | ||
+ | for v in range(u+1, len(ker)): | ||
+ | z_verts = [] | ||
+ | # Alle Vertecies, die rund um die Verbindung beider Zellkerne liegen (Eckpunkte der Zelland zwischen ihnen) werden in z_verts abgespeichert | ||
+ | for w in range(0, len(tetra)): | ||
+ | if ((ker[u] in tetra[w].verts) and (ker[v] in tetra[w].verts)): | ||
+ | z_verts.append(tetra[w].sp) | ||
+ | | ||
+ | if (len(z_verts) > 2): # checkt, ob eine "echte" Fläche aufgespannt wird | ||
+ | normal = np.subtract(ker[u], ker[v]) | ||
+ | geordnet = polySortList(z_verts, normal) | ||
+ | | ||
+ | temp_face1 = [] | ||
+ | temp_face2 = [] | ||
+ | | ||
+ | for i in range(0, len(geordnet)): | ||
+ | | ||
+ | if (inListeAnStelleNP(geordnet[i], vertex[u]) != None): | ||
+ | temp_face1.append(inListeAnStelleNP(geordnet[i], vertex[u])) | ||
+ | else: | ||
+ | vertex[u].append(geordnet[i]) | ||
+ | temp_face1.append(inListeAnStelleNP(geordnet[i], vertex[u])) | ||
+ | |||
+ | if (inListeAnStelleNP(geordnet[i], vertex[v]) != None): | ||
+ | temp_face2.append(inListeAnStelleNP(geordnet[i], vertex[v])) | ||
+ | else: | ||
+ | vertex[v].append(geordnet[i]) | ||
+ | temp_face2.append(inListeAnStelleNP(geordnet[i], vertex[v])) | ||
+ | faces[u].append(temp_face1) | ||
+ | faces[v].append(temp_face2) | ||
+ | | ||
+ | for i in range(0, anzahlKerne): | ||
+ | if (watertightTest(faces[i]) == True): | ||
+ | print("watertight") | ||
+ | if (ScutoidTest(faces[i]) == True): | ||
+ | yellow_tint = randint(10, 100)/100 | ||
+ | scu = generateObj("Scutoid", vertex[i], faces[i], np.array([0,0,0]), ( 0.0, yellow_tint, 1.0, 1.0 )) | ||
+ | scu.parent = grp_scutoids | ||
+ | else: | ||
+ | red_tint = randint(0, 20)/100 | ||
+ | blue_tint = randint(0,25)/100 | ||
+ | zelle = generateObj("Zelle", vertex[i], faces[i], np.array([0,0,0]), ( red_tint, 1.0, blue_tint, 1.0 )) | ||
+ | zelle.parent = grp_zellen | ||
+ | else: | ||
+ | rand = generateObj("Randzelle", vertex[i], faces[i], np.array([0,0,0])) | ||
+ | rand.parent = grp_randzellen | ||
+ | | ||
</code> | </code> |