Bernhard Häussner
Tags: Artikel mit dem Tag «Mathematik» durchstöbern

Processing: 3D-Koch-Kurve mit Sierpinski-Dreieck

23.05.2009, 17:34

Nachdem ich mich jetzt mal an Processing heran gewagt habe, habe ich auch gleich mal einen fraktalen Körper basteln müssen. Es handelt sich um eine Art dreidimensionale Koch-Schneeflocke.

Die Koch-Schneeflocke an sich ist eigentlich recht simpel, habe ich schon in Logo und PHP implementiert. Es wird eine Linie jeweils durch eine Linie mit einem Dreieck darin ersetzt. Auf den Raum übertragen heißt das dann eine Dreieckfläche bekommt einen Tetraeder eingesetzt. Dies wird dann mit den Flächen des Tetraeders rekursiv wiederholt, sodass dann ungefähr so etwas entsteht:

Es entsteht eigentlich eine etwas andere Form, die allerdings nicht sehr interessant anzusehen ist, nämlich ein Tetraeder, der in viele kleine Tetraeder zerlegt ist. Damit der strenge Tetraeder etwas aufgelockert wird, habe ich die neuen Tetraeder immer um 70% skaliert. Es sind also keine Tetraeder mehr, sondern, je tiefer die Rekursion geht, immer schiefere Pyramiden.

Außerdem habe ich nicht mit nur einem Dreieck begonnen, sondern mit einem Tetraeder, sodass ein Körper ensteht, ähnlich wie wenn man in 2D mit dem Dreieck beginnt. Bei 6 Rekursionsschritten ergeben sich dann 66*4 = 186 624 Dreiecke. Die werden dann von Processing z.B. mit 7%iger Transparenz gerendert, damit es etwas schöner aussieht und so kann dann dieses Video entstehen:


Interessant ist eigentlich auch, dass der Rest, der vom ursprünglichen Dreieck bleibt, wenn man die ganzen Tetraeder heraus nimmt, genau dem Sierpinski-Dreieck entspricht. So sieht der Code aus:

fraqTriang trs[]=new fraqTriang[4];

void setup(){
  size(640, 480, P3D);
  noStroke();
  fill(150,150,150,20);
  int bigrad=200;
  int req=6;
  
  PVector A=new PVector(-bigrad,0,0);
  PVector B=new PVector(bigrad/2,0,-173.21);
  PVector C=new PVector(bigrad/2,0,173.21);
  PVector D=new PVector(0,-bigrad*(sqrt(6)/3),0);
  
  trs[0]=new fraqTriang(A,B,C,req);
  trs[1]=new fraqTriang(A,D,B,req);
  trs[2]=new fraqTriang(A,C,D,req);
  trs[3]=new fraqTriang(B,D,C,req);
}


void draw(){
  background(255);
  lights();
  
  float winkel=(mouseX/float(width))*TWO_PI;
  float xpos=cos(winkel);
  float ypos=sin(winkel);
  float radius=300.000;
  camera(xpos*radius, mouseY, ypos*radius, // eyeX, eyeY, eyeZ
         0.0, -50.0, 0.0, // centerX, centerY, centerZ
         0.0, -1.0, 0.0); // upX, upY, upZ
      
  for (int i=0;i<trs.length;i++) {
    trs[i].display();
  }
  //saveFrame("frames/koch3d-####.png"); //uncomment to record
}

class fraqTriang{
  PVector PointA;
  PVector PointB;
  PVector PointC;
  fraqTriang recTris[]=new fraqTriang[6];
  int rec;
  float scaling;
  
  fraqTriang(PVector A,PVector B,PVector C, int recursion){
    scaling=0.7;
    PointA=A;
    PointB=B;
    PointC=C;
    rec=recursion;
    applyRecursion ();
  }
    
  void applyRecursion () {
    if (rec!=0) {
      PVector PointAB2=PVector.add(PointA,PointB);
      PointAB2.div(2);
      PVector PointAC2=PVector.add(PointA,PointC);
      PointAC2.div(2);
      PVector PointBC2=PVector.add(PointB,PointC);
      PointBC2.div(2);
      
      PVector PointZ=PVector.add(PointA,PointB);
      PointZ.add(PointC);
      PointZ.div(3);
      PVector PointAB=PVector.sub(PointA,PointB);
      PVector PointAC=PVector.sub(PointA,PointC);
      PVector PointH=PointAB.cross(PointAC);
      PointH.normalize();
      PVector PointAAB2=PVector.sub(PointA,PointAB2);
      float a=PointAAB2.mag();
      float pheight=a*(sqrt(6)/3)*scaling;
      PointH.mult(-pheight);
      PVector PointZH=PVector.add(PointZ,PointH);
      
      recTris[0]=new fraqTriang(PointA,PointAB2,PointAC2,rec-1);
      recTris[1]=new fraqTriang(PointB,PointBC2,PointAB2,rec-1);
      recTris[2]=new fraqTriang(PointC,PointAC2,PointBC2,rec-1);
      
      recTris[3]=new fraqTriang(PointZH,PointAC2,PointAB2,rec-1);
      recTris[4]=new fraqTriang(PointZH,PointAB2,PointBC2,rec-1);
      recTris[5]=new fraqTriang(PointZH,PointBC2,PointAC2,rec-1);
    }
  }  
  
