Home | Index of Lectures | PDF Version of this Page |
Objectives |
Know the most imortant data structure of vector graphics. |
Learn to compute its geometric properties. |
Learn to scroll, zoom and rotate it. |
Learn how to make look it round. |
Summary |
A polygon is an array of xi, yi. |
Its perimeter its the sum all the distances pi to pi+1. |
Its area is a sum of trapezoids with the x-axis as upper line. |
The bounding box is the minimal axis-parallel rectangle around a polygon. |
There are at least 3 definitions for a mid point. |
Scroll is an addition of dx and dy. |
Zoom is a multiplication of zoomx and zoomy. |
Computing rotation needs 2 times cosinus(alpha) and sinus(alpha). |
A cubic Bézier curve connects pi and pi+3. pi+1 and pi+2 just determine the curvature. |
A cubic spline connects pi, pi+1, pi+2 and pi+3. There are no sharp edges at the joints pi and pi+3 between two splines. |
The parametric form of a straight line between p0 and p1: x = ( x1 - x0 ) * t + x0 and y = ( y1 - y0 ) * t + y 0. |
Technische Basis der Vektorgraphik sind die Vektor Displays (englisch: Random Access Cathode Ray Tubes = RA-CRTs). Dies sind Elektronenstrahlröhren mit Phosphorschirm und frei beweglichem Strahl ohne festem Strahlweg. Es gibt keine Zeilen und keine Pixel. Man lenkt den Strahl in beliebige Richtung über den Bildschirm und erhält feine, hochpräzise Linien. Diese Darstellungsweise ist ideal für technische Zeichnungen, Landkarten, Flugbewegungen etc. Siehe: Vorlesung über Katodenstrahl-Displays
( Problem: Vektor Displays können Bildern der realen Welt (Photos, TV) nicht darstellen; das können nur Rasterdisplays. )
Vertex (lateinisch Ecke, Plural Vertices, gesprochen: Wertizees) = Vektor = Punkt = Eckpunkt = Point ist der atomare Baustein der Vektorgraphik.
Im einfachsten Fall besteht er aus zwei Koordinaten mit den beiden Abständen vom linken und vom oberen Fensterrand.
Man unterscheidet:
1) 2D-Vertices mit 2 Floatkoordinaten: PointF{ float x; float y; }
2) 2D-Vertices mit 2 Integerkoordinaten: Point { int x; int y; }
3) 3D-Vertices mit 3 Floatkoordinaten: Vector3{ float x; float y; float z; }
4) 3D-Vertices wie Vector3
plus Farbe, Normale, Texturkoordinate etc. (siehe: Direct3D-Vertex Formate)
Für 2D-Graphik verwendet man in der Regel Typ 1), weil dieser Datentyp stufenlosen Zoom und Rotation zulässt und frei von Rundungsfehlern ist.
Einfache Malprogramme mit der Maus verwenden oft Datentyp 2), weil die Maus nur Integerkoordinaten liefert und die Malbefehle wie z.B. DrawLine()
Integerkoordinaten verlangen.
= wichtigster Datentyp der Vektorgraphik ist eine geordnete Menge von Vertices p[0], p[1], ... p[i], ... p[n-1]
, wobei die Vertices p[i]
durch Strecken DrawLine( p[i], p[i+1] )
verbunden sind. Diese Strecken dürfen sich nicht überkreuzen.
Man unterscheidet:
a) offenes Polygon (engl.: polyline): Startpunkt und Endpunkt sind nicht identisch. Offene Polygone haben eine Länge = length, aber keinen Umfang und keine Fläche.
b) geschlossenes Polygon: Startpunkt identisch mit Endpunkt. Das hat zur Folge, dass ein geschlossenes n
-Eck durch n+1
Vertices kodiert werden muss. Ein Dreieck hat also 4 Vertices P0, P1, P2, und P3==P0 ! Geschlossene Polygone haben einen Umfang = perimeter und eine Fläche = area.
Umwandlung a) → b): Man kann jedes offene Polygon schließen, indem man den nullten Vertex p[0]
an das Ende des Arrays kopiert.
Spricht man nur von Polygon ohne Adjektiv offen oder geschlossen, dann ist ein geschlossenes Polygon gemeint, auch wenn der letzte schließende Vertex fehlen sollte.
c) konvexes Polygon: Für alle beliebig gewählten zwei Punkte q0
und q1
aus dem Inneren des Polygons gilt: Die Verbindungsstrecken liegen immer vollständig im Polygon.
d) konkaves Polygon: Es gibt Punkte q0
und q1
aus dem Inneren des Polygons, deren Verbindungsstrecke nicht vollständig im Polygon liegt.
e) überschlagenes Polygon (engl.: nonsimple polygon): Polygonkanten überschneiden sich. Solche Polygone können keine Flächen beranden und sind ungeignet für Computergraphik.
Beispiele:
Polygone werden in Form von Arrays programmiert und gespeichert.
Es gibt zwei Basistypen von 2D-Polygon-Arrays:
a) Polygon als Array fester Länge: const Int32 n = 100; PointF[] p = new PointF[n];
Vorteil: einfache und schnelle Zugriffe.
Nachteil: Man muss den Speicherplatz fest reservieren, d.h. man muss n
kennen.
b) Polygon als dynamisches Array: ArrayList p = new ArrayList(); p.Add( p0 );
Vorteil: Man kann jederzeit Vertices an- oder einfügen oder löschen.
Nachteile: 1) Namespace using System.Collections;
notwendig.
2) Zugriffe langsamer als bei festem Array und kein Zugriff über Pointer möglich, weil die Vertices nicht speicherkompakt, sondern als verkettete Liste gespeichert sind.
3) Typecasting = explizite Typspezifizierung (obwohl keine Typkonvertierung stattfindet) beim lesen aus dem Array notwendig: p0 = (PointF)p[i];
, weil eine ArrayList
alle möglichen Objekte in bunter Reihenfolge enthalten darf.
Tip für Malprogramme: Speichern Sie doppelt: Sammeln sie die Vertices zunächst in einem dynamischen Array und kopieren Sie dieses später in ein festes Array.
Vier Beispiele für Speicherung und Zugriff auf Polygone
using System.Collections; //enthält ArrayList
const Int32 n = 100; //Länge des festen Arrays
Random r = new Random(); //Zufallszahlengenerator zum füllen der Arrays
Int32 i;
Pen mypen = new Pen( Color.Red, 1 );
Graphics g = this.CreateGraphics();
1)
Beispiel: Point
-Array fester Länge mit Integer-Koordinaten
Point[] p = new Point[n];
for ( i=0; i < n; i++ ) //write into array
{ p[i].X = r.Next(100); p[i].Y = r.Next(100); }
for ( i=0; i < n-1; i++ ) //read from array
g.DrawLine( mypen, p[i], p[i+1] );
2)
Beispiel: Point
-dynamisches Array variabler Länge mit Integer-Koordinaten
ArrayList p = new ArrayList();
for ( i=0; i < n; i++ ) //write into array
p.Add( new Point( r.Next(100), r.Next(100) ) );
for ( i=0; i < p.Count-1; i++ ) //read from array
g.DrawLine( mypen, (Point)p[i], (Point)p[i+1] );
3)
Beispiel: PointF
-Array fester Länge mit Float-Koordinaten
PointF[] p = new PointF[n];
for ( i=0; i < n; i++ ) //write into array)
{ p[i].X = 100f*(Single)r.NextDouble(); p[i].Y = 100f*(Single)r.NextDouble(); }
for ( i=0; i < n-1; i++ ) //read from array
{ Int32 x0 = Convert.ToInt32( p[i ].X );
Int32 y0 = Convert.ToInt32( p[i ].Y );
Int32 x1 = Convert.ToInt32( p[i+1].X );
Int32 y1 = Convert.ToInt32( p[i+1].Y );
g.DrawLine( mypen, x0, y0, x1, y1 );
}
4)
Beispiel: PointF
-dynamisches Array variabler Länge mit Float-Koordinaten
ArrayList p = new ArrayList();
for ( i=0; i < n; i++ ) //write into array
p.Add( new PointF( 100f*(Single)r.NextDouble(), 100f*(Single)r.NextDouble() ) );
for ( i=0; i < p.Count-1; i++ ) //read from array
{ Int32 x0 = Convert.ToInt32( ((PointF)p[i ]).X );
Int32 y0 = Convert.ToInt32( ((PointF)p[i ]).Y );
Int32 x1 = Convert.ToInt32( ((PointF)p[i+1]).X );
Int32 y1 = Convert.ToInt32( ((PointF)p[i+1]).Y );
g.DrawLine( mypen, x0, y0, x1, y1 );
}
Bei Malprogrammen, wo die Eckpunkte eines Polygons durch das Ereignis OnMouseMove(...)
geliefert werden, sind die Vertices nicht äquidistant, sondern die Punktabstände sind abhängig von der Mausgeschwindigkeit, der Geschwindigkeit des Rechners und von dessen momentaner Auslastung. Bewegt der User die Maus langsam, dann liegen die Vertices sehr dicht beieinander (oft Pixel neben Pixel), wenn er schnelle Bewegungen macht, liegen sie weit auseinander. Das erste Extrem (Vertices zu nah) läßt sich leicht folgendermaßen bekämpfen:
Innerhalb des Eventhandlers OnMouseMove(...)
berechnet man den Abstand des aktuellen Punktes zum Vorgänger mit dem Satz des Pythagoras im rechtwinkligen Dreieck: Quadrat über der Hypothenuse = Summe der Quadrate über beiden Katheten. Ist der Abstand zu klein, Punkt ignorieren.
Beispiel mit erzwungenem Minimalabstand von 10 Pixeln:
Point p0 = new Point(), p1 = new Point(); protected override void OnMouseDown( MouseEventArgs e ) { p0.X = e.X; //first vertex p0.Y = e.Y; //first vertex } protected override void OnMouseMove( MouseEventArgs e ) { if ( e.Button == MouseButtons.None ) return; //if no button pressed do nothing p1.X = e.X; Int32 dx = p1.X - p0.X; //horizontal distance p1.Y = e.Y; Int32 dy = p1.Y - p0.Y; //vertical distance if ( Math.Sqrt( dx*dx + dy*dy ) < 10 ) return; //if distance < 10 do nothing g.DrawLine( blackpen, p0, p1 ); p0 = p1; }
Man kann den OnMouseMove( ... )
-EventHandler erheblich beschleunigen, wenn man die Ungleichung quadriert und damit die Wurzel überflüssig macht. Dazu ersetzt man:
if ( Math.Sqrt( dx*dx + dy*dy ) < 10 ) return;
durch
if ( dx*dx + dy*dy < 100 ) return;
.
Sie finden Anwendungen unter:
www.miszalok.de/C_2DCis/C2_Draw/C2DCisDraw_d.htm und unter
www.miszalok.de/C_2DCis/C3_XML/C2DCisXML_d.htm und unter
www.miszalok.de/C_2DCis/C4_Anim/C2DCisAnim_d.htm
Gegeben sei
ein dynamischer Vertex-Array: ArrayList p = new ArrayList();
gefüllt mit Objekten vom Typ Point
Gesucht sei Double laenge;
Man benutzt für jede Teilstrecke den Satz des Pythagoras: Im rechtwinkligen Dreieck ist die Hypotenuse gleich der Wurzel aus der Summe der Kathetenquadrate. Die Summe aller Hypotenusen summiert sich zur gesamten Länge.
Double laenge = 0.0;
Point p0 = (Point)p[0];
for ( Int16 i=1; i < p.Count; p++ ) //Schleife fängt nicht bei 0 an, sondern bei 1
{ Point p1 = (Point)p[i];
Double dx = p1.X - p0.X; //horizontale Kathete
Double dy = p1.Y - p0.Y; //vertikale Kathete
laenge += Math.Sqrt( dx*dx + dy*dy ); //Hypotenuse
p0 = p1;
}
Gegeben sei
ein dynamischer Vertex-Array: ArrayList p = new ArrayList();
gefüllt mit Objekten vom Typ Point
Gesucht sei Double perimeter;
Zunächst kopiert man den Startpunkt des Polygons an dessen Ende, falls das nicht schon von vornherein der Fall ist.
Double perimeter = 0.0;
Point p0 = (Point)p[0];
if ( p0 != (Point)p[p.Count-1] ) p.Add( p0 ); //Polygon schließen
for ( Int16 i=1; i < p.Count; p++ ) //Schleife fängt nicht bei 0 an, sondern bei 1
{ Point p1 = (Point)p[i];
Double dx = p1.X - p0.X; //horizontale Kathete
Double dy = p1.Y - p0.Y; //vertikale Kathete
perimeter += Math.Sqrt( dx*dx + dy*dy ); //Hypotenuse
p0 = p1;
}
Sie finden eine Anwendung unter: www.miszalok.de/C_2DCis/C2_Draw/C2DCisDraw_d.htm
Gegeben sei
ein dynamischer Vertex-Array: ArrayList p = new ArrayList();
gefüllt mit Objekten vom Typ Point
Gesucht sei Double area;
Zunächst kopiert man den Startpunkt des Polygons an dessen Ende, falls das nicht schon von vornherein der Fall ist.
Double area = 0.0;
Point p0 = (Point)p[0];
if ( p0 != (Point)p[p.Count-1] ) p.Add( p0 ); //Polygon schließen
for ( Int16 i=1; i < p.Count; p++ ) //Schleife fängt nicht bei 0 an, sondern bei 1
{ Point p1 = (Point)p[i];
Double dx = p1.X - p0.X; //Breite des Trapezes
Double my = (p1.Y + p0.Y) / 2.0; //mittlerer y-Wert
area += dx * my; //Fläche des Trapezes
p0 = p1;
}
Die Fläche kann einen negativen Wert ergeben. Ihr Vorzeichen hängt von der Umlaufrichtung ab. Wenn die Umlaufrichtung so ist, dass die Fläche links von der Begrenzungslinie liegt, dann wird die Fläche positiv, andernfalls negativ. Will man ein Ergebnis, das von der Umlaufrichtung unabhängig und positiv ist, so muss man hinter die for
-Schleife schreiben:
area = Math.Abs( area );
Sie finden eine Anwendung unter: www.miszalok.de/C_2DCis/C2_Draw/C2DCisDraw_d.htm
= englisch: Bounding Box ist das kleinstmögliche achsparallele Gefängnis eines Polygons. Die Bounding Box ersetzt meistens das Polygon bei der Frage, ob die Maus auf das Polygon zeigt oder bei der Frage, ob zwei Polygone kollidieren.
Gegeben sei
ein dynamischer Vertex-Array: ArrayList p = new ArrayList();
gefüllt mit Objekten vom Typ Point
Gesucht sei Rectangle box; //bounding box;
Zunächst setzt man die 4 Mauern des Gefängnisses xmin, ymin, xmax, ymax
auf den Startpunkt des Polygons.
Int32 xmin, ymin, xmax, ymax; xmin = xmax = ( (Point)p[0] ).X; ymin = ymax = ( (Point)p[0] ).Y; for ( int i=1; i < p.Count; i++ ) { Point p0 = (Point)p[i] //nächste Polygonecke if ( p0.X < xmin ) xmin = p0.X; //verschiebe die linke Mauer nach links if ( p0.X > xmax ) xmax = p0.X; //verschiebe die rechte Mauer nach rechts if ( p0.Y < ymin ) ymin = p0.Y; //verschiebe die obere Mauer nach oben if ( p0.Y > ymax ) ymax = p0.Y; //verschiebe die untere Mauer nach unten } box = new Rectangle( xmin, ymin, xmax-xmin, ymax-ymin );
Anmerkung: In der Klasse Rectangle existieren die Properties X und Y auch unter den Namen Left und Top .Im Beispiel: xmin = box.X = box.Left undymin = box.Y = box.Top . |
Sie finden Anwendungen unter:
www.miszalok.de/C_2DCis/C2_Draw/C2DCisDraw_d.htm und unter
www.miszalok.de/C_2DCis/C4_Anim/C2DCisAnim_d.htm
Die Feststellung von Kollisionen ist schwierig, wenn die Figuren komplizierte Außenformen haben. Einfach ist hingegen die Feststellung der Kollision der umschreibenden Rechtecke. Beispiel:
Gegeben sei ein bewegliches Rechteck Rectangle box
und ein Array von stationären Rechtecken Rectangle[] boxes = new Rectangle[n];
.
Frage: Ist box
irgendwo angestoßen ?
Lösung zu Fuß: for ( int i=0; i<n; i++ ) { if ( box.X > boxes[i].X + boxes[i].Width ) continue; //box liegt zu weit rechts if ( box.Y > boxes[i].Y + boxes[i].Height ) continue; //box liegt zu weit unten if ( boxes[i].X > box.X + box.Width ) continue; //box liegt zu weit links if ( boxes[i].Y > box.Y + box.Height ) continue; //box liegt zu weit oben Debug.WriteLine("box ist an " + i.ToString() + " gestoßen.\r\n" ); } Lösung mit der Rectangle.IntersectsWith - Methode: for ( int i=0; i<n; i++ ) if ( box.IntersectsWith( boxes[i] ) ) Debug.WriteLine("box ist an " + i.ToString() + " gestoßen.\r\n" );
Man ersetzt in der Regel auch bei Mausabfragen die Graphikobjekte durch ihre umschreibenden Rechtecke.
Beispiel: Liegt der Mauszeiger e.X, e.Y
auf einem der Graphikobjekte i
mit dem umschreibenden Rechteck boxes[i]
?
Lösung mit der Rectangle.Contains - Methode: for ( int i=0; i<n; i++ ) if ( boxes[i].Contains( e.X, e.Y ) ) Debug.WriteLine("Die Maus zeigt auf " + i.ToString() + ".\r\n" );
Man verwendet zweckmäßig Gleitkommakoordinaten (Datentyp PointF
) und keine Integerkoordinaten (Datentyp Point
), weil die Mittelpunktsberechnung immer eine Division erfordert und deshalb selten ganze Zahlen liefert.
Es gibt vier verschiedene Mittelpunkte mp
eines Polygons p
, die ziemlich weit auseinander liegen können.
1 )
Mittelpunkt des umschreibenden Rechtecks
2a)
Mittelpunkt der Ecken
2b)
Mittelpunkt der gewichteten Seiten
2c)
Schwerpunkt einer homogen dichten Fläche
Details siehe English Version: Center of a polygon
Sie finden Anwendungen unter:
www.miszalok.de/C_2DCis/C2_Draw/C2DCisDraw_d.htm und unter
www.miszalok.de/C_2DCis/C4_Anim/C2DCisAnim_d.htm
Gegeben sei:
1)
eine Polygonlänge: const Int32 n = 100;
2)
PointF palt = new PointF[n];
3)
PointF pneu = new PointF[n]; //Ergebnis
Gesucht sei:
2D-Scroll = 2D-Translate = 2D-Verschiebung um die Beträge Single dx, dy
.
for ( i=0; i < palt.Count; i++ )
Es geht auch auf einem einzigen Polygon:
{ pneu[i].X = palt[i].X + dx;
pneu[i].Y = palt[i].Y + dy;
}
for ( i=0; i < p.Count; i++
{ palt[i].X += dx;
palt[i].Y += dy;
}
Gesucht sei: 2D-Zoom = 2D-Scaling = 2D-Vergrößerung/Verkleinerung um die Beträge Single zoomx, zoomy
.
Die Polygone palt
und pneu
müssen unbedingt float-Koordinaten besitzen (Datentyp: PointF
).
for ( i=0; i < palt.Count; i++ )
Es geht auch auf einem einzigen Polygon:
{ pneu[i].X = palt[i].X * zoomx;
pneu[i].Y = palt[i].Y * zoomy;
}
for ( i=0; i < p.Count; i++ )
{ palt[i].X *= zoomx;
palt[i].Y *= zoomy;
}
zoomx < 1.0f =
Verkleinerung und Verschiebung nach links
zoomy < 1.0f =
Verkleinerung und Verschiebung nach oben
zoomx > 1.0f =
Vergrößerung und Verschiebung nach rechts
zoomy > 1.0f =
Vergrößerung und Verschiebung nach unten
Beispiel:
Meistens sind die Verschiebungen unerwünscht. Man will ein Polygon "an Ort und Stelle" zoomen.
Man benötigt dazu einen Mittelpunkt mp
:
1)
Verschiebung des Polygons so, dass mp
auf den Nullpunkt zu liegen kommt.
2)
Zoom auf dem Nullpunkt
3)
Rückverschiebung
for ( i=0; i < palt.Count; i++ )
Es geht auch auf einem einzigen Polygon:
{ pneu[i].X = (palt[i].X - mp.X) * zoomx + mp.X;
pneu[i].Y = (palt[i].Y - mp.Y) * zoomy + mp.Y;
}
for ( i=0; i < p.Count; i
{ palt[i].X -= mp.X;
palt[i].Y -= mp.Y;
palt[i].X *= zoomx;
palt[i].Y *= zoomy;
palt[i].X += mp.X;
palt[i].Y += mp.Y;
}
Sie finden eine Anwendung unter: www.miszalok.de/C_2DCis/C4_Anim/C2DCisAnim_d.htm
Gesucht sei: 2D-Rotation = 2D-Drehung im Uhrzeigersinn um einen Winkel Single alpha
oder Double alpha
.
Drehachse der 2D-Rotation ist die unsichtbare Z-Achse, die den Bildschirm in der linken oberen Ecke der ClientArea senkrecht durchstößt.
Die Polygone palt
und pneu
müssen unbedingt Gleitkomma-Koordinaten besitzen (Datentyp: PointF
).
Double arcus = alpha * 2.0 * Math.PI / 360.0; //alpha in Bogenmaß
Es geht auch auf einem einzigen Polygon, allerdings mit einer Hilfsvariablen, weil man
Single cosinus = (Single)Math.Cos( arcus ); //cosinus(alpha) als float
Single sinus = (Single)Math.Sin( arcus ); // sinus(alpha) als float
for ( i=0; i < palt.Count; i++ )
{ pneu[i].X = palt[i].X * cosinus - palt[i].Y * sinus;
pneu[i].Y = palt[i].X * sinus + palt[i].Y * cosinus;
}
palt[i].X
zweimal im Original braucht:
for ( i=0; i < p.Count; i+
Will man gegen den Uhrzeigersinn drehen, dann vertauscht man die Vorzeichen vor den beiden
{ Single help = palt[i].X * cosinus - palt[i].Y * sinus;
palt[i].Y = palt[i].X * sinus + palt[i].Y * cosinus;
palt[i].X = help;
}
sinus
-Produkten:
for ( i=0; i < palt.Count; i++ )
{ pneu[i].X = palt[i].X * cosinus + palt[i].Y * sinus;
pneu[i].Y = -palt[i].X * sinus + palt[i].Y * cosinus;
}
Beispiel 90 Grad: pneu verschwindet durch die Drehung nach links aus dem Koordinatensystem.
Vorsicht: Wie beim 2D-Zoom ist auch mit jeder Rotation eine Verschiebung verbunden, die fast immer unerwünscht ist.
Will man an "Ort und Stelle" drehen, geht man vor wie beim Zoom:
1)
Verschiebung des Polygons so, dass mp
auf den Nullpunkt zu liegen kommt.
2)
Rotation auf dem Nullpunkt
3)
Rückverschiebung
for ( i=0; i < palt.Count; i++ )
{ Single x = palt[i].X - mp.X;
Single y = palt[i].Y - mp.Y;
pneu[i].X = x * cosinus - y * sinus + mp.X;
pneu[i].Y = x * sinus + y * cosinus + mp.Y;
}
Sie finden eine Anwendung unter: www.miszalok.de/C_2DCis/C4_Anim/C2DCisAnim_d.htm
= Strahlenbündel, wo alle Strahlenanfänge von einem Mittelpunkt ausgehen und alle Strahlenendpunkte gleichmäßig verteilt auf einem Kreis liegen.
Gegeben sei:
1)
ein Mittelpunkt: Point mid = new Point();
2)
ein Radius: Double radius = 100;
3)
die Anzahl der Strahlen des Sterns: Int16 const nn = 120;
4)
Farbe und Dicke der Strahlen: Pen mypen = new Pen( Color.Red, 5 );
5)
Figur mit der der Strahl enden soll: abgeschnitten, abgerundet, Pfeil etc:
mypen.EndCap = System.Drawing.Drawing2D.LineCap.DiamondAnchor;
Wenn man den Winkel arcus
eines Strahls (in Bogenmaß) kennt, erhält man seinen Endpunkt x,y
mit Hilfe der so genannten Parameterdarstellung der Kreisgleichung:
x = mid.X + radius * Math.Cos( arcus );
y = mid.Y + radius * Math.Sin( arcus );
Der Winkel in Grad zwischen je zwei benachbarten Strahlen beträgt 360.0 / nn
, der gleiche Winkel in Bogenmaß beträgt:
Double arcus_1 = 2.0 * Math.PI / nn;
Wir benötigen Speicherplatz für
nn
Endpunkte:
und füllen diesen Array mit folgender Schleife:
Point[] stern = new Point[nn];
for ( Int16 i=0; i < nn; i++ )
{ Double arcus_i = arcus_1 * i;
Double x = radius * Math.Cos( arcus_i );
Double y = radius * Math.Sin( arcus_i );
stern[i].X = mid.X + ConvertToInt32( x );
stern[i].Y = mid.Y + ConvertToInt32( y );
g.DrawLine( mypen, mid.X, mid.Y, stern[i].X, stern[i].Y );
}
Sie finden eine Anwendung unter: www.miszalok.de/C_2DCis/C1_Intro/C2DCisIntro_d.htm
siehe: English Version: Bézier Approximation
siehe: English Version: Cubic Spline Interpolation
siehe: English Version: Programming curves in parametric form
siehe: Wie Vektorformate Grafiken beschreiben
top of page: |