Polardiagramm

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
wichard
Beiträge: 7
Registriert: Mi 11. Dez 2013, 18:12

Polardiagramm

Beitrag von wichard »

Ich möchte gerne meine Daten (z.B. Antennendiagramm) als Polardiagramm darstellen.
Da es für tChart tPolarseries gibt, war ich hoffnungsvoll.Ich habe dazu aber eine Menge ungelöster Probleme:
(1.) Wenn ich Daten polar darstelle, möchte ich auch ein polares Koordinatengitter haben, mit Winkel-Gitterlinien, die vom Ursprung radial nach außen laufen und Radius-Gitterlinien, die als Kreise um den Ursprung verlaufen.
(2.) Die vertikale(x=0) Achse und die horizontale (y=0)Achse sollten durch den Ursprung verlaufen.
3. Wenn (1.) und (2.) ohne Riesenaufwand machbar sind, bin ich schon ziemlich glücklich. Bei Polardiagrammen benutzt man aber häufig einen logarithmischen Maßstab. Das erfordert wohl, dass die x- und y-Achse jeweils aus zwei Achsen bestehen, da sonst die Achsen den Nullpunkt enthalten müssten und Log(0) gibt immer seltsame Ergebnisse.
Ein Beispiel für so ein Diagramm ist
http://commons.wikimedia.org/wiki/File: ... esonse.png
Hat jemand schonmal sowas gemacht?
Vielen dank!
Wichard

wp_xyz
Beiträge: 5216
Registriert: Fr 8. Apr 2011, 09:01

Re: Polardiagramm

Beitrag von wp_xyz »

Also "offiziell" geht das nicht. Allerdings sind die Haken und Ösen vorhanden, um sowas einbauen zu können. Ich habe mal ein ternäres Koordinatensystem mit TAChart gemacht (http://forum.lazarus.freepascal.org/ind ... l#msg93141), ähnlich müsste es auch hier funktionieren.

wp_xyz
Beiträge: 5216
Registriert: Fr 8. Apr 2011, 09:01

Re: Polardiagramm

Beitrag von wp_xyz »

Ich hab' jetzt etwas mit diesem Thema herumgespielt. Mit diesen beiden Prozeduren kannst du das runde Koordinatensystem eines Polardiagramms zeichnen:

Code: Alles auswählen

 
uses
  TAChartUtils, TAGeometry;
 
procedure PreparePolarAxes(AChart: TChart; AMax: Double);
var
  ex: TDoubleRect;
begin
  ex.a.x := -AMax;
  ex.a.y := -AMax;
  ex.b.x :=  AMax;
  ex.b.y :=  AMax;
  with AChart do begin
    Extent.FixTo(ex);
    Proportional := true;
    Frame.Visible := false;
    with LeftAxis do begin
      AxisPen.Visible := false;
      Grid.Visible := false;
      PositionUnits := cuGraph;
      Marks.Visible := false;
    end;
    with BottomAxis do begin
      AxisPen.Visible := false;
      Grid.Visible := false;
      PositionUnits := cuGraph;
      Marks.Visible := false;
    end;
  end;
end; 
 
procedure DrawPolarAxes(AChart: TChart; AMax, ADelta: Double);
var
  r, theta: Double;
  P1, P2: TPoint;
  i, h, w: Integer;
  s: String;
begin
  with AChart do begin
    // Hintergrund
    Drawer.SetBrushParams(bsSolid, Color);
    Drawer.FillRect(0, 0, Width, Height);
 
    // Gradlinien
    Drawer.SetBrushParams(bsClear, clNone);
    Drawer.SetPenParams(psDot, clGray);
    for i:=0 to 5 do begin
      theta := i * pi/6;
      P1 := GraphToImage(DoublePoint(AMax*sin(theta), AMax*cos(theta)));
      P2 := GraphToImage(DoublePoint(-AMax*sin(theta), -AMax*cos(theta)));
      Drawer.MoveTo(P1);
      Drawer.Lineto(P2);
    end;
 
    // Kreise
    r := ADelta;
    while r <= AMax do begin
      P1 := GraphToImage(DoublePoint(-r, -r));
      P2 := GraphToImage(DoublePoint(+r, +r));
      Drawer.SetPenParams(psDot, clGray);
      Drawer.SetBrushParams(bsClear, clNone);
      Drawer.Ellipse(P1.x, P1.y, P2.x, P2.y);
      r := r + ADelta;
    end;
 
    // Achsenbeschriftung
    Drawer.Font := BottomAxis.Marks.LabelFont;
    h := Drawer.TextExtent('0').y;
    r := 0;
    while r <= AMax do begin
      s := FloatToStr(r);
      w := Drawer.TextExtent(s).x;
      P1 := GraphToImage(DoublePoint(0, r));
      Drawer.TextOut.Pos(P1.X - w div 2, P1.y - h div 2).Text(s).Done;
      r := r + ADelta;
    end;
  end;
end; 
PreparePolarAxes rufst du auf, nachdem deine Daten geladen sind und du weißt, wie groß der maximale Radiuswert ist. Den brauchst du als Parameter AMax in dieser Prozedur. Damit werden die x- und y-Achsen auf gleichen Wertebereich eingestellt und insgesamt komplett ausgeblendet.

DrawPolarAxes wird im OnAfterDrawBackwall-Ereignis des Chart aufgerufen. Zu diesem Zeitpunkt sind die Daten noch nicht ausgegeben - es würde sich auch OnAfterPaint anbieten, aber damit würden die Achsenkreise über die Datenkurven gezeichnet und in der hier gezeigten Implementierung komplett übermalt, weil das Hintergrundrechteck mit eingefärbt wird. DrawPolarAxes erhält als Parameter wieder den maximalen Radius und den Abstand der Kreise. Die komplette Zeichenausgabe ist etwas ungewohnt ("Chart.Drawer"), weil TAChart eine Zwischenschicht für die Ausgabe eingeführt hat, so dass man verschiedene Ausgabe"geräte" (BGRABitmap, Vektorformate, Drucker) mit demselben Code ansprechen kann.

Mit den folgenden Daten und einem Standard-Chart mit zwei PolarSeries konnte ich das beigefügte Diagramm erzeugen:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  phi: Double;
begin
  for i:=0 to 360 do begin
    phi := i/180 * pi;
    Chart1PolarSeries1.AddXY(phi, sqr(sin(phi)));
    Chart1PolarSeries2.AddXY(phi, sqr(sin(phi)) - 1/4*sqr(cos(2*phi)));
  end;
  PreparePolarAxes(Chart1, 1.0);
end;  
 
procedure TForm1.Chart1AfterDrawBackWall(ASender: TChart; ACanvas: TCanvas;
  const ARect: TRect);
begin
  DrawPolarAxes(Chart1, 1.0, 0.2);
end;  
Dateianhänge
polar_chart.png

wp_xyz
Beiträge: 5216
Registriert: Fr 8. Apr 2011, 09:01

Re: Polardiagramm

Beitrag von wp_xyz »

Falls das hier noch interessant ist: TPolarSeries ist bisher abgestürzt, wenn man die Legende verwendet hat. Dies ist nun behoben und wird in Lazarus 1.2 enthalten sein.

Antworten