  void display () {
    if (rec==0) {
      beginShape();
      vertex(PointA.x, PointA.y ,PointA.z);
      vertex(PointB.x, PointB.y ,PointB.z);
      vertex(PointC.x, PointC.y ,PointC.z);
      endShape(CLOSE);
    } else {
      for (int i=0; i<recTris.length;i++) {
        recTris[i].display();
      }
    }
  }
  
}

Man sieht dem Code an, wie schnell man mit Processing Graphiken erstellen kann, ohne sich mit komplizierteren APIs herumzuschlagen. Durch die vielen Beispiele, die Processing beiliegen und die übersichtliche API-Dokumentation kann man innerhalb von kurzer Zeit schon recht hübsche Ergebnisse erzielen. Viel Synatax muss man auch nicht lernen, da die Processing-Synatx mehr oder weniger der von Java entspricht.

Kommentare: keine

Wellen mit PHP rendern

30.04.2009, 19:52

Da ich heute mal wieder etwas Zeit zum Programmieren hatte (bzw. zum was-ich-will-Programmieren) habe ich unseren aktuellen Physik-Stoff visualisiert und zwar mit PHP und GD.

Ich habe ja schon vorher ein wenig mit SVG herumexperimentiert. Das war allerdings alles etwas krum und schief und ganz ohne Mathematik per Hand zusammengebastelt:


interference

Jetzt musste natürlich ein Algorithmus her. Eigentlich sind es nur 2 Klassen, die festlegen, wie eine Welle aussieht und was passiert, wenn sie von einem Zentrum ausgeht. Und zwar werden zyklische Wellenfronten von verschiedenen Zentren aus übereinandergelegt. Schwer zu erklären, einfacher im PHP-Code zu sehen:

class Wave {
  function __construct($length,$amplitude) {
    $this->length=$length;
    $this->amplitude=$amplitude;
  }
  function getPhaseAtLength($s) {
    $phase=($s%$this->length)/$this->length;
    $phaseWithDelta=fmod($phase+(isset($this->phase)?$this->phase:0)+1,2)-1;
    return $phaseWithDelta;
  }
  function getAmplitudeAtLength($s) { //sinuswelle
    $A=sin(deg2rad($this->getPhaseAtLength($s)*360))*$this->amplitude;
    return $A;
  }
  function setPhase($phase) {
    $this->phase=$phase;
    return $this;
  }
}

class Wavecenter {
  function __construct(Wave $wave,Point $center) {
    $this->wave=$wave;
    $this->center=$center;
  }
  function getAmplitudeAtPoint(Point $point) {
    $distance=$this->center->getDistance($point);
    $amp=$this->wave->getAmplitudeAtLength($distance);
    return $amp;
  }
}

Und mit ein bisschen anderem Code, der das ganze Visualisiert, entsteht dann sowas, wie die roten Wellen oben.

Das erste ist natürlich keine Sinuswelle: Am Anfang hat meine Point::getDistance() nicht funktioniert (^ statt pow()), so dass diese seltsame Figur entstanden ist. Interessant ist, dass man bereits zwei überlagerte Wellen sieht (daher die Symmetrie), diese jedoch seltsam deformiert sind.

Da ich gerade noch eine Phasenverschiebung dazu gezimmert habe, kann ich die Wellen auch noch animieren, also nicht nur in der Position des Mittelpunkts, sondern sie auch wandern lassen. Das sieht dann so aus:

Als kleinen Nachtrag noch ein paar Überlagerung einer Welle und anderen mit kleinerer Wellenlänge, wobei die Wellenlängen in (anfangs) kleinen ganzzahligen Verhältnissen stehen. Daher kann man sie auch recht leicht in der Musik finden. Die oberste Reihe wäre demnach das Schallbild, wenn man 1, 2, und 3 mal den selben Ton spielt, nur eben in der anderen Oktave. Das hört man normalerweise recht einfach, aber beim Sehen ist es etwas anders:

Jetzt wäre es natürlich irgendwie cool, auch einzelne Wellen irgendwie umher wandern zu lassen, um Doppler-Effekt und Reflexion etc. zu zeigen, aber da wären etwas kompliziertere Models nötig.

Nachtrag 26. Mai 2009: OpenGL

Da ich vor kurzem begonnen habe mit Processing Grafiken zu basteln, habe ich Processing auch gleich mal für das Wellen-Darstellen benutzt. Und das funktioniert sehr gut: Processing bietet die Grundlagen, sodass man leicht eine Welle, die von einem Zentrum ausgeht rendern kann. Das macht man dann z.B. mit 10 Frames, immer mit leichter Phasenverschiebung. Das könnte man dann einfach animieren. Wesentlich ansehnlicher ist es aber, wenn man die gerenderte Welle als animierte Textur in eine OpenGL-Szene mit additiver Überblendung einsetzt. Dann entstehen nämlich die oben gezeigten Bilder in realtime. Abgesehen vom anfänglichen Rendern und dem Laden in den Graphikspeicher der 10 2000x2000 Pixel Texturen. Screenshot:

