Komplexe Zahlen - Das Apfelmännchen
von Fabian Geißler
Übersicht
Dieses Tutorial ist eine Einführung in die Mathematik der komplexen Zahlen. Im Anschluss an etwas Theorie dazu wird in C# und VB.NET eine Klasse zur Darstellung einer komplexen Zahl implementiert. Für deren Demonstration wird anschliessend ein Programm gezeigt, welches mit Hilfe der komplexen Zahlen die Mandelbrotmenge, das sog. Apfelmännchen, darstellt.
Komplexe Zahlen
Eine komplexe Zahl wird durch zwei reelle Zahlen beschrieben, Realteil und Imaginärteil. Der Imaginärteil ist durch die Multiplikation mit der imaginären Einheit gekennzeichnet. Die Imaginäre Einheit ist als die Quadratwurzel von -1 definiert (). Demzufolge ist , was bei den späteren Berechnungen eine wichtige Rolle spielt.
Beispiel:
: Realteil (Re)
: Imaginärteil (Im)
Rechnen mit komplexen Zahlen
Da wir nun den Aufbau von komplexen Zahlen kennen ist das Rechnen damit nicht mehr so komplex, wie man jetzt vielleicht vermuten mag. (Im Folgenden ist x der Realteil und y der Imaginärteil einer komplexen Zahl)
Anmerkung der Redaktion: In der Technik wird als imaginäre Einheit sehr häufig nicht der Buchstabe i, sondern j verwendet, um eine Verwechslung mit der Stromstärke zu vermeiden.
Addition
Bei der Addition werden der Realteil von Zahl 1 und der Realteil von Zahl 2 addiert. Gleiches gilt für die Imaginärteile:
Beispiel:
Subtraktion
Die Subtraktion funktioniert ähnlich wie die Addition, nur dass eben subtrahiert wird:
Beispiel:
Multiplikation
Jetzt wird es schon etwas komplexer, da nach den mathematischen Regeln ausgeklammert werden muss:
Da hierbei der Term entsteht, welcher gemäss Definition -1 ergibt, wird nur die reelle Zahl verwendet und negiert:
Beispiel:
Division
Die Division ist die schwierigste Grundrechenoperation bei den komplexen Zahlen. In einem ersten Schritt muss die imaginäre Einheit im Nenner (unter dem Bruchstrich) entfernt werden, was durch Erweiterung mit dem komplex-konjugierten Nenner möglich ist. Eine komplex-konjugierte Zahl entsteht, wenn das Vorzeichen des Imaginärteils umgedreht wird:
Bei dieser Erweiterung entsteht im Nenner folgender binomischer Term:
Damit ergibt sich für die Division:
Beispiel:
Darstellung einer komplexen Zahl
Reelle Zahlen können noch auf einem Zahlenstrahl dargestellt werden. Doch was machen wir mit den komplexen Zahlen? Ganz einfach, wir tragen sie in ein Koordinatensystem (die gaußsche Zahlenebene) ein. Dabei ergibt der Realteil die x-Koordinate und der Imaginärteil die y-Koordinate.
Daraus ergibt sich eine weitere Schreibweise für komplexe Zahlen. Bei dieser wird das Argument (der Winkel zur Realachse) und der Betrag r der Zahl angegeben. Der Betrag errechnet sich wie folgt:
wobei Re der Realteil und Im der Imaginärteil der Zahl sind. Das Argument wird wie folgt berechnet:
Nun zum Programmieren
Da wir uns nun durch die ganze Theorie gekämpft haben, beginnen wir jetzt mit dem Programmieren. Im Folgenden wird eine Klasse ComplexNumber erstellt, welche eine komplexe Zahl beschreibt.
Anmerkung der Redaktion: Die C#-Ausführung der folgenden Codeausschnitte wurde jeweils etwas gekürzt. Die volle Version kann am Ende des Artikels als Beispielprojekt heruntergeladen werden.
VB.NET
Public Class ComplexNumber Private mReal As Double Private mImag As Double Public Overrides Function ToString() As String Return "z = " & mReal.ToString() & " + " & mImag.ToString() & "i" End Function Public Sub New(ByVal Real As Double, ByVal Imag As Double) mReal = Real mImag = Imag End Sub Public Shared Function Parse(ByVal Number As String) As ComplexNumber 'Number muss etwa so aussehen: "5,59+56i" oder "5,59+56*i" Number = Number.Replace(" ", "") Number = Number.Replace("*", "") Number = Number.Replace(".", ",") If Number(Number.Length - 1) = "i"c Then Return New ComplexNumber(Double.Parse(Number.Substring(0, _ Number.IndexOf("+"))), _ Double.Parse(Number.Substring(Number.IndexOf("+") + 2))) Else Return Nothing End If End Function Public Property RealPart() As Double Get Return mReal End Get Set(ByVal value As Double) mReal = value End Set End Property Public Property ImaginaerPart() As Double Get Return mImag End Get Set(ByVal value As Double) mImag = value End Set End Property Public Function GetBetrag() As Double Return Math.Sqrt(mReal * mReal + mImag * mImag) End Function Public Function GetArgument() As Double Dim tan As Double = Math.Atan(mImag / mReal) * 180 / Math.PI If mReal < 0 Then tan += 180 End If If tan < 0 Then tan = 360 + tan End If Return tan End Function #Region "operators" Public Shared Operator +(ByVal z0 As ComplexNumber, _ ByVal z1 As ComplexNumber) As ComplexNumber Return New ComplexNumber(z0.mReal + z1.mReal, z0.mImag + z1.mImag) End Operator Public Shared Operator -(ByVal z0 As ComplexNumber, _ ByVal z1 As ComplexNumber) As ComplexNumber Return New ComplexNumber(z0.mReal - z1.mReal, z0.mImag - z1.mImag) End Operator Public Shared Operator *(ByVal z0 As ComplexNumber, _ ByVal z1 As ComplexNumber) As ComplexNumber Return New ComplexNumber(z0.mReal * z1.mReal - z0.mImag * z1.mImag, _ z0.mReal * z1.mImag + z1.mReal * z0.mImag) End Operator Public Shared Operator ^(ByVal z0 As ComplexNumber, _ ByVal ex As Integer) As ComplexNumber Dim ret As ComplexNumber = z0 For i As Integer = 1 To ex - 1 ret *= z0 Next Return ret End Operator Public Shared Operator /(ByVal z0 As ComplexNumber, _ ByVal z1 As ComplexNumber) As ComplexNumber Dim real As Double = (z0.mReal * z1.mReal + z0.mImag * z1.mImag) / _ (z1.mReal * z1.mReal + z1.mImag * z1.mImag) Dim imag As Double = (z0.mImag * z1.mReal - z0.mReal * z1.mImag) / _ (z1.mReal * z1.mReal + z1.mImag * z1.mImag) Return New ComplexNumber(real, imag) End Operator #End Region End Class
C#
public class ComplexNumber { private double mReal; private double mImag; public override string ToString() { return "z = " + mReal.ToString() + " + " + mImag.ToString() + "i"; } public ComplexNumber(double Real, double Imag) { mReal = Real; mImag = Imag; } public static ComplexNumber Parse(string Number) { //Number muss etwa so aussehen: "5,59+56i" oder "5,59+56*i" Number = Number.Replace(" ", ""); Number = Number.Replace("*", ""); Number = Number.Replace(".", ","); if (Number[Number.Length - 1] == 'i') { return new ComplexNumber( double.Parse(Number.Substring(0, Number.IndexOf("+"))), double.Parse(Number.Substring(Number.IndexOf("+") + 2))); } else return null; } public double RealPart { get { return mReal; } set { mReal = value; } } public double ImaginaerPart { get { return mImag; } set { mImag = value; } } public double GetBetrag() { return Math.Sqrt(mReal * mReal + mImag * mImag); } public double GetArgument() { double tan = Math.Atan(mImag / mReal) * 180 / Math.PI; if (mReal < 0) tan += 180; if (tan < 0) tan = 360 + tan; return tan; } #region operators public static ComplexNumber operator +(ComplexNumber z0, ComplexNumber z1) { return new ComplexNumber(z0.mReal + z1.mReal, z0.mImag + z1.mImag); } public static ComplexNumber operator -(ComplexNumber z0, ComplexNumber z1) { return new ComplexNumber(z0.mReal - z1.mReal, z0.mImag - z1.mImag); } public static ComplexNumber operator *(ComplexNumber z0, ComplexNumber z1) { return new ComplexNumber(z0.mReal * z1.mReal - z0.mImag * z1.mImag, z0.mReal * z1.mImag + z1.mReal * z0.mImag); } public static ComplexNumber operator ^(ComplexNumber z0, int ex) { ComplexNumber ret = z0; for (int i = 1; i < ex; i++) ret *= z0; return ret; } public static ComplexNumber operator /(ComplexNumber z0, ComplexNumber z1) { double real = (z0.mReal * z1.mReal + z0.mImag * z1.mImag) / (z1.mReal * z1.mReal + z1.mImag * z1.mImag); double imag = (z0.mImag * z1.mReal - z0.mReal * z1.mImag) / (z1.mReal * z1.mReal + z1.mImag * z1.mImag); return new ComplexNumber(real, imag); } #endregion }
Das Apfelmännchen
Wir wissen nun einiges über komplexe Zahlen, also wenden wir uns nun einer Anwendung, dem Apfelmännchen, zu. Das Apfelmännchen trägt den Namen, da es in der Grundform wie ein Apfel aussieht. Der eigentliche Name, Mandelbrotmenge, kommt vom Entdecker Benoît Mandelbrot. Die Mandelbrot Menge ist ein Fraktal, also eine Punktmenge, die eine gewisse Selbstähnlichkeit aufweist und auf beliebig großer Vergrößerung immer noch eine Struktur aufweist. Trotz dessen ist das Zeichnen der Mandelbrot Menge am Computer nicht besonders schwierig, sondern eher etwas rechenaufwändig.
Der Sichtbereich
Bevor wir zeichnen können müssen wir den Bereich festlegen, den wir zeichnen wollen. Um das Apfelmännchen komplett sehen zu können müssen wir den Ausschnitt so wählen, dass in der oberen linken Ecke unserer Zeichenebene der Realteil x=-2 und der Imaginärteil y=-1 beträgt. In der unteren rechten Ecke muss der Realteil x=1 und der Imaginärteil y=1 betragen. Wenn wir ein Rechteck betrachten hat dieses also Den x-Wert -2, den y-Wert -1, die Höhe 2 und die Breite 3. Da wir in unserem Programm natürlich auch zoomen wollen, müssen wir uns vorher eine neue Struktur erstellen, um mit Doubles arbeiten zu können:
Public Structure RectangleD Private mWidth As Double Private mHeight As Double Private mX As Double Private mY As Double Public Sub New(ByVal Width As Double, _ ByVal Height As Double, _ ByVal X As Double, _ ByVal Y As Double) mWidth = Width mHeight = Height mX = X mY = Y End Sub Public Property Y() As Double Get Return mY End Get Set(ByVal value As Double) mY = value End Set End Property Public Property X() As Double Get Return mX End Get Set(ByVal value As Double) mX = value End Set End Property Public Property Width() As Double Get Return mWidth End Get Set(ByVal value As Double) mWidth = value End Set End Property Public Property Height() As Double Get Return mHeight End Get Set(ByVal value As Double) mHeight = value End Set End Property End Structure Public Structure PointD Private mx As Double Private my As Double Public Property x() As Double Get Return mx End Get Set(ByVal value As Double) mx = value End Set End Property Public Property y() As Double Get Return my End Get Set(ByVal value As Double) my = value End Set End Property Public Sub New(ByVal x As Double, ByVal y As Double) mx = x my = y End Sub End Structure
public struct RectangleD { private double mWidth; private double mHeight; private double mX; private double mY; public RectangleD(double Width, double Height, double X, double Y) { mWidth = Width; mHeight = Height; mX = X; mY = Y; } public double Y { get { return mY; } set { mY = value; } } public double X { get { return mX; } set { mX = value; } } public double Width { get { return mWidth; } set { mWidth = value; } } public double Height { get { return mHeight; } set { mHeight = value; } } }
Die Mathematik des Apfelmännchens
Um das Apfelmännchen zu zeichnen muss man erst einmal seinen mathematischen Hintergrund kennen. Das Apfelmännchen ist eine Menge von Punkten auf der gaußschen Ebene, die meistens schwarz dargestellt werden. Das Fraktal wird über die Rekursionsgleichung berechnet, wobei c der Wert des Punktes in der gaußschen Ebene (komplexe Zahl) ist und . Dabei entstehen entweder Komplexe Zahlen, die immer annähernd gleich bleiben oder nach kurzer Zeit gegen unendlich streben. Es ist mathematisch bewiesen, dass eine Komplexe Zahl, deren Betrag größer als zwei ist, beim Potenzieren gegen unendlich strebt, also wird die Rekursion dann abgebrochen, wenn . Bei den anderen Zahlen, die später die Mandelbrotmenge darstellen, würde n bis unendlich gehen, da deren Betrag nie über 2 geht. Da wir am Computer jedoch keine Schleife bis unendlich laufen lassen können müssen wir etwas schummeln, indem wir nach einem bestimmten Wert abbrechen und den getesteten Punkt als zur Menge zugehörig zählen. Die Farben außerhalb der Menge entstehen durch das Einfärben des Pixels mit der Farbe, die einer bestimmten Anzahl von Iterationen (Rekursionsschritte, also n) zugeordnet ist.
Die Programmierung
Bevor wir nun zur Hauptklasse kommen, müssen wir noch eine Struktur erstellen: SizeD, für die Angabe einer Größe mit doppelter Genauigkeit:
Public Structure SizeD Private mWidth As Double Private mHeight As Double Public Sub New(ByVal Width As Double, _ ByVal Height As Double) mWidth = Width mHeight = Height End Sub Public Property Width() As Double Get Return mWidth End Get Set(ByVal value As Double) mWidth = value End Set End Property Public Property Height() As Double Get Return mHeight End Get Set(ByVal value As Double) mHeight = value End Set End Property End Structure
public struct SizeD { private double mWidth; private double mHeight; public SizeD(double Width, double Height) { mWidth = Width; mHeight = Height; } public double Width { get { return mWidth; } set { mWidth = value; } } public double Height { get { return mHeight; } set { mHeight = value; } } }
Nun zur Hauptklasse ClassMandel:
Klassenvariablen
Imports System.Drawing.Imaging Imports System.Runtime.InteropServices Imports System.IO Public Class ClassMandel Private mImageSize As New Size(640, 480) 'Größe des Bildes, das gerendert wird Private mPosition As New RectangleD(4, 3, -2.5, -1.5) 'zu rendernder Ausschnitt des Fraktals Private mStartVal As New ComplexNumber(0, 0) 'z_0 Private mPal As ColorPalette = New Bitmap(1, 1, PixelFormat.Format8bppIndexed).Palette 'Palette (Später verwendet für Farbrotation) Private mProg As Double = 0 'Renderfortschritt in Prozent Private mPower As Integer = 2 'Exponent von z in der Rekursionsgleichung Private mIterations As Integer = 300 'Maximal zu durchlaufende Iterationen '... End Class
private Size mImageSize = new Size(640, 480); private RectangleD mPosition = new RectangleD(4, 3, -2.5, -1.5); private ComplexNumber mStartVal = new ComplexNumber(0,0); private ColorPalette mPal = new Bitmap(1, 1, PixelFormat.Format8bppIndexed).Palette; private double mProg = 0; private int mPower = 2; private int mIterations = 300;
Properties
'... Public Property Palette() As ColorPalette Get Return mPal End Get Set(ByVal value As ColorPalette) mPal = value End Set End Property Public Property StartPos() As ComplexNumber Get Return mStartVal End Get Set(ByVal value As ComplexNumber) mStartVal = value End Set End Property Public Property Power() As Integer Get Return mPower End Get Set(ByVal value As Integer) mPower = value End Set End Property Public Property ViewRect() As RectangleD Get Return mPosition End Get Set(ByVal value As RectangleD) mPosition = value End Set End Property Public Property ImageSize() As Size Get Return mImageSize End Get Set(ByVal value As Size) mImageSize = value End Set End Property Public Property Iterations() As Integer Get Return mIterations End Get Set(ByVal value As Integer) mIterations = value End Set End Property Public ReadOnly Property Progress() As Double Get Return mProg End Get End Property '...
public ColorPalette Palette { get { return mPal; } set { mPal = value; } } public ComplexNumber StartPos { get { return mStartVal; } set { mStartVal = value; } } public int Power { get { return mPower; } set { mPower = value; } } public RectangleD ViewRect { get { return mPosition; } set { mPosition = value; } } public Size ImageSize { get { return mImageSize; } set { mImageSize = value; } } public int Iterations { get { return mIterations; } set { mIterations = value; } } public double Progress { get { return mProg; } }
Hilfsroutinen
'... Private Function Recur(ByVal z As ComplexNumber, ByVal c As ComplexNumber) As ComplexNumber 'Hier ist nur die Rekursionsgleichung extra (für etwaige veränderungen) Return (z ^ mPower) + c '([pixel] ^ pow + start) End Function ' Startwert für z | Zweitmember der Rekursionsgleichung Private Function DoRecur(ByVal Start As ComplexNumber, ByVal c As ComplexNumber) As Integer 'Diese Routine führt die Rekursion durch Dim n As Integer = 0 Dim z As ComplexNumber = Start While n < mIterations AndAlso Not (z.ImaginaerPart * z.ImaginaerPart + z.RealPart * z.RealPart > 4) 'Wurzelziehen ist langsam n += 1 z = Recur(z, c) End While Return n End Function Private Function GetStep() As SizeD 'schrittgröße im Koordinatensystem errechnen, damit jedes Pixel richtig berechnet wird Return New SizeD(mPosition.Width / mImageSize.Width, mPosition.Height / mImageSize.Height) End Function Public Function GetPalFile(ByVal FileName As String) As ColorPalette 'Einlesen eines *.pal Files (Aufbau: 256*4 Bytes = 256 Farben) Dim bmp As New Bitmap(1, 1, PixelFormat.Format8bppIndexed) Dim pal As ColorPalette = bmp.Palette 'Palette mit 256 Einträgen übernehmen Dim stream As New FileStream(FileName, FileMode.Open) For i As Integer = 0 To pal.Entries.Length - 1 pal.Entries(i) = Color.FromArgb(stream.ReadByte(), stream.ReadByte(), _ stream.ReadByte(), stream.ReadByte()) Next stream.Close() Return pal End Function 'Punkt, an dem die maus geklickt wurde | Punkt, an dem die Maus losgelassen wurde Public Function GetNewRect(ByVal Down As Point, ByVal Up As Point) As RectangleD 'Berechnet den neuen Ausschnitt im Koordinatensystem für punkte auf der Zeichenfläche (Punkte in Pixel) Dim Steps As SizeD = GetStep() Dim DownP As New PointD(mPosition.X + (Steps.Width * Down.X), mPosition.Y + (Steps.Height * Down.Y)) Dim UpP As New PointD(mPosition.X + (Steps.Width * Up.X), mPosition.Y + (Steps.Height * Up.Y)) Dim minX As Double = Math.Min(DownP.X, UpP.X) Dim minY As Double = Math.Min(DownP.Y, UpP.Y) Dim maxX As Double = Math.Max(DownP.X, UpP.X) Dim maxY As Double = Math.Max(DownP.Y, UpP.Y) Return New RectangleD(maxX - minX, maxY - minY, minX, minY) End Function '...
private ComplexNumber Recur(ComplexNumber z, ComplexNumber c) {//Hier ist nur die Rekursionsgleichung extra (für etwaige veränderungen) return (z ^ mPower) + c; //([pixel] ^ pow + start) } private int DoRecur(ComplexNumber Start/*Startwert für z*/, ComplexNumber c/*Zweitmember der Rekursionsgleichung*/) {//Diese Routine führt die Rekursion durch int n = 0; ComplexNumber z = Start; while (n < mIterations && !(z.ImaginaerPart * z.ImaginaerPart + z.RealPart * z.RealPart > 4)) //Wurzelziehen ist langsam { n++; z = Recur(z, c); } return n; } private SizeD GetStep() {//schrittgröße im Koordinatensystem errechnen, damit jedes Pixel richtig berechnet wird return new SizeD(mPosition.Width / mImageSize.Width, mPosition.Height / mImageSize.Height); } public ColorPalette GetPalFile(string FileName) {//Einlesen eines *.pal Files (Aufbau: 256*4 Bytes = 256 Farben) Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed); ColorPalette pal = bmp.Palette;//Palette mit 256 Einträgen übernehmen FileStream stream = new FileStream(FileName, FileMode.Open); for (int i = 0; i < pal.Entries.Length; i++) { pal.Entries[i] = Color.FromArgb(stream.ReadByte(), stream.ReadByte(), stream.ReadByte(), stream.ReadByte()); } stream.Close(); return pal; } public RectangleD GetNewRect(Point Down/*Punkt, an dem die maus geklickt wurde*/, Point Up/*Punkt, an dem die Maus losgelassen wurde*/) {//Berechnet den neuen Ausschnitt im Koordinatensystem für punkte auf der Zeichenfläche (Punkte in Pixel) SizeD Steps = GetStep(); PointD DownP = new PointD(mPosition.X + (Steps.Width * Down.X), mPosition.Y + (Steps.Height * Down.Y)); PointD UpP = new PointD(mPosition.X + (Steps.Width * Up.X), mPosition.Y + (Steps.Height * Up.Y)); double minX = Math.Min(DownP.X, UpP.X); double minY = Math.Min(DownP.Y, UpP.Y); double maxX = Math.Max(DownP.X, UpP.X); double maxY = Math.Max(DownP.Y, UpP.Y); return new RectangleD(maxX - minX, maxY - minY, minX, minY); }
Hauptroutine
'... Public Function DrawMandelbrot() As Bitmap 'Zeichnet die Mandelbrotmenge mit den voreingestellten Parametern Dim bmp As New Bitmap(mImageSize.Width, mImageSize.Height, PixelFormat.Format8bppIndexed) bmp.Palette = mPal Dim Steps As SizeD = GetStep() Dim NColorP As Double Dim tmp As Integer Dim data0 As BitmapData = bmp.LockBits(New Rectangle(Point.Empty, mImageSize), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed) Dim ColorData As Byte() = New Byte(data0.Stride * mImageSize.Height - 1) {} Marshal.Copy(data0.Scan0, ColorData, 0, ColorData.Length) For x As Integer = 0 To mImageSize.Width - 1 'Jedes Pixel durchlaufen For y As Integer = 0 To mImageSize.Height - 1 tmp = DoRecur(mStartVal, New ComplexNumber(mPosition.X + (Steps.Width * x), mPosition.Y + (Steps.Height * y))) 'Rekursion durchführen NColorP = (tmp * 1.0) / mIterations Dim s As Byte = CByte(Math.Truncate(NColorP * 255)) 'Einfärben TRICK: da 8Bit Indexed Bitmap können verschiedene Farbpaletten geladen werden) ColorData(x + y * data0.Stride) = s Next mProg = (x * 1.0) / mImageSize.Width Application.DoEvents() Next Marshal.Copy(ColorData, 0, data0.Scan0, ColorData.Length) bmp.UnlockBits(data0) Return bmp End Function
public Bitmap DrawMandelbrot() {//Zeichnet die Mandelbrotmenge mitden voreingestellten Parametern Bitmap bmp = new Bitmap(mImageSize.Width, mImageSize.Height, PixelFormat.Format8bppIndexed); bmp.Palette = mPal; SizeD Steps = GetStep(); double NColorP; int tmp; BitmapData data0 = bmp.LockBits(new Rectangle(Point.Empty, mImageSize), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); byte[] ColorData = new byte[data0.Stride * mImageSize.Height]; Marshal.Copy(data0.Scan0, ColorData, 0, ColorData.Length); for (int x = 0; x < mImageSize.Width; x++) {//Jedes Pixel durchlaufen for (int y = 0; y < mImageSize.Height; y++) { tmp = DoRecur(mStartVal, new ComplexNumber(mPosition.X + (Steps.Width * x), mPosition.Y + (Steps.Height * y)));//Rekursion durchführen NColorP = (tmp * 1D) / mIterations; byte s = (byte)(NColorP * 255); ColorData[x + y * data0.Stride] = s;//Einfärben //(TRICK: da 8Bit Indexed Bitmap können verschiedene Farbpaletten geladen werden) } mProg = (x * 1D) / mImageSize.Width; Application.DoEvents(); } Marshal.Copy(ColorData, 0, data0.Scan0, ColorData.Length); bmp.UnlockBits(data0); return bmp; }
Genutzte Namensbereiche
- System
- System.Collections.Generic
- System.Linq
- System.Text
- System.Drawing
- System.Windows.Forms
- System.Drawing.Imaging
- System.Runtime.InteropServices
- System.IO
Beispielprogramm
Das Beispielprogramm ist in C# geschrieben, ist aber sehr einfach gehalten und lässt sich ohne viel Mühe nach VB.Net übersetzen.
Zoomen kann man über Drag & Drop auf der Zeichenfläche.
Wer sich Zeit nimmt und ein Stückchen zoomt, der wird feststellen, dass nach einer bestimmten Tiefe die Grafik wie verpixelt wirkt. Das kommt durch die "Ungenauigkeit" vom Zahlentyp Double. Also wenn sich jemand von den Assemblerprogrammierern für so etwas interessiert und etwas Zeit hat, kann er ja mal einen Zahlentyp schreiben, der etwas genauer ist als double (vielleicht mit 50 Nachkommastellen ohne Exponentialschreibweise). Ich wäre dann auch daran interessiert :-)
Screenshot
Download des Beispielprogramms
Ihre Meinung
Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.