EINFÜHRUNG |
Statt die Fadengrafik selbst zu erzeugen, könntest du auch eine Maschine damit beauftragen. Diese müsste die Anleitung verstehen und dann in eine Aktion umsetzen, beispielsweise mit einem Roboterarm die Fäden ziehen oder die Fäden auf einem Bildschirm aufzeichnen. Eine solche Anleitung für eine Maschine nennt man auch Algorithmus. Man kann den Algorithmus zwar wie eine Bastel-Anleitung zuerst allgemein verständlich in einer Umgangssprache formulieren. Da es aber erwünscht ist, dass die Maschine bei jedem Durchlauf genau dasselbe Muster erzeugt, muss der Algorithmus so exakt formuliert sein, dass die Maschine bei jedem Schritt eindeutig weiss, was zu tun ist. Dazu hat man die Programmiersprachen erfunden und darum lernst du programmieren, denn in der natürlichen Sprache gibt es keine solche Eindeutigkeit. |
PUNKTE ALS LISTEN |
In der Geometrie schreibst du für einen Punkt P(x, y), wo x und y die Koordinaten sind. Im Programm können wir die zwei Zahlen x und y in eine Datenstruktur verpacken, die wir eine Liste nennen. Wir schreiben p = [x, y]. Der geometrische Punkt P(0, 8) wird also durch die Liste p = [0, 8] modelliert. Auf die einzelnen Komponenten einer Liste kannst du mit einem Index zugreifen, wobei die Zählung bei 0 beginnt. Du schreibst den Index in eine eckiges Klammerpaar, also für die x-Koordinate p[0] und für die y-Koordinate p[1]. Das Schöne daran ist, dass alle Grafikfunktionen von GPanel "listenbewusst" sind, denn sie funktionieren statt mit x-y-Koordinaten auch mit Punkt-Listen. Dein Programm modelliert das Ziehen der Fäden von einem Nagel A über 19 Nägel bei den Koordinaten auf der x-Achse zum Nagel B und wieder zurück. Du kannst sogar ein delay() einbauen, das bewirkt, dass das Fadenziehen tatsächlich eine für Menschen erfassbare Zeit lang dauert. from gpanel import * DELAY = 100 def step(x): p1 = [x, 0] draw(p1) delay(DELAY) draw(pB) delay(DELAY) p2 = [x + 1, 0] draw(p2) delay(DELAY) draw(pA) delay(DELAY) makeGPanel(-10, 10, -10, 10) pA = [0, 8] pB = [0, -8] move(pA) for x in range(-9, 9, 2): step(x) |
MEMO |
Bei der Implementierung eines Algorithmus müssen auch die Daten günstig strukturiert werden. Bei uns werden geometrische Punkte als Listen mit zwei Elementen (x- und y-Koordinaten) modelliert. Die Wahl der Datenstruktur beeinflusst das Programm ganz wesentlich. Niklaus Wirth, ein berühmter Informatikprofessor an der ETH Zürich, sagte treffend: Programm = Algorithmus + Datenstruktur [Lit.] |
PROGRAMMIEREN IST EINE KUNST |
|
MEMO |
Ein Algorithmus kann auf verschiedene Arten implementiert werden, die sich in der Länge des Codes und in der Laufzeit des Programms unterscheiden. Man spricht auch von eleganten und weniger eleganten Programmen. Merke dir, dass es nicht genügt, dass ein Programm ein richtiges Resultat hervorbringt, sondern dass es auch elegant geschrieben ist. Fasse darum Programmieren als eine Kunst auf. |
ELEGANTE FADENGRAFIK-ALGORITHMEN |
from gpanel import * makeGPanel(0, 100, 0, 100) pA = [10, 10] pB = [90, 20] pC = [30, 90] line(pA, pB) line(pA, pC) r = 0 while r <= 1: pX1 = getDividingPoint(pA, pB, r) pX2 = getDividingPoint(pA, pC, 1 - r) line(pX1, pX2) r += 0.05 delay(300) |
MEMO |
Bibliotheksfunktionen, wie hier getDividingPoint(), können ein Programm stark vereinfachen. Für bestimmte wohldefinierte Teilaufgaben solltest du vorhandene Bibliotheksfunktionen verwenden, die du von deiner Programmiererfahrung her kennst, aus Dokumentationen entnimmst oder mit Web-Suchmaschinen findest. Mathematisch betrachtet ist die entstehende Kurve eine quadratische Bézierkurve. Du kannst sie mit der Funktion quadraticBezier(pB, pA, pC) zeichnen (pB und pC sind die Endpunkte, pA der Kontrollpunkt der Kurve). |
MAUSGESTEUERTE FADENGRAFIKEN |
Dazu verwendest du zum Erstellen der Grafik in die Funktion updateGraphics(), die von den Maus-Callbacks aufgerufen wird. Dabei löschst du jeweils das ganze Grafikfenster und erstellst es neu mit dem Punkt A an der aktuellen Lage des Mauscursors from gpanel import * def updateGraphics(): clear() line(pA, pB) line(pA, pC) r = 0 while r <= 1: pX1 = getDividingPoint(pA, pB, r) pX2 = getDividingPoint(pA, pC, 1 - r) line(pX1, pX2) r += 0.05 def myCallback(x, y): pA[0] = x pA[1] = y updateGraphics() makeGPanel(0, 100, 0, 100, mousePressed = myCallback, mouseDragged = myCallback) pA = [10, 10] pB = [90, 20] pC = [30, 90] updateGraphics() |
MEMO |
Du kannst auch zwei verschiedene Events, hier den Press-Event und den Drag-Event mit demselben Callback abhandeln. |
AUFGABEN |
|
ZUSATZSTOFF |
BÉZIER-KURVEN |
Du kannst den Algorithmus einfach in einem Programm implementieren, wenn du Punkte als Listen implementierst und die Funktion getDividingPoint() mehrmals aufrufst. from gpanel import * makeGPanel(0, 100, 0, 100) pt1 = [10, 10] pc1 = [20, 90] pc2 = [70, 70] pt2 = [90, 20] setColor("green") line(pt1, pc1) line(pt2, pc2) line(pc1, pc2) r = 0 while r <= 1: q1 = getDividingPoint(pt1, pc1, r) q2 = getDividingPoint(pc1, pc2, r) q3 = getDividingPoint(pc2, pt2, r) line(q1, q2) line(q2, q3) r2 = getDividingPoint(q1, q2, r) r3 = getDividingPoint(q2, q3, r) line(r2, r3) r += 0.05 setColor("black") #cubicBezier(pt1, pc1, pc2, pt2) |
MEMO |
Eine kubische Bézier-Kurve ist durch 4 Punkte bestimmt. In GPanel kannst du sie mit der Funktion cubicBezier() zeichnen. Es werden die aktuelle Zeichnungsfarbe und Liniendicke verwendet. |
INTERAKTIVER KURVENDESIGN |
from gpanel import * def updateGraphics(): # erase all clear() # draw points lineWidth(1) for i in range(4): move(points[i]) if active == i: setColor("green") fillCircle(2) setColor("black") circle(2) # draw tangents setColor("red") line(points[0], points[1]) line(points[3], points[2]) # draw Bezier curve setColor("blue") lineWidth(3) cubicBezier(points[0], points[1], points[2], points[3]) def onMouseDragged(x, y): if active == -1: return points[active][0] = x points[active][1] = y updateGraphics() def onMouseReleased(x, y): active = -1 updateGraphics() def onMouseMoved(x, y): global active active = near(x, y) updateGraphics() def near(x, y): for i in range(4): rsquare = (x - points[i][0]) * (x - points[i][0]) + (y - points[i][1]) * (y - points[i][1]) if rsquare < 4: return i return -1 pt1 = [20, 20] pc1 = [10, 80] pc2 = [90, 80] pt2 = [80, 20] points = [pt1, pc1, pc2, pt2] active = -1 makeGPanel(0, 100, 0, 100, mouseDragged = onMouseDragged, mouseReleased = onMouseReleased, mouseMoved = onMouseMoved) updateGraphics() |
MEMO |
Es gibt auch kompliziertere Datenstrukturen, wie beispielsweise eine Liste, deren Elemente wieder Listen sind. Willst du beispielsweise die x-Koordinate von P1 ansprechen, so verwendest du points[1][0], also ein doppeltes Klammerpaar. Heute sind die Bézierkurven ein wichtiges Designhilfsmittel im CAD-Bereich. [Lit.] |
AUFGABEN |
|