unit uscan_windtydy;

{$H+}

interface

uses
  Windows,
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  lclintf, StdCtrls, ExtCtrls, ComCtrls, xmatdefs, uparqueseolicos, ufechas,
  uauxiliares,
  ugetwindows;

const
  Artigas_TOP_BR_Lat = -30.085869;
  SALTO_Long = -57.9797;
  MVD_Lat = -34.893;
  Rocha_BR_long = -53.4012;


{xDEFINE ADME}

  Archi_Windyty_Fotos_lst= 'c:\basura\windyty_fotos.txt';


{$IFDEF ADME}
  y_vel00 = 622;
  y_vel29 = 830;
  x_vels = 1576;
  Artigas_TOP_BR_Y = 197;
  SALTO_X = 585;
  MVD_Y = 716;
  Rocha_BR_X = 1013;  // abajo del Chuy
  x_Marco_Left = 535;
  x_Marco_Right = 1036;
  y_Marco_Top = 189;
  y_Marco_Bottom = 727;
{$ELSE}
  x_Marco_Left = 582;
  x_Marco_Right = 1333;
  y_Marco_Top = 145;
  y_Marco_Bottom = 957;
  y_vel00 = 686;
  y_vel29 = 1002;
  x_vels = 1896;
  Artigas_TOP_BR_Y = 170;
  SALTO_X = 640;
  MVD_Y = 953;
  Rocha_BR_X = 1279; // abajo del Chuy
{$ENDIF}

type

  { TForm1 }

  TForm1 = class(TForm)
    btColorear: TButton;
    btGetWindtydy: TButton;
    btPlotParques: TButton;
    btDifImages: TButton;
    btGetFotos: TButton;
    cbNewImages: TCheckBox;
    cbWait: TCheckBox;
    Edit1: TEdit;
    Image1: TImage;
    Image2: TImage;
    PageControl1: TPageControl;
    ScrollBox1: TScrollBox;
    ScrollBox2: TScrollBox;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    procedure btColorearClick(Sender: TObject);
    procedure btGetFotosClick(Sender: TObject);
    procedure btGetWindtydyClick(Sender: TObject);
    procedure btPlotParquesClick(Sender: TObject);
    procedure btDifImagesClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
  private
    { private declarations }
  public
    { public declarations }
    radio: integer;

    flg_SinDiferenciar: boolean;

    procedure copiarPantalla_old(bm: TBitMap);
    procedure GetCopy(Image: TImage);
    procedure SaveToArchi(Image: TImage; archi: string);
    procedure LoadFromArchi(Image: TImage; archi: string);

    procedure ScanValsColors;

    // Explora un rectangulo orientado en la direccion ang
    // centrado en x, y de ancho W y largo H
    // El resultado es el número de lineas (del total W)
    // en la dirección ANG que  resultaron con algún punto en las trazas BLANCAS.
    function scan_LCB(x, y, W, H: integer; ang: NReal): integer;

    // Explora un rectangulo orientado en la direccion ang
    // centrado en x, y de ancho W y largo H
    // El resultado es la máxima cantidad de blancos consecutivos
    function scan_MCB(x, y, W, H: integer; ang: NReal): integer;

    // Promedia las velocidades del rectángulo
    function Calc_Velocidad(x, y, R: integer; ang: NReal): NReal;

    // Busca la dirección de mínimo valor de LCB
    function search_minLCB_dir(x, y, W, H: integer): NReal;

    // Busca la dirección con máximo valor de MCB
    function search_maxMCB_dir(x, y, W, H: integer): NReal;

    procedure PlotFlecha(Image: TImage; x, y: integer; ang: NReal; radio: integer);

  end;


  // Rota un (x,y) dado en pixeles ang grados en sentido anti - horario.
  // supone x de izquierda a derecha en la pantalla e y de Arriba a Abajo.
  TRotDesp = class
    x0, y0: integer;
    cw, sw: NReal;
    ang_gr: NReal;
    constructor Create(x0, y0: integer; ang_gr: NReal);
    procedure SetAng(ang_gr: NReal);
    procedure SetX0Y0(x0, y0: integer);
    procedure eval(var xp, yp: integer; dx, dy: integer);
  end;

var
  Form1: TForm1;

  vals_: TDAOfNReal;
  cols_rgb: array of longint;
  parques_eolicos: TList;
  flg_ImagenInicializada: boolean;
  umbral_blanco: integer;




