unit framegrafico;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, ExtCtrls, Astronomy, DateUtils, Graphics,
  StdCtrls, LResources;


    procedure calcolapianeti;
    function FormattaOraDaMinutiGiorno(minutigiorno:integer):string;

type

  { TPianetiGraph }

  TPianetiGraph = class(TFrame)
    Label1: TLabel;
    PaintBox1: TPaintBox;
    procedure FrameMouseLeave(Sender: TObject);
    procedure ImpostaTempoClick(Sender: TObject; Tempo:TDateTime; lat, long:double);
    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure PaintBox1Paint(Sender: TObject);

  private

  public
    var colorform:TColor;
  end;

  type
  TNomePianeta= (Sole, Luna, Mercurio, Venere, Marte, Giove, Saturno, Urano, Nettuno);
  TPianeta= record
   colore:Tcolor;
   sorge:integer; //minuti da inizio giornata
   tramonta:integer; //minuti da inizio giornata
   distanza:double; //distanza in unità astronomiche
   testo:string;
   isinsky: boolean; // variabile che dice se l'oggetto è nel cielo nel caso sia l'alba che il tramonto non siano ottenibili
   illumData:astro_illum_t;

   case nome: TNomePianeta of       //questo record genera i campi per il crepuscolo civile solo se il nome è Sole
   Sole: (civildawn, civildusk, nauticaldawn, nauticaldusk, astronomicaldawn, astronomicaldusk :integer);
   Luna, Mercurio, Venere, Marte, Giove, Saturno, Urano, Nettuno:  ( );
  end;

implementation

{$R *.lfm}

{ TPianetiGraph }

var
  //Array dei vari oggetti celesti $00090909
  oggetticelesti:array of TPianeta=
    (( colore: $003AE1FE; sorge: 0; tramonta: 0; distanza: 0; testo: 'Sole'; isinsky: False; illumData:(); nome: Sole; civildawn: 0; civildusk:0; nauticaldawn:0; nauticaldusk:0; astronomicaldawn:0; astronomicaldusk:0 ),
    ( colore: $00EADBCD; sorge:0; tramonta: 0; distanza: 0;  testo: 'Luna'; isinsky: False;illumData:(); nome: Luna;),
    ( colore: $00A1D11D; sorge:0; tramonta: 0; distanza: 0;  testo: 'Mercurio'; isinsky: False;illumData:(); nome: Mercurio;),
    ( colore: $00D3D200; sorge:0; tramonta: 0; distanza: 0; testo: 'Venere'; isinsky: False;illumData:(); nome: Venere; ),
    ( colore: $005352EE; sorge: 0; tramonta: 0; distanza: 0; testo: 'Marte'; isinsky: False;illumData:(); nome: Marte;),
    ( colore: $00628EFF; sorge: 0; tramonta: 0; distanza: 0; testo: 'Giove'; isinsky: False;illumData:(); nome: Giove; ),
    ( colore: $00439FFF; sorge: 0; tramonta: 0; distanza: 0;  testo: 'Saturno'; isinsky: False;illumData:(); nome: Saturno;),
    ( colore: $00FFA054; sorge: 0; tramonta: 0; distanza: 0; testo: 'Urano'; isinsky: False;illumData:(); nome: Urano;),
    ( colore: $00CD275F; sorge: 0; tramonta: 0; distanza: 0;  testo: 'Nettuno'; isinsky: False;illumData:(); nome: Nettuno;)
    );

oggetto:string;
durataora:double;
durataminuto:double;
duratamezzora:double;
durataorai:integer; //la durata ora per la variabile i
orafine:integer;
altezzarighe:double;


//variabili di posizione
latitudine:double;
longitudine:double;
altitudine:integer=500;

tempoinizio:TDateTime;//tempo di inizio della giornata coincide con le 0.0 della giornata corrente oppure quello impostato dall'utente
isFirstTime:boolean=True;

const

  xtesto=10;
  xgrafico=50;     // cordinata x di inizio del grafico
  distrighetop=30;
  spesspianeti=13; //spessore righe sorgere e tramonto PianetiGraph
  aggpospianeti=2; //aggiusta la posizione del rettangolo PianetiGraph rispetto al testo
  ridpaint=60;     //indica quanto è più piccolo in pixel il grafico (dal righello in poi) rispetto alla larghezza della paintbox

  distrighellotop=25;
  altezzarighello=6;
  mezzorighello=5;
  roundrett=5; //arrottondamento angoli rettangoli

