Ok, wir haben Ende 2023. Aber immernoch gibt es manchmal Situationen, wo ich weder mit Code noch mit Pseudocode was gut erklären kann. Dann hilft mir oft ein Relikt aus meiner Kindheit namens GFA-Basic. Der Interpreter kümmert sich um die Einrückung und Colorierung während ich tippe. Es gibt kaum Klammern und keine Semicola.
Menschen können das fast vom Blatt lesen, und darum geht es.
Ihr kennt sicher alle diese schönen Kurven, die galant durch eine Reihe von Kontrollpunkten laufen? Nun, gestern hab ich versucht, Emilio zu erklären, wie solche Bezierkurven entstehen:
Durch Interpolation zwischen Punkten, die ihrerseits Interpolationen sind. Vielleicht erkläre ich später noch ein paar Fallstricke zu Anzahl und Reihenfolge dieser Punkte, aber für heute nehmen wir die einfachste Form: Jeder Ankerpunkt, durch den die Kurve läuft, hat zwei spiegelbildliche Kontrollpunkte, durch feine Nadeln zu ihrem Ankerpunkt dargestellt. Deswegen brauchen wir nur einen davon abzuspeichern, der andere leitet sich davon ab.
Der Trick ist nun, dass man die Kurve in Abschnitten zwischen je zwei Ankerpunkten zeichnet. Den ersten Ankerpunkt wird die Kurve in Richtung ausgehender Kontrollpunkt verlassen, den zweiten Ankerpunkt in Richtung eingehender Kontrollpunkt erreichen. Dazu lässt man einen Parameter von 0 nach eins wandern und nutzt ihn insgesamt sechs mal zur Interpolation zwischen je zwei Punkten:
Zunächst errechnet man drei Zwischenpunkte aus vieren, aus denen sich wiederum zwei Zwischenpunkte ergeben, und zwischen diesen liegt der eine gesuchte Punkt, der die eigentliche Kurve zeichnet.
Hier ist der GFA-Basic-Code, um einige zufällige Punkte zu erzeugen und dann eine Bezierkurve durch sie zu zeichnen:
' *** Für Emilio mal eben BezierKurven visualisiert
' *** Remember: We have to calculate 3 levels of interpolation in order to
' *** get those curves going through control points continuously
' ** INITIALISIERUNG
Type Int2 :
x As Short
y As Short
EndType
Local Short maxSegment = 18
Local Short maxContRange = 512
Local Short maxSteps = 120
Global Tang(maxSegment) As Int2
Global Cont(maxSegment) As Int2
Global ext, p As Int2
AutoRedraw = True
OpenW 1, , , 1600, 1280, 0
Global res As Int2 : res.x = _X : res.y = _Y
randomPoints(maxSegment, maxContRange)
DrawBezier(maxSegment, maxSteps)
'Warteschleife
Repeat
DoEvents
Until ext Or InKey$ = #27
CloseW 1
Sub randomPoints(maxSegment&, maxContRange&)
' Generate and draw points
Local T As Int2, C As Int2, segment
For segment = 0 To maxSegment - 1
' tangent points
T.x = Rand(res.x * .8) + res.x * .1
T.y = Rand(res.y * .8) + res.y * .1
Color RGB(128, 128, 128)
Box T.x - 2, T.y - 2, T.x + 3, T.y + 3
' write to array
Tang(segment).x = T.x
Tang(segment).y = T.y
' control points
C.x = Rand(maxContRange) - maxContRange / 2
C.y = Rand(maxContRange) - maxContRange / 2
Color RGB(64, 192, 128)
Box T.x + C.x - 1, T.y + C.y - 1, T.x + C.x + 1, T.y + C.y + 1
Color RGB(128, 144, 255)
Box T.x - C.x - 1, T.y - C.y - 1, T.x - C.x + 1, T.y - C.y + 1
Cont(segment).x = C.x
Cont(segment).y = C.y
Next
Return
Sub DrawBezier(maxSegment&, maxSteps&)
Local Int segment, step, j
Local Float s
Local T0 As Int2, T1 As Int2, C0 As Int2, C1 As Int2
Local R As Int2, G As Int2, B As Int2
Local GR As Int2, RB As Int2
Local Z As Int2, old As Int2
For j = 0 To maxSegment - 1
T0 = Tang(j)
C0 = Cont(j)
' make control point absolute:
C0.x += T0.x : C0.y += T0.y
T1 = Tang((j + 1) % maxSegment)
C1 = Cont((j + 1) % maxSegment)
' mirror control point 1 and make absolute:
C1.x = T1.x - C1.x : C1.y = T1.y - C1.y
For step = 0 To maxSteps
s = step / maxSteps
' Interpolation Level 1:
' grüne Punkte = Tangentialpunkt 0 bis Kontrollpunkt 0
interpol(T0, C0, s, G)
Color RGB(64, 192, 128)
Pset G.x, G.y
' blaue Punkte = Spiegelung von Kontrollpunkt 1 bis Tangentialpunkt 1
interpol(C1, T1, s, B)
Color RGB(128, 144, 255)
Pset B.x, B.y
' rote Punkte = Interpolation zwischen Kontrollpunkten
interpol(C0, C1, s, R)
Color RGB(255, 192, 184)
Pset R.x, R.y
' Interpolation Level 2:
' grün-rote Interpolation
interpol(G, R, s, GR)
' rot-blaue Interpolation
interpol(R, B, s, RB)
' Interpolation Level 3:
' schwarZe Punkte = BeZierkurve
old = Z
interpol(GR, RB, s, Z) ' in Z wird das Ergebnis geschrieben
If step > 0
Color RGB(0, 0, 0)
Line old.x, old.y, Z.x, Z.y
EndIf
Next
Next
Return
Sub interpol(ByRef start As Int2, ByRef end As Int2, s, ByRef p As Int2)
p.x = start.x * (1 - s) + end.x * s
p.y = start.y * (1 - s) + end.y * s
Return
Sub Win_1_MouseWheel(Buttons&, Delta%, MseX%, MseY%)
ext = Buttons && 1
Return
Sub Win_1_ReSize
res.x = _X : res.y = _Y
Win_1.BorderStyle = 0
Return