implementation

{$R *.lfm}

{ TForm1 }




// Capture the entire screen
// Screenshot des gesamten Bildschirms
procedure ScreenShot(Bild: TBitMap);
var
  c: TCanvas;
  r: TRect;
begin
  c := TCanvas.Create;
  c.Handle := GetWindowDC(GetDesktopWindow);
  try
    r := Rect(0, 0, Screen.Width, Screen.Height);
    Bild.Width := Screen.Width;
    Bild.Height := Screen.Height;
    Bild.Canvas.CopyRect(r, c, r);
  finally
    ReleaseDC(0, c.Handle);
    c.Free;
  end;
end;

procedure ScreenShotActiveWindow(Bild: TBitMap);
var
  c: TCanvas;
  r, t: TRect;
  h: THandle;
begin
  c := TCanvas.Create;
  c.Handle := GetWindowDC(GetDesktopWindow);
  h := GetForeGroundWindow;
  if h <> 0 then
    GetWindowRect(h, t);
  try
    r := Rect(0, 0, t.Right - t.Left, t.Bottom - t.Top);
    Bild.Width := t.Right - t.Left;
    Bild.Height := t.Bottom - t.Top;
    Bild.Canvas.CopyRect(r, c, t);
  finally
    ReleaseDC(0, c.Handle);
    c.Free;
  end;
end;

constructor TRotDesp.Create(x0, y0: integer; ang_gr: NReal);
begin
  inherited Create;
  SetX0Y0(x0, y0);
  SetAng(ang_gr);
end;

procedure TRotDesp.SetAng(ang_gr: NReal);
begin
  self.ang_gr := ang_gr;
  cw := cos(2 * pi * ang_gr / 360);
  sw := sin(2 * pi * ang_gr / 360);
end;

procedure TRotDesp.SetX0Y0(x0, y0: integer);
begin
  self.x0 := x0;
  self.y0 := y0;
end;

procedure TRotDesp.eval(var xp, yp: integer; dx, dy: integer);
begin
  xp := round(cw * dx + sw * dy + x0);
  yp := round(-sw * dx + cw * dy + y0);
end;

procedure LatLongToXY(var X, Y: integer; lat, long: NReal);
const
  my_lat = (MVD_Y - Artigas_TOP_BR_Y) / (MVD_Lat - Artigas_TOP_BR_Lat);
  mx_long = (Rocha_BR_X - SALTO_X) / (Rocha_BR_long - SALTO_Long);
begin
  Y := trunc((lat - Artigas_TOP_BR_Lat) * my_lat + Artigas_TOP_BR_Y + 0.5);
  X := trunc((long - SALTO_Long) * mx_long + SALTO_X + 0.5);
end;


function Intensidad(c: TColor): integer;
var
  rgb: longint;
begin
  rgb := ColorToRGB(c);
  Result := red(rgb) + green(rgb) + blue(rgb);
end;

function DistRGB(rgb1, rgb2: longint): NReal;
var
  dr, dg, db: longint;
begin
  dr := abs(Red(rgb1) - Red(rgb2));
  dg := abs(Green(rgb1) - Green(rgb2));
  db := abs(BLue(rgb1) - Blue(rgb2));
  Result := sqr(dr) + sqr(dg) + sqr(db);
end;


function LocateVal(c: TColor): NReal;
var
  k: integer;
  d, dmin: NReal;
  kmin: integer;
  rgb: longint;
begin
  rgb := ColorToRGB(c);
  kmin := 0;
  dmin := DistRGB(rgb, cols_rgb[0]);
  for k := 1 to high(cols_rgb) do
  begin
    d := DistRGB(rgb, cols_rgb[k]);
    if d < dmin then
    begin
      dmin := d;
      kmin := k;
    end;
  end;
  if dmin < 1500 then
    Result := vals_[kmin]
  else
    Result := -1111111;
end;


function DateTimeToWindTydyStr(const fecha: TDateTime): string;
var
  Year, Month, Day: word;
  Hour, Min, Sec, MSec: word;
  res: string;
begin
  DecodeDate(fecha, Year, Month, Day);
  DecodeTime(fecha, Hour, Min, Sec, MSec);
  res := IntToStr(Year) + '-' + pad2d(IntToStr(Month)) + '-' + pad2d(IntToStr(Day));
  res := res + '-' + pad2d(IntToStr(Hour));
  Result := res;