procedure TPianetiGraph.PaintBox1Paint(Sender: TObject);


  var
  i:integer=1;
  corpo:TPianeta;
  orafine2:integer;
begin




    if isFirstTime then
    begin
     tempoinizio:=EncodeDate(YearOf(Now), MonthOf(Now), DayOf(Now));
     calcolapianeti;
     isFirstTime:=False;
    end;


   orafine:=PaintBox1.Width-ridpaint+xgrafico; //calcola gli ultimi pixel da rappresentare nel righello
   orafine2:=PaintBox1.Width-ridpaint;
   durataora:=(PaintBox1.Width-ridpaint)/24;   //calcola il numero di pixel che sono rappresentati da un ora
   durataminuto:=durataora/60;                 //calcola il numero di pixel che rappresentano un minuto
   duratamezzora:=(PaintBox1.Width-ridpaint)/48;
   altezzarighe:=(PaintBox1.height/11);        //calcola la distanza che vi è tra le diverse righe che rappresentano il sorg e il tramont dei PianetiGraph

   PaintBox1.Canvas.Brush.Color:=colorform; //decide il colore di sfondo del canvas
   PaintBox1.Canvas.Font.Color:=clWhite;    //mette il font bianco di default perchè cambia colore dal tema chiaro a scuro del sistema

   for corpo in oggetticelesti do //scrive i nomi degli oggetti celesti e rettangoli
  begin
    PaintBox1.Canvas.Pen.Color:=clWhite;
    PaintBox1.Canvas.Font.Size:=11;
   PaintBox1.Canvas.TextOut(xtesto,round(altezzarighe*i+distRighetop), copy(corpo.testo,0,3));


  if corpo.nome= Sole then
   begin   //i crepuscoli vanno messi in ordine dall'astronomio al civile per non fare in modo che si sovrascrivano a vicenda
       //crepuscolo astronomico
   PaintBox1.Canvas.Brush.Color:=$00300A00;
   PaintBox1.Canvas.Pen.Color:=clWhite;
   PaintBox1.Canvas.RoundRect(round(xgrafico+orafine2*corpo.astronomicaldusk/1440),round(altezzarighe*i+aggpospianeti+distrighetop),round(xgrafico+orafine2*corpo.astronomicaldawn/1440),round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);


     //crepuscolo nautico
   PaintBox1.Canvas.Brush.Color:=$009E2200;
   PaintBox1.Canvas.Pen.Color:=clWhite;
   PaintBox1.Canvas.RoundRect(round(xgrafico+orafine2*corpo.nauticaldusk/1440),round(altezzarighe*i+aggpospianeti+distrighetop),round(xgrafico+orafine2*corpo.nauticaldawn/1440),round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);


    //crepuscolo civile
   PaintBox1.Canvas.Brush.Color:=$00FF3700;
   PaintBox1.Canvas.Pen.Color:=clWhite;
   PaintBox1.Canvas.RoundRect(round(xgrafico+orafine2*corpo.civildusk/1440),round(altezzarighe*i+aggpospianeti+distrighetop),round(xgrafico+orafine2*corpo.civildawn/1440),round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);
  end;

   PaintBox1.Canvas.Brush.Color:=corpo.colore;
   PaintBox1.Canvas.Pen.Color:=clWhite;
  if (corpo.sorge=-1) and (corpo.tramonta>=0) then
     begin
     PaintBox1.Canvas.RoundRect(xgrafico,round(altezzarighe*i+aggpospianeti+distrighetop),round(xgrafico+(orafine2*corpo.tramonta/1440)),round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);
     end else
     begin
         if (corpo.sorge>=0) and (corpo.tramonta=-1) then
         begin
          PaintBox1.Canvas.RoundRect(round(xgrafico+orafine2*corpo.sorge/1440),round(altezzarighe*i+aggpospianeti+distrighetop),orafine,round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);
         end else

             begin

                  if (corpo.sorge=-1) and (corpo.tramonta=-1) then
                 begin
                   if corpo.isinsky=True then
                    PaintBox1.Canvas.RoundRect(xgrafico,round(altezzarighe*i+aggpospianeti+distrighetop),orafine,round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);
                 end else

                 begin

                        if corpo.sorge<corpo.tramonta then
                          PaintBox1.Canvas.RoundRect(round(xgrafico+orafine2*corpo.sorge/1440),round(altezzarighe*i+aggpospianeti+distrighetop),round(xgrafico+orafine2*corpo.tramonta/1440),round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett)
                        else
                        begin
                          PaintBox1.Canvas.RoundRect(xgrafico,round(altezzarighe*i+aggpospianeti+distrighetop),round(xgrafico+(orafine2*corpo.tramonta/1440)),round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);
                          PaintBox1.Canvas.RoundRect(round(xgrafico+orafine2*corpo.sorge/1440),round(altezzarighe*i+aggpospianeti+distrighetop),orafine,round(altezzarighe*i+aggpospianeti+spesspianeti+distrighetop),roundrett,roundrett);
                        end;

                 end;

             end;

        end;



   PaintBox1.Canvas.Brush.Color:=colorform; //decide il colore di sfondo del canvas
    i:=i+1;

  end;

  for i:=0 to 24 do  //crea le tacche delle ore
  begin
    durataorai:=round( durataora*i);
    PaintBox1.Canvas.Pen.Color:=clwhite;
    PaintBox1.Canvas.Line(durataorai+xgrafico,distrighellotop,durataorai+xgrafico,distrighellotop+altezzarighello);

    PaintBox1.Canvas.Pen.Color:=clNone;
    PaintBox1.Canvas.TextOut(durataorai+xgrafico-6,5, inttostr(i));
  end;

  for i:=0 to 23 do     //crea le tacche delle mezzore
  begin
    durataorai:=round( durataora*i+duratamezzora);
    PaintBox1.Canvas.Pen.Color:=clwhite;
    PaintBox1.Canvas.Line(durataorai+xgrafico,distrighellotop+round(mezzorighello/2),durataorai+xgrafico,distrighellotop+round(mezzorighello));


  end;

    PaintBox1.Canvas.Line(xgrafico,distrighellotop+mezzorighello,orafine, distrighellotop+mezzorighello);//disegna la riga sulle tacche