Vor allem wenn man mehr Wellenzentren man hat, übernimmt OpenGL viele Berechnungen, weshalb man die Wellenzentren ohne Wartezeiten einfach mit der Maus verschieben kann.

Kommentare: keine

Vektor-Wallpaper

12.04.2009, 21:01

Heute habe ich mal wieder ein bisschen mit Inkscape herum gespielt, wobei diese Interessanten Fraktal-artigen, (dreh)symmetrischen Figuren entstanden sind. Ich habe dann Details ausgewählt (und mit selektierten Farben versehen) aus diesem gesamten Set:

Da alles als SVG-Datei angelegt wurde ist es auch beliebig skalierbar. Irgendwie ruft der leicht kubistische Style nach einem Kantenfindungs-Algorithmus, der das Ganze mehr oder weniger automatisch mit Fotos macht... muss ich aber wohl vertagen.

order (custom-made) prints

Kommentare: keine

Fraktale klicken

27.03.2009, 14:22
DHML fun

DHML fun

Letzte Woche bin ich über I could not stop auf dhteumeuleu gestolpert, wo es auch noch ein paar andere nette Javascript (DHTML) Spielereien zu sehen gibt. Mir waren diese roten und schwarzen Kacheln des Originals etwas zu langweilig, und so habe ich beschlossen (da die Demo ja auch unter einer CC-Lizenz steht) mal ein paar andere Bilder aus zu testen, die lustige Muster ausspucken.

Zuerst hatte ich vor etwas ähnliches wie das hier zu basteln, doch das stellte sich als nicht zu einfach heraus, da die schrägen Linien beim Unterteilen nicht richtig aneinander passen. Darum habe ich zunächst einen Rand und ein paar vertikale und horizontale Streben eingebaut, die sich dann immer weiter unterteilen Lassen.

» Demo

Dann wollte ich etwas Farbe ins Spiel bringen und habe noch die bisher weißen Zwischenfelder mit (sehr) bunten Farbvariationen gefüllt. Das ergibt dann ein paar nette, mehr oder weniger zufällig angeordnete Farbkacheln.

» Demo

Wenn sich das Ganze mit Farbe abspielt, kann man die inneren Verzweigungen eigentlich auch weg lassen. Das Ergebnis sind noch buntere Bilder. Leider hat man kaum noch Kontrolle über die Farbkomposition.

» Demo

Am Ende habe ich dann noch, nur um zu sehen, wie sich Schrägen machen, ein weiteres Derivat erstellt. Diesmal sind die Quadrate in zwei Dreiecke unterteilt und unterteilen sich bei Mausklick noch weiter. Im Endeffekt entsteht ein total zufälliger Mix aus Dreieckflächen, der ein wenig an Tangram erinnert.

» Demo

Die Muster sind dann so etwas wie teils zufällige, teils interaktiv gestaltete Fraktale.

Kommentare: 4 Einträge

Rotationskörper des Würfels

20.11.2008, 19:46

Heute im Mathe-LK: Die Rotationskörper. Während der Umriss einiger Rotationskörper durch eine einfache Funktion beschrieben wird, ist das bei einem anders: Einer mit 3 Abschnitten, einer der nicht leicht berechenbar ist, aber trozdem einer, der im Alltag beobachtbar ist: Ein Würfel wird gekreiselt, und der entstehende Eindruck einer Kontur wird als Umriss für den Rotationskörper verwendet.

Wie der Schatten eines Würfels in der Sonne aussieht, kann man recht schnell feststellen. Wie der Schatten eines sich genau in der Raumdiagonale drehenden Würfels waagrecht von der Sonne bestrahlt aussieht kann man im Abendlicht vielleicht grob erkennen, aber mathematisch korrekt berechnen nicht so leicht. Da man dieses Phänomen auch schlecht Photographieren kann, und ich auch ein Fan von Blender bin, musste ich diesen kreiselnden Würfel unbedingt digital nachbauen.

In einer orthographischen Seitenansicht wird ein schwarzer Würfel auf weißem Grund gedreht. Damit das ganze kein Video wird und der für das Auge erkennbare neue Umriss sichtbar wird, musste auch noch Motion Blur aktiviert werden. Ein Frame wird in 16 Frames zerlegt, die jeweils ein 16tel der Zeit des Frames widerspiegeln. Ich habe den Würfel so animiert, dass es sich in einem Frame um 360 Grad dreht. Im obigen Bild sind also 16 Mini-Frames mit je einem Rotationsunterschied von 22,5 Grad übereinandergelegt.

Und die Näherung ist schon nicht schlecht. Doch mit mathematisch exakter Berechnung hat das natürlich noch wenig gemeinsam. Vielleicht macht sich ja eines Tages ein beschäftigungsloser oder interessierter Mathematiker an die Berechnung. Doch eins ist sicher: Ganz einfach wird sie nicht.

Kommentare: keine
 
Χρόνογραφ
© 2008-2012 by Bernhard Häussner - Impressum - Login
Kurz-Link zur Homepage: http://1.co.de/