end;

function ArchiWindyty(sdt: string; nArchi: integer): string;
begin
  Result := 'c:\basura\windyty' + sdt + '_' + IntToStr(nARchi) + '.bmp';
end;

function ArchiWindyty_err(sdt: string; nArchi: integer): string;
begin
  Result := 'c:\basura\windtydy' + sdt + '_' + IntToStr(nARchi) + '.bmp';
end;


procedure TForm1.btGetWindtydyClick(Sender: TObject);
var
  dt0, dt: TDateTime;
  sdt: string;
  archi_sal, archi1, archi2: string;
  ent_lst: textfile;
  sal: textfile;
  kPaso, kParque: integer;
  ap: TParqueEolico;
  NPasos: integer;
  r: string;



begin
  filemode:=0;
  assignfile( ent_lst, Archi_Windyty_Fotos_lst  );
  reset( ent_lst );

  filemode:= 2;
  archi_sal := 'c:\basura\windyty_sal.xlt';
  assignfile(sal, archi_sal);
  rewrite(sal);

  while not eof( ent_lst ) do
  begin
    readln( ent_lst, r );
    kPaso:= nextint( r );
    sdt:= nextpal( r );

    writeln( kPaso, ': ', sdt );
    archi1 := ArchiWindyty(sdt, 1);
    archi2 := ArchiWindyty(sdt, 2);

    // esto es porque me había equivocado en los nombre
    if not FileExists( archi1 ) then
      archi1 := ArchiWindyty_err(sdt, 1);
    if not FileExists( archi2 ) then
      archi2 := ArchiWindyty_err(sdt, 2);

    LoadFromArchi(Image1, archi1);
    LoadFromArchi(Image2, archi2);

    flg_ImagenInicializada:= true;

    ScanValsColors;

    while cbWait.checked do
      application.ProcessMessages;
    cbWait.Checked:= false;

    flg_ImagenInicializada := True;
    flg_SinDiferenciar := True;

    write( sal, sdt );
    btPlotParquesClick(Self);
    for kParque := 0 to parques_eolicos.Count - 1 do
    begin
      ap := parques_eolicos[kParque];
      if ap.Activo = 1 then
      begin
        Write(sal, #9, ap.viento_dir_gr, #9, ap.viento_vel_mps, #9,
          ap.Potencia_Autorizada_MW);
      end;
    end;
    writeln(sal);

  end;
  closefile(sal);
end;

procedure TForm1.btColorearClick(Sender: TObject);
var
  k, j: integer;
  c: TColor;
  v: NReal;
begin
  for k := 0 to Image1.Width - 1 do
  begin
    for j := 0 to Image1.Height - 1 do
    begin
      c := Image1.Canvas.Pixels[k, j];
      v := LocateVal(c);
      if v < 0 then
        Image1.Canvas.Pixels[k, j] := clRed
      else
        Image1.Canvas.Pixels[k, j] := clBlue;
    end;
  end;
end;

procedure TForm1.btGetFotosClick(Sender: TObject);
var
  dt0, dt: TDateTime;
  sdt: string;
  archi_sal, archi1, archi2: string;
  kPaso: integer;
  NPasos: integer;
  chrome_hwnd: HWND;
  sal: textfile;

