Analog zu meinem Projekt JTM habe ich mich gefragt, wie ich in ein Polarkoordinatensystem zeichnen könnte. Die Lösung ist nicht so einfach wie bei einem kartesischen Koordinatensystem. Mein Lösungsansatz wäre der folgende:
Delphi-Quellcode:
var
...
stepOne: Integer;
oX, oY: Integer;
wh: Integer; // wh = WidthHeight because it's the same
// But we only want the smaller one, so we
// save it in a variable that we don't always
// have to ask for it.
Damit haben wir unsere Variablen. oX und oY sind jeweils origin-x und origin-y, also die Mitte in diesem Fall (genau gesagt der Koordinatenursprung). Mit StepOne wird der Abstand zwischen zwei Ticks festgelegt, wobei der numerische Abstand zwischen zwei Ticks immer 1 ist. Nun zeichnen wir erstmal das leere Koordinatensystem ohne Inhalt:
Delphi-Quellcode:
procedure TForm1.DrawPlot;
var
x, y, i: Integer;
_x, _y: Single;
can: TMetafileCanvas;
begin
try
if Image1.Width > Image1.Height then
wh := Image1.Height
else wh := Image1.Width;
Image1.Picture.Metafile.Width := wh;
Image1.Picture.Metafile.Height := wh;
can := TMetafileCanvas.Create(Image1.Picture.Metafile, 0);
oX := wh div 2; // find the origin (x)
oY := wh div 2; // find the origin (y)
can.Pen.Color := clSilver;
can.Pen.Style := psClear;
can.Brush.Color := clWhite;
can.Rectangle(0, 0, wh, wh);
if Ring1.Checked then // draw the gray circle around the graph, as you
// can se it in most of the graphs you find.
begin
can.Pen.Style := psSolid;
can.Ellipse(0, 0, wh, wh);
end;
can.Pen.Style := psSolid;
can.Font.Name := 'Arial';
can.Font.Size := 10;
for x := -(wh div stepOne div 2) to (wh div stepOne div 2) do // Draw ticks
begin
can.Pen.Color := clBlack;
can.MoveTo(oX + x * stepOne, oX - 2); // ticks on the x-axis
can.LineTo(oX + x * stepOne, oX + 3);
can.TextOut(oX + x * stepOne + 5, oX + 5, // draw the labels
IntToStr(x));
can.MoveTo(oY - 2, oY + x * stepOne); // ticks on the y-axis
can.LineTo(oY + 3, oY + x * stepOne);
can.TextOut(oY + 5, oY + x * stepOne + 5, // draw the labels
IntToStr(x));
end;
can.MoveTo(0, oX); // The lines of the y- and x-axis
can.LineTo(wh, oX);
can.MoveTo(oY, 0); // the other line
can.LineTo(oY, wh);
Bis dahin ist alles fröhliches Zeichnen ohne große Rechnerei. Ring1 ist bei mir ein Menu-Item, aber es kann genauso eine Checkbox oder ein Radiobutton (wobei das nicht sinnvoll wäre) sein. Wie der Name schon sagt, wird angegeben, ob man einen grauen Ring außen herum haben möchte.
can ist als MetafileCanvas deklariert. Warum? Weil eine Vektorgraphik praktisch ist, wenn man Drucken möchte oder die Graphik verlustfrei ins Word kopieren möchte. Und da das für uns keine großen Umstände sind, lohnt es sich, von Anfang an mit Vektorgraphiken zu arbeiten. Nun kommt der Teil, der die Funktionen aufzeichnen:
Delphi-Quellcode:
// No go through all items and draw them
for i := 0 to ListBox1.Items.Count - 1 do
begin
Parser1.Expression := ListBox1.Items[i];
Parser1.X := 0;
try
can.Pen.Color := StrToInt('$00'+Copy(ColorDialog1.CustomColors[i], 8, 6));
except
can.Pen.Color := clBlack;
end;
can.MoveTo(oX + round(cos(_x) * Parser1.Value * stepOne),
oY - round(sin(_x) * Parser1.Value * stepOne));
for x := 1 to round(2 * Pi * 100) do
begin
_x := x / 100;
Parser1.X := _x;
can.LineTo(oX + round(cos(_x) * Parser1.Value * stepOne),
oY - round(sin(_x) * Parser1.Value * stepOne));
can.MoveTo(oX + round(cos(_x) * Parser1.Value * stepOne),
oY - round(sin(_x) * Parser1.Value * stepOne));
end;
end;
finally
can.Free;
end;
end;
Genau genommen ist es der zweite Teil der Prozedur DrawPlot, also mit Freigabe von can. Parser1 ist vom Typ TParser aus der
Unit Parser10. Einfach mal bei torry.net nach "parser" suchen, dann wird man schnell fündig. Wenn man einen anderen Parser benutzt, einfach die entsprechenden Aufrufe ersetzen. Was die Eigenschaften bedeuten, sollte sich eigentlich von selbst erklären.