end;

procedure TPianetiGraph.ImpostaTempoClick(Sender: TObject; Tempo:TDateTime; lat, long:double);
begin

    tempoinizio:=Tempo;
    latitudine:=lat;
    longitudine:=long;
    calcolapianeti;
    PaintBox1.Refresh;
end;

procedure TPianetiGraph.FrameMouseLeave(Sender: TObject);
begin
  Label1.Caption:='';
end;


var
stop:boolean;

procedure TPianetiGraph.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
elementi:TPianeta;
valoreriga: integer;
magnitudine:string;
fasepianetiinterni:string;
saturnoring:string;
orasorge:string;
oratramonta:string;
crepuscoli:string;
i:integer;
begin
  i:=0;
  for elementi in oggetticelesti do
  begin
   i:=i+1;
   valoreriga:=round(altezzarighe*i+aggpospianeti+distrighetop);
   if (Y>valoreriga) and (Y<valoreriga+spesspianeti) then
    begin
      if elementi.nome=Sole then
       begin
        crepuscoli:='     Crepuscoli:';
        crepuscoli:=crepuscoli+'     Astronomico '+ FormattaOraDaMinutiGiorno(elementi.astronomicaldusk)+ '-'+ FormattaOraDaMinutiGiorno(elementi.astronomicaldawn);
        crepuscoli:=crepuscoli+'     Nautico '+ FormattaOraDaMinutiGiorno(elementi.nauticaldusk)+ '-'+ FormattaOraDaMinutiGiorno(elementi.nauticaldawn);
        crepuscoli:=crepuscoli+'     Civile '+ FormattaOraDaMinutiGiorno(elementi.civildusk)+ '-'+ FormattaOraDaMinutiGiorno(elementi.civildawn);
       end;



      magnitudine:='     Magnitudine: '+ SysUtils.FormatFloat('0.00', elementi.illumData.mag);
      if (elementi.nome=Mercurio) or (elementi.nome=Venere) then fasepianetiinterni:='     Fase: '+FormatFloat('0.00', elementi.illumData.phase_fraction) ;
      if (elementi.nome=Saturno) then  saturnoring:='     Inclinazione Anelli: '+FormatFloat('0.00', elementi.illumData.ring_tilt);
      if elementi.sorge=-1 then orasorge:='--'
      else orasorge:=FormattaOraDaMinutiGiorno(elementi.sorge);

      if elementi.tramonta=-1 then oratramonta:='--'
      else oratramonta:=FormattaOraDaMinutiGiorno(elementi.tramonta) ;

      Label1.Caption:=elementi.testo+magnitudine+fasepianetiinterni+saturnoring+Crepuscoli+'     Sorge alle: '+orasorge + '     Tramonta alle: ' + oratramonta+'     Distanza '+SysUtils.FormatFloat('0.000',elementi.distanza)+'UA'; ;

      break;
    end


   end;
end;

function FormattaOraDaMinutiGiorno(minutigiorno:integer):string;
begin
 result:= SysUtils.FormatFloat('00',(minutigiorno div 60))+':'+ sysUtils.FormatCurr('00', (minutigiorno mod 60) );

end;

procedure calcolapianeti;
  var
i:integer;
   alba:TDateTime;
   tramonto:TDateTime;
   crepalba:TDateTime;//crepuscolo civile alba
   creptram:TDateTime;//crepuscolo tramonto alba

minutidainizio:integer; //indica il numero di minuti da inizio giorno
   numerocorpo:integer; //serve per corrispondenza tra gli enum in TPianeta e quelli contenuti in astro_body_t

   ognicrepuscolo:TCrepuscolo;
   datecrep:TDateTime;
   segnaposto:integer;

   albaerror:boolean=False;     //variabili utilizzate per segnare in caso di errore;
   tramontoerror:boolean=False;
   altezzaoggetto:double=0;
begin


for i:=0 to 8 do  //serve per corrispondenza tra gli enum in TPianeta e quelli contenuti in astro_body_t
    begin

    albaerror:=False;
    tramontoerror:=False;

    case oggetticelesti[i].nome of
               Mercurio: numerocorpo:=0;
               Venere: numerocorpo:=1;
               Marte: numerocorpo:=3;
               Giove: numerocorpo:=4;
               Saturno: numerocorpo:=5;
               Urano: numerocorpo:=6;
               Nettuno: numerocorpo:=7;
               Sole: numerocorpo:=9;
               Luna: numerocorpo:=10;
          end;


      oggetticelesti[i].distanza:=astronomy.DistanzaOggetto(numerocorpo, latitudine, longitudine, altitudine, DateUtils.IncHour(tempoinizio, 12));
      oggetticelesti[i].illumData:=astronomy.Illuminazione(numerocorpo, DateUtils.IncHour(tempoinizio, 12));

      if oggetticelesti[i].nome=Sole then
       begin
             //il sole presenta anche i crepuscoli a differenza degli altri oggetti

          for ognicrepuscolo in Tcrepuscolo do
          begin


                datecrep:=Astronomy.Crepuscolo(latitudine, longitudine, altitudine, 1, tempoinizio, ognicrepuscolo);
                segnaposto:=DateUtils.MinutesBetween(tempoinizio, datecrep);

                case ognicrepuscolo of
                           civildawn:oggetticelesti[i].civildawn:=segnaposto;
                           civildusk:oggetticelesti[i].civildusk:=segnaposto;
                           nauticaldawn:oggetticelesti[i].nauticaldawn:=segnaposto;
                           nauticaldusk:oggetticelesti[i].nauticaldusk:=segnaposto;
                           astronomicaldawn:oggetticelesti[i].astronomicaldawn:=segnaposto;
                           astronomicaldusk:oggetticelesti[i].astronomicaldusk:=segnaposto;
                end;

           end;


      end;
       try
         alba:=Astronomy.TrovaSorgereOggetto(numerocorpo, latitudine, longitudine, altitudine, tempoinizio, 1);
       except
       on E:exception do
       begin
       albaerror:=True;
       oggetticelesti[i].sorge:=-1;
       end;

       end;


        try
          tramonto:=Astronomy.TrovaTramontoOggetto(numerocorpo, latitudine, longitudine, altitudine, tempoinizio, 1);
        except
        on E:exception do
          begin

          tramontoerror:=True;
          oggetticelesti[i].tramonta:=-1;
          end;
        end;


    if albaerror=False then oggetticelesti[i].sorge:=DateUtils.MinutesBetween(tempoinizio, alba);

    if tramontoerror=False then oggetticelesti[i].tramonta:=DateUtils.MinutesBetween(tempoinizio, tramonto);

    if albaerror and tramontoerror then
     begin

     end;
         altezzaoggetto:= astronomy.AltitudineOggetto(numerocorpo, latitudine, longitudine, altitudine, DateUtils.IncHour(tempoinizio, 12));
         if altezzaoggetto>0 then
          oggetticelesti[i].isinsky:=True else oggetticelesti[i].isinsky:=False;

    end;

end;

end.