begin

  assignfile( sal, Archi_Windyty_Fotos_lst );
  rewrite( sal );

  // Cierro cualquier instancia abierta
  chrome_hwnd := Get_Window_Handle('Windyty', 'Chrome_');
  while chrome_hwnd <> 0 do
  begin
    chrome_hwnd := Get_Window_Handle('Windyty', 'Chrome_');
    Close_Window(chrome_hwnd);
  end;
  dt0 := trunc((now - 3.0 / 24.0) * 8) / 8.0;
  sdt := DateTimeToWindTydyStr(dt0);
  NPasos := 10 * 8;

  if cbNewImages.Checked then
    NPasos := 1;
  for kPaso := 0 to NPasos-1 do
  begin
    dt := dt0 + kPaso * 3.0 / 24.0;
    sdt := DateTimeToWindTydyStr(dt);
    writeln(kPaso, ' ', DateTimeToStr(dt),' ->', sdt );

    writeln( sal, kpaso,#9, sdt );

    archi1 := ArchiWindyty(sdt, 1);
    archi2 := ArchiWindyty(sdt, 2);
    if not ((fileExists(archi1) and fileExists(archi2)) and not
      cbNewImages.Checked) then
    begin
      cbNewImages.Checked := False;
      OpenURL('https://www.windyty.com/?975h,' + sdt + ',-32.676,-55.712,7');
      Hide;
      Sleep(1000 * 7);
      chrome_hwnd := Get_Window_Handle('Windyty', 'Chrome_');
      if chrome_hwnd = 0 then
        raise Exception.Create('No encontré ventana del Chrome');
      GetCopy(Image1);
      SaveToArchi(Image1, archi1);
      Sleep(10);
      GetCopy(Image2);
      SaveToArchi(Image2, archi2);
      Close_Window(chrome_hwnd);
      Show;
    end;
  end;
  closefile( sal );
end;



// Explora un rectangulo orientado en la direccion ang
// centrado en x, y de ancho W y largo H
// El resultado es el número de lineas (del total W)
// en la dirección ANG que  resultaron con algún punto en las trazas BLANCAS.
function TForm1.scan_LCB(x, y, W, H: integer; ang: NReal): integer;
var
  kLin, kP: integer;
  xp, yp: integer;
  cnt: integer;
  wdiv2, hdiv2: integer;
  xr, yr: integer;
  v: NReal;
  rd: TRotDesp;
begin
  rd := TRotDesp.Create(x, y, ang);

  wdiv2 := w div 2;
  hdiv2 := h div 2;
  cnt := 0;
  for kLin := 0 to W - 1 do
  begin
    xr := klin - wdiv2;
    for kP := 0 to H - 1 do
    begin
      yr := kP - hdiv2;
      rd.eval(xp, yp, xr, yr);
      v := LocateVal(image1.Canvas.Pixels[xp, yp]);
      if v < 0 then
      begin
        Inc(cnt);
        break;
      end;
    end;
  end;
  Result := cnt;
end;

function TForm1.scan_MCB(x, y, W, H: integer; ang: NReal): integer;
var
  kLin, kP: integer;
  xp, yp: integer;
  cnt: integer;
  wdiv2, hdiv2: integer;
  xr, yr: integer;
  v: NReal;
  rd: TRotDesp;
  MCB: integer;
  flg_inbl: boolean;
{$IFDEF _DEBUG_SCNA_MCB_}
  sal: textfile;
{$ENDIF}
begin
  {$IFDEF _DEBUG_SCNA_MCB_}
  assignfile(sal, 'c:\basura\scan_MCB_' + IntToStr(x) + '_' + IntToStr(
    y) + '_' + IntToStr(Trunc(ang)) + '.xlt');
  rewrite(sal);
  {$ENDIF}
  rd := TRotDesp.Create(x, y, ang);
  wdiv2 := w div 2;
  hdiv2 := h div 2;
  MCB := 0;
  for kLin := 0 to W - 1 do
  begin
    {$IFDEF _DEBUG_SCNA_MCB_}
    Write(sal, kLin, #9);
    {$ENDIF}
    cnt := 0;
    xr := klin - wdiv2;
    flg_inbl := False;

    for kP := 0 to H - 1 do
    begin
      yr := kP - hdiv2;
      rd.eval(xp, yp, xr, yr);
      //        v:= LocateVal( image1.Canvas.Pixels[ xp, yp ] );
      //       v:= intensidad( image1.Canvas.Pixels[ xp, yp ] );
      if image2.Canvas.Pixels[xp, yp] = clRed then
      begin
        v := -1;
            {$IFDEF _DEBUG_SCNA_MCB_}
        Write(sal, 'X');
            {$ENDIF}
      end
      else
      begin
        v := 0;
           {$IFDEF _DEBUG_SCNA_MCB_}
        Write(sal, '_');
           {$ENDIF}
      end;

      if v < 0 then
        v := umbral_blanco + 2;
      if flg_inbl then
        if v > umbral_blanco then
          Inc(cnt)
        else
        begin
          flg_inbl := False;
          if cnt > MCB then
            MCB := cnt;
        end
      else
      if v > umbral_blanco then
      begin
        flg_inbl := True;
        cnt := 1;
      end;
           (*
        if v > umbral_blanco then
          image1.Canvas.Pixels[ xp, yp ]:= clWhite
        else
          image1.Canvas.Pixels[ xp, yp ]:= clRed;
         *)
    end;
    if cnt > MCB then
      MCB := cnt;
     {$IFDEF _DEBUG_SCNA_MCB_}
    writeln(sal, #9, MCB);
     {$ENDIF}
  end;
  {$IFDEF _DEBUG_SCNA_MCB_}
  closefile(sal);
  {$ENDIF}
  Result := MCB;
end;

// Promedia las velocidades del rectángulo
function TForm1.Calc_Velocidad(x, y, R: integer; ang: NReal): NReal;
var
  kLin, kP: integer;
  xp, yp: integer;
  wdiv2, hdiv2: integer;
  v: NReal;
  cnt: integer;
  acumV: NReal;
begin
  wdiv2 := R div 2;
  hdiv2 := R div 2;

  cnt := 0;
  acumV := 0;
  for kLin := 0 to R - 1 do
  begin
    xp := x + klin - wdiv2;
    for kP := 0 to R - 1 do
    begin
      yp := y + kP - hdiv2;
      if image2.Canvas.Pixels[xp, yp] <> clRed then
      begin
        v := LocateVal(image1.Canvas.Pixels[xp, yp]);
        if v > 0 then
        begin
          acumV := acumV + V;
          Inc(cnt);
        end;
      end;
    end;
  end;
  if cnt > 0 then
    Result := AcumV / cnt
  else
    Result := -1;
end;



function scan_MaximoBlancosConsecutivos(mvr: TMatR; ang: NReal): integer;
var
  kLin, kP: integer;
  xp, yp: integer;
  cnt: integer;
  mfdiv2, ncdiv2: integer;
  xr, yr: integer;
  v: NReal;
  rd: TRotDesp;
  MCB: integer;
  flg_inbl: boolean;
  cosw, sinw: NReal;
begin

  mfdiv2:= mvr.nf div 2;
  ncdiv2:= mvr.nc div 2;

  cosw:= cos( 2* pi* ang / 360 );
  sinw:= sin( 2* pi* ang / 360 );

  if cosw > sinw then
  begin
    // scan by rows
    padding:= mvr.nc * sinw
  end
  else
  begin
    // scan by cols
  end;

  MCB := 0;
  for kLin := 1 to mvr.nf do
  begin
    cnt := 0;
    xr := klin - wdiv2;
    flg_inbl := False;

    for kP := 0 to H - 1 do
    begin
      yr := kP - hdiv2;
      rd.eval(xp, yp, xr, yr);
      //        v:= LocateVal( image1.Canvas.Pixels[ xp, yp ] );
      //       v:= intensidad( image1.Canvas.Pixels[ xp, yp ] );
      if image2.Canvas.Pixels[xp, yp] = clRed then
      begin
        v := -1;
            {$IFDEF _DEBUG_SCNA_MCB_}
        Write(sal, 'X');
            {$ENDIF}
      end
      else
      begin
        v := 0;
           {$IFDEF _DEBUG_SCNA_MCB_}
        Write(sal, '_');
           {$ENDIF}
      end;

      if v < 0 then
        v := umbral_blanco + 2;
      if flg_inbl then
        if v > umbral_blanco then
          Inc(cnt)
        else
        begin
          flg_inbl := False;
          if cnt > MCB then
            MCB := cnt;
        end
      else
      if v > umbral_blanco then
      begin
        flg_inbl := True;
        cnt := 1;
      end;
           (*
        if v > umbral_blanco then
          image1.Canvas.Pixels[ xp, yp ]:= clWhite
        else
          image1.Canvas.Pixels[ xp, yp ]:= clRed;
         *)
    end;
    if cnt > MCB then
      MCB := cnt;
     {$IFDEF _DEBUG_SCNA_MCB_}
    writeln(sal, #9, MCB);
     {$ENDIF}
  end;
  {$IFDEF _DEBUG_SCNA_MCB_}
  closefile(sal);
  {$ENDIF}
  Result := MCB;
end;


// Promedia las velocidades del rectángulo
function TForm1.Calc_Velocidad_Dir(x, y, R: integer; var DirAng: NReal): NReal;
var
  kLin, kP: integer;
  xp, yp: integer;
  wdiv2, hdiv2: integer;
  v: NReal;
  cnt: integer;
  acumV: NReal;
  mvr: TMatR;

begin
  wdiv2 := R div 2;
  hdiv2 := R div 2;

  cnt := 0;
  acumV := 0;
  mvr:= TMatR.Create_ini( R, R );

  for kLin := 0 to R - 1 do
  begin
    xp := x + klin - wdiv2;
    for kP := 0 to R - 1 do
    begin
      yp := y + kP - hdiv2;
      v := LocateVal(image1.Canvas.Pixels[xp, yp]);
      mvr.pon_e( xp, yp, v );
      if v > 0 then
      begin
        acumV := acumV + V;
        Inc(cnt);
      end;
    end;
  end;

  if cnt = 0 then
  begin
    DirAnt:= -1;
    Result := -1;
    exit; // imposible calcular
  end;
  AcumV:= AcumV / cnt

  // Bien si estoy aquí ahora hago segunda pasada detectando puntos claros


end;







procedure TForm1.PlotFlecha(Image: TImage; x, y: integer; ang: NReal; radio: integer);
var
  kP: integer;
  xp, yp: integer;
  cnt: integer;
  cw, sw: NReal;
  wdiv2, hdiv2: integer;
  xr, yr: integer;
  v: NReal;
  xp1, yp1, xp2, yp2: integer;
  rd: TRotDesp;
begin
  rd := TRotDesp.Create(x, y, ang);
  hdiv2 := radio div 2;
  cnt := 0;

  xr := 0;
  yr := -hdiv2;
  rd.eval(xp1, yp1, xr, yr);
  yr := Radio - hdiv2;
  rd.eval(xp2, yp2, xr, yr);
  Image.Canvas.Line(xp1, yp1, xp2, yp2);

  xr := -4;
  yr := 0;
  rd.eval(xp1, yp1, xr, yr);
  xr := +4;
  rd.eval(xp2, yp2, xr, yr);
  Image.Canvas.Line(xp1, yp1, xp2, yp2);

  xr := 0;
  yr := radio - hdiv2;
  rd.eval(xp1, yp1, xr, yr);
  xr := -4;
  yr := radio - 4 - hdiv2;
  rd.eval(xp2, yp2, xr, yr);
  Image.Canvas.Line(xp1, yp1, xp2, yp2);

  xr := 0;
  yr := radio - hdiv2;
  rd.eval(xp1, yp1, xr, yr);
  xr := +4;
  yr := radio - 4 - hdiv2;
  rd.eval(xp2, yp2, xr, yr);
  Image.Canvas.Line(xp1, yp1, xp2, yp2);

end;

// Busca la dirección de mínimo valor de LCB
function TForm1.search_minLCB_dir(x, y, W, H: integer): NReal;
var
  kDir: integer;
  ang: NReal;
  kMinMin, kMinMax: integer;
  minLCB: integer;
  LCB: integer;
  nDirs: integer;
  dang: NReal;
begin
  ang := 0;
  dang := 2; // grados entre direcciones
  kMinMin := 0;
  kMinMax := 0;
  minLCB := scan_LCB(x, y, W, H, 0);
  nDirs := trunc(180 / dang);
  for kDir := 1 to nDirs - 1 do
  begin
    ang := kDir * 2;
    LCB := scan_LCB(x, y, W, H, ang);
    if LCB < minLCB then
    begin
      minLCB := LCB;
      kMinMin := kDir;
      kMinMax := kMinMin;
    end
    else
    if LCB = minLCB then
    begin
      kMinMax := kDir;
    end;
  end;
  Result := (kMinMin + kMinMax) / 2.0 * 2.0;
end;


// Busca la dirección con máximo valor de MCB
function TForm1.search_maxMCB_dir(x, y, W, H: integer): NReal;
var
  kDir: integer;
  ang: NReal;
  kMinMin, kMinMax: integer;
  maxMCB: integer;
  MCB: integer;
  nDirs: integer;
  dang: NReal;
begin
  ang := 0;
  dang := 2; // grados entre direcciones
  kMinMin := 0;
  kMinMax := 0;
  maxMCB := scan_MCB(x, y, W, H, 0);
  nDirs := trunc(180 / dang);
  for kDir := 1 to nDirs - 1 do
  begin
    application.ProcessMessages;
    ang := kDir * 2;
    MCB := scan_MCB(x, y, W, H, ang);
    if MCB > maxMCB then
    begin
      maxMCB := MCB;
      kMinMin := kDir;
      kMinMax := kMinMin;
    end
    else
    if MCB = maxMCB then
    begin
      kMinMax := kDir;
    end;
  end;
  Result := (kMinMin + kMinMax) / 2.0 * 2.0;
end;




procedure TForm1.btPlotParquesClick(Sender: TObject);
var
  k: integer;
  ap: TParqueEolico;
  dir, vel: NReal;
begin
  if parques_eolicos = nil then
    parques_eolicos := Create_Parques_Uruguay;

  if flg_SinDiferenciar then
    btDifImagesClick(self);
  for k := 0 to parques_eolicos.Count - 1 do
  begin
    ap := parques_eolicos[k];
    LatLongToXY(ap.left, ap.top, ap.Latitud, ap.Longitud);

    case ap.activo of
      -1: // no se hace
      begin
        color := clRed;
      end;
      0: // En construcción
      begin
        color := clBlue;
      end;
      1: // En Operación
      begin
        ;
        color := clGreen;
        //            dir :=  search_minLCB_dir( ap.left, ap.top, 100, 50);
        dir := search_maxMCB_dir(ap.left, ap.top, 200, 100);
        vel := calc_velocidad(ap.left, ap.top, 50, dir);
        ap.viento_dir_gr := dir;
        ap.viento_vel_mps := vel;
      end;
      else
        raise Exception.Create('Estado desconocido en parque: ' +
          ap.CentralGeneradora + ', ' + ap.AgenteGenerador);
    end;


 (*
     Image1.Canvas.Brush.Color:= color;
     Image1.Canvas.RadialPie( ap.left-40, ap.top-40, ap.left+40, ap.top+40,0, 360*16 );
 *)
  end;


  for k := 0 to parques_eolicos.Count - 1 do
  begin
    ap := parques_eolicos[k];
    if ap.activo = 1 then
    begin
      plotFlecha(Image2, ap.left, ap.top, ap.viento_dir_gr, 50);
      plotFlecha(Image1, ap.left, ap.top, ap.viento_dir_gr, 50);
      Image1.Canvas.TextOut(ap.left, ap.top, FloatToStr(
        trunc(ap.viento_vel_mps * 10) / 10));
      //            Image1.Canvas.TextOut( ap.Left, ap.top, ap.CentralGeneradora );

    end;
  end;


end;

procedure TForm1.btDifImagesClick(Sender: TObject);
var
  k, j: integer;
  c1, c2: TColor;
  v: NReal;

begin
  for k := x_Marco_Left to x_Marco_Right do
  begin
    for j := y_Marco_Top to y_Marco_Bottom do
    begin
      c1 := Image1.Canvas.Pixels[k, j];
      c2 := Image2.Canvas.Pixels[k, j];
      if c1 = c2 then
        Image2.Canvas.Pixels[k, j] := clWhite
      else
        Image2.Canvas.Pixels[k, j] := clRed;
    end;
  end;
  flg_SinDiferenciar := False;
end;


function GetHandle(windowtitle: string): HWND;
var
  WindowList: TList;
  h, TopWindow: HWND;
  Dest: array[0..80] of char;
  i: integer;
  s: string;

  function getWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
  begin
    Result := True;
    WindowList.Add(Pointer(Handle));
  end;

begin
  Result := 0;

  try
    WindowList := TList.Create;
    TopWindow := Application.MainFormHandle;
    EnumWindows(@getWindows, longint(@TopWindow));
    i := 0;
    while (i < WindowList.Count) and (Result = 0) do
    begin
      GetWindowText(HWND(WindowList[i]), Dest, sizeof(Dest) - 1);
      s := dest;
      if length(s) > 0 then
      begin
        if (Pos(UpperCase(Windowtitle), UpperCase(s)) >= 1) then
        begin
          h := HWND(WindowList[i]);
          if IsWindow(h) then
            Result := h;
        end;
      end;
      Inc(i);
    end
  finally
    WindowList.Free;
  end;
end;





procedure TForm1.GetCopy(Image: TImage);
begin
  // oculto la form para copiar la pantalla
  autosize := False;
  // le doy tiempo a desaparecer y obtengo la copia

  //copiarPantalla(image.Picture.Bitmap);
  ScreenShotActiveWindow(Image.Picture.Bitmap);

  image.Width := image.Picture.Bitmap.Width;
  image.Height := image.Picture.Bitmap.Height;
end;


procedure TForm1.ScanValsColors;
var
  k: integer;
  dy: NReal;

begin

  setlength(vals_, 14);
  for k := 0 to 10 do
    vals_[k] := 2 * k;
  vals_[11] := 24;
  vals_[12] := 27;
  vals_[13] := 29;

  setlength(cols_rgb, 14);

  dy := (y_vel29 - y_vel00) / 13.0;

  umbral_blanco := 0;
  for k := 0 to 13 do
  begin
    color := Image1.Canvas.Pixels[x_vels, trunc(y_vel00 + k * dy + 0.5)];
    Image1.Canvas.Pixels[x_vels, trunc(y_vel00 + k * dy + 0.5)] := clYellow;

    cols_rgb[k] := ColorToRGB(color);
    if Intensidad(color) > umbral_blanco then
      umbral_blanco := Intensidad(Color);
  end;

  umbral_blanco := umbral_blanco + 3;

end;

procedure TForm1.SaveToArchi(Image: TImage; archi: string);
var
  b: TBitMap;
begin
  b := TBitMap.Create;
  try
    b.Width := Image.Picture.Width;
    b.Height := Image.Picture.Height;
    b.canvas.CopyRect(Rect(0, 0, b.Width, b.Height),
      Image.Canvas, Rect(0, 0, b.Width, b.Height));
    b.SaveToFile(archi);
  finally
    b.Free;
  end;
end;

procedure TForm1.LoadFromArchi(Image: TImage; archi: string);
var
  b: TBitMap;
begin
  b := TBitMap.Create;
  try
    b.LoadFromFile(archi);
    Image.Picture.Bitmap.Assign(b);
    Image.Width := Image.Picture.Width;
    Image.Height := Image.Picture.Height;
    Show;
  finally
    b.Free;
  end;
end;


procedure TForm1.copiarPantalla_old(bm: TBitMap);
var
  //  DestRect,
  SourceRect: TRect;
  h: THandle;
  hdcSrc: THandle;
  wx, hy: integer;
  logPix_X: longint;
  logPix_Y: longint;
  sf_x: longint;
  sf_y: longint;
begin
  h := GetDesktopWindow;
  // h := GetForegroundWindow;
  if h <> 0 then
  begin
    try
      hdcSrc := GetWindowDC(h);
      GetWindowRect(h, SourceRect);
      wx := SourceRect.Right - SourceRect.Left;
      hy := SourceRect.Bottom - SourceRect.Top;

      wx := GetDeviceCaps(hdcSrc, HORZRES);
      hy := GetDeviceCaps(hdcSrc, VERTRES);
      logPix_X := getDeviceCaps(hdcSrc, LOGPIXELSX);
      logPix_Y := getDeviceCaps(hdcSrc, LOGPIXELSY);
      sf_x := getDeviceCaps(hdcSrc, SCALINGFACTORX);
      sf_y := getDeviceCaps(hdcSrc, SCALINGFACTORY);

      bm.Width := wx;
      bm.Height := hy;

      radio := trunc(wx / 96 + 0.5);

      //     DestRect := Rect(0, 0, SourceRect.Right - SourceRect.Left, SourceRect.Bottom - SourceRect.Top);
      StretchBlt(bm.Canvas.Handle, 0, 0, wx, hy,
        hdcSrc, 0, 0, wx, hy, SRCCOPY);
    finally
      ReleaseDC(0, hdcSrc);
    end;
  end;
end;




procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.DoubleBuffered := True;  // avoids flicker
  flg_ImagenInicializada := False;
  flg_SinDiferenciar := False;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: integer);
begin
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
var
  s: string;
  color: TColor;
  r, g, b: integer;
  vel: NREal;

begin
  if not flg_ImagenInicializada then
    exit;
  s := 'x: ' + IntToStr(x) + ', y: ' + IntToStr(y);
  color := Image1.Canvas.Pixels[X, Y];
  s := s + ', color: ' + ColorToString(color);

  vel := LocateVal(color);
  s := s + ', vel[m/s]: ' + FloatToStr(vel);
  edit1.Text := s;
end;


initialization
  parques_eolicos := nil;
end.
