Einzelnen Beitrag anzeigen

Benutzerbild von Jakob Ullmann
Jakob Ullmann

Registriert seit: 17. Feb 2007
218 Beiträge
 
Lazarus
 
#1

Funktion graphisch aufzeichnen (Polarkoordinaten)

  Alt 21. Jun 2008, 22:42
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.
Jakob
  Mit Zitat antworten Zitat