Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Orientierungspunkte für die Y-Achse eines Diagramms berechnen (https://www.delphipraxis.net/192710-orientierungspunkte-fuer-die-y-achse-eines-diagramms-berechnen.html)

Harry Stahl 14. Mai 2017 18:38

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Die von Samso vorgeschlagene Lösung sieht schon sehr gut aus. Habe das mal in den Source gepackt, der sieht nun so aus:
Delphi-Quellcode:
procedure TForm82.Button1Click(Sender: TObject);
var
  L: Integer;
  Val, MaxVal: Extended;

  NPotenz: Integer;
  YDiv, YNorm: Double;
  YResult: Double;

begin
  with chart1 do begin
    series1.clear;
    title.Text.Text := 'Monatliche Umsätze im Jahr 2017';

    Maxval := 0;

    sgUmsatz.cells[0, 0] := 'Monat';
    sgUmsatz.cells[1,0] := 'Betrag';
    sgUmsatz.cells[2,0] := 'Wert Y-Achse';

    for L := 1 to 12 do begin
      sgUmsatz.cells[0, L] := FormatDateTime ('mmm', StrToDate ('01.01.2017') + ((L-1) * 33));
      Val := Random (TButton(sender).tag);
      MaxVal := max (MaxVal, val);
      sgUmsatz.cells[1, L] := Val.ToString;
      series1.Add (StrToFloat (sgUmsatz.cells[1, L]), sgUmsatz.cells[0,L], clGreen);
    end;
  end;

  NPotenz := trunc(Log10(MaxVal));
  YDiv:= IntPower(10, NPotenz);
  YNorm := trunc(MaxVal / YDiv);

  if YNorm<=3
    then
      YResult := YNorm
    else
    if YNorm<=5
    then
      YResult := 5
    else
      YResult := 10;

  // Soll hier nun die selbst errechneten Werte für die Y-Achse ausgeben
  for L := 1 to 10 do begin
    sgUmsatz.cells[2,L] := (L * YDiv).tostring;
  end;

  // Nicht benötigt, oder?
  YResult := YResult * YDiv;
end;
Eigentlich brauche ich doch nur den YDiv-Wert, oder?

Siehe neuer Screenshot und angepasstes Demo im Anhang.

samso 15. Mai 2017 06:51

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Da liegt ein Missverständnis vor. YDiv hat nichts mit der Einteilung der Y-Achse zu tun. Eventuell komme ich heute Abend dazu, mir Dein Demoprogramm mal vorzunehmen.

samso 15. Mai 2017 07:26

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Ist Dein Ziel eine Nachbildung von TChart? Der Maximalwert der Y-Achse ist dort daran orientiert, dass oberhalb der Säule mit dem maximalen Umsatz noch genug Platz sein muss um das zugehörige Monats-Label anzubringen. Da muss dann natürlich die Schriftgröße des Labels, die Größe der Zeichenfläche usw. in der Berechnung berücksichtigt werden.

Harry Stahl 15. Mai 2017 16:31

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Zitat:

Zitat von samso (Beitrag 1371411)
Ist Dein Ziel eine Nachbildung von TChart? Der Maximalwert der Y-Achse ist dort daran orientiert, dass oberhalb der Säule mit dem maximalen Umsatz noch genug Platz sein muss um das zugehörige Monats-Label anzubringen. Da muss dann natürlich die Schriftgröße des Labels, die Größe der Zeichenfläche usw. in der Berechnung berücksichtigt werden.

Habe das TChart mal dabei gefügt, damit man eine Orientierung hat, was sinnvolle Zahlen sein könnten. Muss also nicht exakt mit TChart übereinstimmen.

Michael II 15. Mai 2017 18:42

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Der Funktion yIntervallLOG() unten kannst du den minimalen Y Wert und den maximalen Y Werte deiner darzustellenden Zahlenpaare übergeben. (miny, maxy : Extended) - (Negative Werte sind auch OK.)

Du kannst wählen, in wie viele Teile (anzint_min bis anzint_max) die Y Achse ungefähr eingeteilt werden soll. (anzint_min, anzint_max : integer)

Du kannst der Funktion übergeben, welche Zahlentypen du erlauben willst. (Parameter erlaubt: TExtendedArray)

Falls aufgrund der übergebenen Parameter kein Resultat gefunden wird:
Über andereschrittweitenerlaubt:boolean steuerst du, ob zusätzlich getestet werden soll, ob Schrittweiten vom Typ f*10^p, f=1,2,3,4,5,6,7,8,9 möglich sind.

Falls die Funktion keine mögliche Unterteilung findet, dann wird geprüft, ob's mit anzint_min := anzint_min-1 klappt.



Delphi-Quellcode:

uses math;


type TExtendedArray = array of extended;


function yIntervallLOG( miny, maxy : extended; anzint_min, anzint_max : integer;
     erlaubt: TExtendedArray; andereschrittweitenerlaubt : boolean = false ) : TExtendedArray;

var fak_min, fak_max,
    delta, int_min, int_max : extended;
    potmin, potmax, len, i : integer;
    potenz10 : integer;


label nocheinmal;

    procedure check( pot: integer; f : extended );
    // prüfe, ob f*10^pot im Intervall I liegt und damit eine mögliche schrittweite ist
    var wert : extended;
    begin
      wert := f*power(10,pot);
      if ( int_min <= wert ) and ( wert <= int_max ) then
      begin
          SetLength( Result, length(Result)+1 );
          Result[length(Result)-1] := wert;
      end;
    end;

    procedure pot_fak( z : extended; var pot : integer; var f : extended );
    // Berechne die Zerlegung von z: z = f*10^pot
    // IN z - OUT f, pot
    begin
      if ( abs(z) >= 1 ) then
      begin
        pot := trunc(log10( abs(z) ));
        f := z/power(10,pot);
      end
      else
      begin
        pot := trunc(log10( abs(z) ))-1;
        f := z/power(10,pot);
      end;
    end;

begin
  delta := maxy - miny;
  assert( delta > 0 , 'Maximum ' + floattostr(maxy) + ' nicht grösser als Minimum ' + floattostr(miny) );

  nocheinmal:
  // Schrittweite liegt aufgrund der Parameter zwischen int_min und int_max:
  // I:= Intervall [int_min..int_max]
  int_min := delta/anzint_max;
  int_max := delta/anzint_min;

  // Zerlegungen in_min = fak_min*10^potmin, in_max = fak_max*10^potmax bestimmen:
  pot_fak( int_min, potmin, fak_min );
  pot_fak( int_max, potmax, fak_max );

  len := length(erlaubt);

  // Prüfen, ob die erlaubten Vorgaben im Intervall [int_min..int_max] liegen
  for i := 0 to len-1 do
  for potenz10 := potmin to potmax do
    check( potenz10, erlaubt[i] );

  // keine Werte gefunden?
  if ( length(Result) = 0 ) then
  begin
    // falls andere schrittweiten erlaubt:
    // Prüfe, ob n*10^potenz10, für n=1,2,3,4,5,6,7,8,9 möglich ist
    // Prüfe also zum Beispiel auf 300er, 70er, 0.4, 0.07 etc. Schritte...
    if andereschrittweitenerlaubt then
    begin
      for I := 1 to 9 do
      for potenz10 := potmin to potmax do
       check( potenz10, i );
    end;

    // Wenn immer noch keine Resultate gefunden sind, dann erlauben wir ein tieferes anzint_min:
    if ( length(Result) = 0 ) then
    if anzint_min > 1 then
    begin
      dec( anzint_min );
      goto nocheinmal;
    end;
  end;
end;

So rufst du die Funktion auf:

Delphi-Quellcode:
procedure TForm92.Button2Click(Sender: TObject);
var erlaubt, res : TExtendedArray;
    min, max : extended;
    hs : string;
    i : integer;
begin
   setlength( erlaubt, 5 );
   erlaubt[0] := 1;
   erlaubt[1] := 5;
   erlaubt[2] := 2;
   erlaubt[3] := 2.5;
   erlaubt[4] := 7.5;

   min := 0;
   max := 543;
   res := yIntervallLOG( min, max, 8, 16, erlaubt );

   hs := 'min=' + min.ToString + #13#10 +
         'max=' + max.ToString + #13#10;
   hs := hs + 'Mögliche Intervalle:' + #13#10;
   for i := 0 to length(res)-1 do hs := hs + floattostr(res[i]) + #13#10;

   Showmessage( hs);
end;
Beispiel (Code oben):
Du willst y Werte zwischen 0 und 543 darstellen, die y-Achse soll in 8-16 Teile geteilt werden, erlaubt sind Schrittweiten vom Typ 1*10^p, 5*10^p, 2*10^p, 2.5*10^p und 7.5*10^p.

Resultat: 50 (ist vom erlaubten Typ 5*10^5)

Die Funktion gibt jeweils alle möglichen Schrittweiten (Unterteilungen) aus. Zuerst werden Zahlen vom Typ erlaubt[0] ausgegeben, dann vom Typ erlaubt[1], usw..

Beispiel 2:
Wenn du im Beispiel statt 8-16 Teile nun 4-16 Teile erlaubst, dann gibt dir die Funktion die drei möglichen Unterteilungen 100, 50, 75 zurück. (100 zuerst, weil erlaubt[0]=1, dann 50, weil erlaubt[1]=5 und schliesslich noch 75, weil du mit erlaubt[4]=7.5 auch nach Unterteilungen vom Typ 7.5*10^p suchen lässt.)


Beispiel 3:
res := yIntervallLOG( min, max, 5, 16, NIL, true );
sucht nach Unterteilungen der y-Achse vom Typ n*10^p n=1..9.

Harry Stahl 15. Mai 2017 23:05

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
@Michael II

Super, klappt hervorragend!:thumb:

Michael II 16. Mai 2017 07:55

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Hallo Harry

Bin froh :-D - und wenn du einen Bug finden solltest, dann bitte PN - Dankeschön.

Harry Stahl 16. Mai 2017 21:51

AW: Orientierungspunkte für die Y-Achse eines Diagramms berechnen
 
Zitat:

Zitat von Michael II (Beitrag 1371556)
Hallo Harry

Bin froh :-D - und wenn du einen Bug finden solltest, dann bitte PN - Dankeschön.

OK, würde ich dann machen (sieht aber nicht so aus, als ob das mal eintreffen sollte).


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:55 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz