{*******************************************}
{   Delphi Lab Tools 1.0                    }
{                                           }
{   TeeChart (Steema) drawing routines      }
{                                           }
{   Copyright (c) 1997-2002                 }
{   Marjan Slatinek & Janez Makovsek        }
{   All Rights Reserved                     }
{*******************************************}
unit MtxVecTee;

{$I BdsppDefs.inc}
interface

Uses Windows, Graphics, Sysutils, Classes, Controls, TeEngine, Chart, Series,
     Forms, MtxVec, Math387, MtxVecEdit;

type TMtxEditTeeItemAcces = class(TMtxEditTeeItem);

type TTeeItemDrawCols = class(TMtxEditTeeItem)
  private
    procedure DrawCols(Sender: TObject);
    procedure IdleCols(Sender: TObject);
  public
    Constructor Create(Form: TMtxVecEditForm);override;
  end;

type TTeeItemDrawRows = class(TMtxEditTeeItem)
  private
    procedure DrawRows(Sender: TObject);
    procedure IdleRows(Sender: TObject);
  public
    Constructor Create(Form: TMtxVecEditForm);override;
  end;

  TGridPalette=packed record
    UpToValue : TChartValue;
    Color     : TColor;
  end;

  TGridPaletteStyle = (palRange, palRangeCustom, palCustom);

  TPaletteList=Array of TGridPalette;

{ adapted from TeeChart v5 PRO, copyright David Berneda }
type TMtxGridSeries = class(TChartSeries)
  private
    FMatrix: TMtx;
    FBitmap: TBitmap;
    FTopColor: TColor;
    FBottomColor: TColor;
    FPaletteSteps: Integer;
    FPalette : TPaletteList;
    { internal variables }
    IRangeRed    : Integer;
    IBottomRed   : Integer;
    IRangeGreen  : Integer;
    IBottomGreen    : Integer;
    IRangeBlue   : Integer;
    IBottomBlue     : Integer;
    IRangeInv,
    IRange       : TSample;
    IMinValue    : TSample;
    IMaxValue    : TSample;
    FPaletteStyle: TGridPaletteStyle;
    procedure FillIndexes;
    procedure SetMatrix(const Value: TMtx);
    procedure SetBottomColor(const Value: TColor);
    procedure SetTopColor(const Value: TColor);
    procedure SetPaletteSteps(const Value: Integer);
    procedure CalcColorRange(ABottom,ATop: TColor);
    procedure CheckPalette;
    procedure SetPaletteStyle(const Value: TGridPaletteStyle);
    procedure InternalRangeSet(AMin,AMax: TSample);
  protected
    procedure DrawAllValues; override;
    Procedure DoBeforeDrawChart; override;
    {$IFDEF TEE5}class Function GetEditorClass:String; override;{$ENDIF}
  public
    property Matrix: TMtx read FMatrix write SetMatrix;
    {$IFNDEF TEE5}Function GetEditorClass:String; override;{$ENDIF}
    function MaxXValue: double; override;
    function MaxYValue: double; override;
    function MinXValue: double; override;
    function MinYValue: double; override;
    function Clicked(X, Y: Integer): Integer; override;
    function CountLegendItems:Integer; override;
    {$IFNDEF TEESTD}
    function LegendItemColor(LegendIndex:Integer):TColor; override;
    function LegendString( LegendIndex:Integer; LegendTextStyle:TLegendTextStyle
                                                ):String; override;
    {$ENDIF}
    procedure CreateDefaultPalette;
    procedure CreateCustomRangePalette(ABottomColor, ATopColor: TColor;
      AMin, AMax: TSample; Steps: integer);
    procedure SizeChanged;
    procedure ClearPalette;
    function AddPalette(const AValue:TSample; AColor:TColor):Integer;
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;
  published
    property TopColor: TColor read FTopColor write SetTopColor default clNavy;
    property BottomColor: TColor read FBottomColor write SetBottomColor default clWhite;
    property PaletteSteps: Integer read FPaletteSteps write SetPaletteSteps default 10;
    property PaletteStyle : TGridPaletteStyle read FPaletteStyle write SetPaletteStyle default palRange;
end;


procedure DrawValues(Y: TVec; Series: TChartSeries; XOffset: TSample = 0; XStep: TSample = 1.0; PixelDownSample: boolean = false); overload;
{$IFDEF TEE5}
procedure DrawValues(const Vectors: array of TVec; Series: TChartSeries; XOffset: TSample; XStep: TSample = 1.0); overload;
{$ENDIF}
procedure DrawValues(X,Y: TVec; Series: TChartSeries; PixelDownSample: boolean = false); overload;
procedure DrawValues(const Vectors: Array of TVec; Series: TChartSeries); overload;
procedure DrawValues(const Vectors: Array of TVec; const Series: array of TChartSeries; PixelDownSample: boolean = false); overload;
procedure DrawValues(A: TMtx; Series: TMtxGridSeries); overload;

procedure DrawIt(A: TVec; Caption: string = ''; PixelDownSample: boolean = false); overload;
procedure DrawIt(A: TVec; Width, Height: Integer); overload;
procedure DrawIt(const A: array of TVec; const SeriesCaptions: array of string; Caption: string = ''; PixelDownSample: boolean = false); overload;
procedure DrawIt(A: TMtx; Caption: String = ''); overload;
procedure DrawIt(X,Y: TVec; Caption: string = ''; PixelDownSample: boolean = false); overload;

{$IFDEF TEE5}
var
  Tee_MtxGridSeries,
  Tee_DewGallery: String;
{$ENDIF}

implementation

Uses Dialogs, Registry;

var AxForm: TForm = nil;
    AxChart: TChart = nil;

{Setup TeeChat component }
procedure SetupChart(AChart: TChart; AParent: TWinControl);
begin
  If Assigned(AChart) then
  with AChart do
  begin
    Title.Text.Clear;
    Parent := AParent;
    Align := alClient;
    View3D := false;
    Color := clWhite;
    BevelInner := bvNone;
    BevelOuter := bvNone;
    FreeAllSeries;
    {$IFDEF TEE5}
    Zoom.Brush.Style := bsFDiagonal;
    Zoom.Brush.Color := clSilver;
    Zoom.Pen.Color := clBlack;
    {$ENDIF}
  end;
end;

{ --------------------------------------------------------- }

procedure CleanUpChartForm;
var RegIni: TRegIniFile;
    AFileName: string;
    AFile: TFileStream;
begin
  If Assigned(AxForm) then
  begin        {AChart WriteComponent}
       RegIni := TRegIniFile.Create(MtxRegistryKey);
       RegIni.WriteInteger('DrawIt','Top',AxForm.Top);
       RegIni.WriteInteger('DrawIt','Left',AxForm.Left);
       RegIni.WriteInteger('DrawIt','Width',AxForm.Width);
       RegIni.WriteInteger('DrawIt','Height',AxForm.Height);
       AFileName := RegIni.ReadString('DrawIt','AFileName','C:\');
       AFile := TFileStream.Create(AFileName, fmCreate);
       try
          AFile.WriteComponent(AxChart);
       finally
          AFile.Destroy;
       end;
       AxChart.Destroy;
       AxForm.Destroy;
  end;
end;

procedure PrepareChartForm;
var RegIni: TRegIniFile;
    AFileName: string;
begin
   if not Assigned(AxForm) then
   begin
      AxForm := TForm.Create(nil);
      AxChart := TChart.Create(AxForm);
      RegIni := TRegIniFile.Create(MtxRegistryKey);
      AxForm.Top := RegIni.ReadInteger('DrawIt','Top',AxForm.Top);
      AxForm.Left := RegIni.ReadInteger('DrawIt','Left',AxForm.Left);
      AxForm.Width := RegIni.ReadInteger('DrawIt','Width',AxForm.Width);
      AxForm.Height := RegIni.ReadInteger('DrawIt','Height',AxForm.Height);
      AFileName := RegIni.ReadString('DrawIt','AFileName', MtxVecWorkingDir);
      if FileExists(AFileName) then
      begin
        {  AFile := TFileStream.Create(AFileName, fmOpenRead);
          try      AxChart := TChart(AFile.ReadComponent(AxChart));
          finally  AFile.Destroy;   end;}
      end;
      RegIni.Destroy;
   end; {AChart ReadComponent, could also be placed in initialize/finalize}
   SetupChart(AxChart,AxForm);
end;

{ --------------------------------------------------------- }
{$IFNDEF TEE5}
type TChartValueListAccess=class(TChartValueList)
     end;
{$ENDIF}

{ --------------------------------------------------------- }

procedure DrawValues(Y: TVec; Series: TChartSeries; XOffset: TSample = 0; XStep: TSample = 1.0; PixelDownSample: boolean = false);

    procedure AddValues(AY: TVec; PerformDownS: boolean);
    var i: Integer;
        Len : Integer;
        {$IFDEF TEE5}
        j : Integer;
        PY,PX : TChartValues;
        {$ELSE}
        NotManda: TChartValueList;
        {$ENDIF}
    begin
      Len := AY.Length;
      {$IFDEF TEE5}
      With Series.MandatoryValueList do
      begin
        Count := Len;
        SetLength(Value,Len);
        Modified := true;
      end;
      With Series.NotMandatoryValueList do
      begin
        Count := Len;
        SetLength(Value,Len);
        Modified := true;
      end;
      { Usually MandatoryValueList = YValues i.e. for vertical series }
      PX := Series.NotMandatoryValueList.Value;
      PY := Series.MandatoryValueList.Value;
      j := 0;
      {$ELSE}
      Series.Clear;
      if Series.YMandatory then NotManda := Series.XValues else NotManda := Series.YValues;
      {$ENDIF}
      for i := 0 to Len -1 do
        if Not(IsNanInf(AY.Values[i])) then
        begin
          {$IFDEF TEE5}
          PX[j] := XOffset + i*XStep;
          PY[j] := Ay.Values[i];  {Pazi na i in j indexe}
          Inc(j);
          {$ELSE}
          With Series do
          begin
            TChartValueListAccess(MandatoryValueList).InsertChartValue(
            TChartValueListAccess(NotManda).AddChartValue(XOffset + i*XStep), AY.Values[i]);
          end
         {$ENDIF}
        end;
      {$IFDEF TEE5}
      if (j < Len) then
      begin
        Series.MandatoryValueList.Count := j;
        Series.MandatoryValueList.Modified := true;
        SetLength(Series.MandatoryValueList.Value,j);
        Series.NotMandatoryValueList.Count := j;
        SetLength(Series.NotMandatoryValueList.Value,j);
        Series.NotMandatoryValueList.Modified := true;
      end;
      {$ENDIF}
      Series.RefreshSeries;
      Series.Repaint;
  end;

var CW : Integer;
    pixelY: TVec;
    PC : TCustomAxisPanel;
begin
  if Y.Complex then ERaise('Y.Complex');
  ClearFPU;
  PC := Series.ParentChart;
  CW := PC.Width;
  if (Assigned(PC)) and (PixelDownSample) and (CW < 3 * Y.Length) then
  begin
    CreateIt(pixelY);
    try
      pixelY.PixelDownSample(CW*3,Y,nil,nil,true);
      XStep := XStep * Y.Length/PixelY.Length;
      AddValues(pixelY,true);
    finally
      FreeIt(pixelY);
    end;
  end else AddValues(Y,false);
end;

{ --------------------------------------------------------- }
{$IFDEF TEE5}
procedure DrawValues(const Vectors: array of TVec; Series: TChartSeries;
          XOffset: TSample; XStep: TSample = 1.0);

    procedure AddValues(const AY: array of TVec; PerformDownS: boolean);
    var
        Len,k,i: integer;
        PY: TChartValues;
        VList: TChartValueLists;
    begin
      Len := AY[0].Length;
      VList := Series.ValuesList;
      for i := 0 to VList.Count-1 do
      begin
           VList[i].Count := Len;
           SetLength(VList[i].Value,Len);
           VList[i].Modified := true;
      end; {No IsNanInf checking, because it would be to slow. Do it outside, if needed.}
      PY := VList[0].Value; {First array is X. Second is Y and third is ...}
      for i := 0 to Len-1 do PY[i] := XOffset+i*XStep;
      {$IFDEF TTDOUBLE}
      for k := 0 to VList.Count-2 do CopyMemory(VList[k+1].Value,AY[k].Values,Len*SizeOf(TSample)); {$ENDIF}
      {$IFDEF TTSINGLE}
      for k := 0 to VList.Count-2 do
      begin
          PY := VList[k+1].Value;
          AVec := AY[k];
          for i := 0 to Len-1 do  PY[i] := AVec.Values[i];
      end; {$ENDIF}
      Series.RefreshSeries;
      Series.Repaint;
    end;

begin
  if Length(Vectors) = 0 then ERaise('Length(Vectors) = 0');
  ClearFPU;
  AddValues(Vectors,false);
end;
{$ENDIF}
{ --------------------------------------------------------- }
procedure DrawValues(X,Y: TVec; Series: TChartSeries; PixelDownSample: boolean = false);

    procedure AddValues(AX, AY: TVec);
    var i,Len   : Integer;
        {$IFDEF TEE5}
        PY,PX : TChartValues;
        j: integer;
        {$ELSE}
        NotManda: TChartValueList;
        {$ENDIF}
    begin
      Len := AY.Length;
      {$IFDEF TEE5}
      with Series.MandatoryValueList do
      begin
        Count := Len;
        SetLength(Value,Len);
        Modified := true;
      end;
      With Series.NotMandatoryValueList do
      begin
        Count := Len;
        SetLength(Value,Len);
        Modified := true;
      end;
      PX := Series.NotMandatoryValueList.Value;
      PY := Series.MandatoryValueList.Value;
      j := 0;
      {$ELSE}
      Series.Clear;
      if Series.YMandatory then NotManda := Series.XValues else NotManda := Series.YValues;
      {$ENDIF}
      for i := 0 to Len -1 do
        if Not(IsNanInf(AY.Values[i]) or IsNanInf(AX.Values[i])) then
        begin
          {$IFDEF TEE5}
          PX[j] := AX.Values[i];
          PY[j] := AY.Values[i];
          Inc(j);
          {$ELSE}
          With Series do
          begin
            TChartValueListAccess(MandatoryValueList).InsertChartValue(
            TChartValueListAccess(NotManda).AddChartValue(AX.Values[i]), AY.Values[i]);
          end;
          {$ENDIF}
        end;
      {$IFDEF TEE5}
      if (j < Len) then
      begin
        Series.MandatoryValueList.Count := j;
        Series.MandatoryValueList.Modified := true;
        SetLength(Series.MandatoryValueList.Value,j);
        Series.NotMandatoryValueList.Count := j;
        SetLength(Series.NotMandatoryValueList.Value,j);
        Series.NotMandatoryValueList.Modified := true;
      end;
      {$ENDIF}
      Series.RefreshSeries;
      Series.Repaint;
    end;

var pixelY, pixelX: TVec;
    PC : TCustomAxisPanel;
    CW : Integer;
begin
  if X.Complex or Y.Complex then ERaise('X.Complex or Y.Complex');
  if X.Length <> Y.Length then ERaise('X.Length or Y.Length');
  ClearFPU;
  PC := Series.ParentChart;
  CW := PC.Width;
  if (Assigned(PC)) and (PixelDownSample) and (CW < 3* X.Length) then
  begin
    CreateIt(pixelX,pixelY);
    try
      pixelY.PixelDownSample(CW*3,Y,X,pixelX,false);
      AddValues(pixelX,pixelY);
    finally
      FreeIt(pixelX,pixelY);
    end;
  end else AddValues(X,Y);
end;

{ --------------------------------------------------------- }
procedure DrawValues(const Vectors: Array of TVec; Series: TChartSeries);
var Len,i,j: Integer;
    Vec0Len: Integer;
    {$IFNDEF TEE5}
    Index : Integer;
    {$ENDIF}
const PROC_NAME='DrawValues';
begin
  Len := High(Vectors) - Low(Vectors) + 1;
  if Len <> Series.{$IFDEF TEE5}ValuesList{$ELSE}ValuesLists{$ENDIF}.Count then ERaise(PROC_NAME + 'Vectors array count <> Series.ValuesList.Count');
  try
    ClearFPU;
    Vec0Len := Vectors[0].Length;
    {$IFDEF TEE5}
    for j := 0 to Len -1 do
    With Series.ValuesList[j] do
    begin
      Count := Vec0Len;
      SetLength(Value,Vec0Len);
      Modified := true;
    end;
    {$ELSE}
    Series.Clear;
    {$ENDIF}
    for i := 0 to Vec0Len-1 do
    begin
      {$IFDEF TEE5}
      for j := 0 to Len -1 do Series.ValuesList[j].Value[i] := Vectors[j].Values[i];
      {$ELSE}
      Index := TChartValueListAccess(Series.XValues).AddChartValue(Vectors[0].Values[i]);
      for j := 1 to Len - 1 do TChartValueListAccess(Series.ValuesLists.Items[j]).InsertChartValue(Index,Vectors[j].Values[i]);
      {$ENDIF}
    end;
    Series.RefreshSeries;
    Series.Repaint;
  except
    on E: Exception do ERAISE(PROC_NAME+' : '+E.Message);
  end;
end;

procedure DrawValues(const Vectors: Array of TVec;
                     const Series: array of TChartSeries; PixelDownSample: boolean = false);
var i: Integer;
const PROC_NAME = 'DrawValues';
begin
    if Length(Vectors) <> Length(Series) then ERaise('Length(Vectors) <> Length(Series)');
    try
        for i := 0 to Length(Vectors)-1 do
        begin
             if Vectors[i].Caption <> '' then Series[i].Title := Vectors[i].Caption;
             DrawValues(Vectors[i],Series[i],0,1,PixelDownSample);
        end;
    except
        on E: Exception do ERAISE(PROC_NAME+' : '+E.Message);
    end;
end;

{ --------------------------------------------------------- }
procedure DrawValues(A: TMtx; Series: TMtxGridSeries);
begin
  if A.Complex then ERaise('A.Complex');
  ClearFPU;
  Series.Matrix := A;
end;

{ --------------------------------------------------------- }
procedure DrawIt(A: TMtx; Caption: string = '');
var tmpMtx: TMtx;
begin
    PrepareChartForm;
    if Caption = '' then AxForm.Caption := 'Matrix values'
      else AxForm.Caption := Caption;
    With AxChart do
    begin
      With BottomAxis do
      begin
        Title.Caption := 'Index';
        OtherSide := True;
        Increment := 1.0;
        Automatic := false;
        SetMinMax(0,A.Cols-1);
      end;
      With LeftAxis do
      begin
        Title.Caption := 'Index';
        Increment := 1.0;
        Automatic := false;
        SetMinMax(0,A.Rows-1);
      end;
      Legend.Visible := True;
      AddSeries(TMtxGridSeries.Create(AxForm));
      If A.Complex then
      begin
        Title.Text.Add('Magnitude');
        CreateIt(tmpMtx);
        try
          tmpMtx.Mag(A);
          DrawValues(tmpMtx,TMtxGridSeries(Series[0]));
          AxForm.ShowModal;
        finally
          FreeIt(tmpMtx);
        end;
      end else
      begin
        Title.Text.Add('Value');
        DrawValues(A,TMtxGridSeries(Series[0]));
        AxForm.ShowModal;
      end;
    end;
end;

{ --------------------------------------------------------- }
procedure DrawIt(X,Y: TVec; Caption: string = ''; PixelDownSample: boolean = false);
var  tmpVec : TVec;
begin
     PrepareChartForm;
     if Caption = '' then AxForm.Caption := 'Vector values'
                      else AxForm.Caption := Caption;
     with AxChart do
     begin
         Legend.Visible := false;
         With BottomAxis do
         begin
              OtherSide := False;
              Automatic := True;
              Title.Caption := 'Index';
         end;
         With LeftAxis do
         begin
              Automatic := True;
              if Y.Complex then Title.Caption := 'Magnitude'
              else Title.Caption := 'Value'
         end;
         AddSeries(TFastLineSeries.Create(AxForm));
         If Y.Complex then
         begin
              CreateIt(tmpVec);
              try
                 tmpVec.Mag(Y);
                 DrawValues(X,tmpVec,Series[0],PixelDownSample);
              finally
                 FreeIt(tmpVec);
              end;
         end else DrawValues(X,Y,Series[0],PixelDownSample);
         AxForm.ShowModal;
     end;
end;

procedure DrawIt(A: TVec; Caption: string = ''; PixelDownSample: boolean = false);
var  tmpVec : TVec;
begin
     PrepareChartForm;
     if Caption = '' then AxForm.Caption := 'Vector values'
                      else AxForm.Caption := Caption;
     with AxChart do
     begin
         Legend.Visible := false;
         With BottomAxis do
         begin
              OtherSide := False;
              Automatic := false;
              SetMinMax(0,A.Length-1);
              Increment := 1.0;
              Title.Caption := 'Index';
         end;
         With LeftAxis do
         begin
              Automatic := True;
              if A.Complex then Title.Caption := 'Magnitude'
              else Title.Caption := 'Value'
         end;
         AddSeries(TFastLineSeries.Create(AxForm));
         If A.Complex then
         begin
              CreateIt(tmpVec);
              try
                 tmpVec.Mag(A);
                 DrawValues(tmpVec,Series[0],0,1,PixelDownSample);
              finally
                 FreeIt(tmpVec);
              end;
         end else DrawValues(A,Series[0],0,1,PixelDownSample);
         AxForm.ShowModal;
     end;
end;

procedure DrawIt(const A: array of TVec; const SeriesCaptions: array of string;
                 Caption: string = ''; PixelDownSample: boolean = false);
var tmpVec: TVec;
    i: integer;
begin
     PrepareChartForm;
     if Caption = '' then AxForm.Caption := 'Values'
                    else AxForm.Caption := Caption;
     with AxChart do
     begin
         Legend.Visible := True;
         With BottomAxis do
         begin
            Automatic := True;
            Increment := 1.0;
            OtherSide := False;
            Title.Caption := 'Index';
         end;
         With LeftAxis do
         begin
            Automatic := True;
            Title.Caption := 'Value';
         end;
         for i := 0 to Length(A)-1 do
         begin
             AddSeries(TFastLineSeries.Create(AxForm));
             if i < Length(SeriesCaptions) then
             begin
                 if SeriesCaptions[i] <> '' then Series[i].Title := SeriesCaptions[i] else
                 begin
                 if A[i].Caption = '' then Series[i].Title := 'Series ' + IntToStr(i)
                                      else Series[i].Title := A[i].Caption;
                 end;
             end else
             begin
                 if A[i].Caption = '' then Series[i].Title := 'Series ' + IntToStr(i)
                                      else Series[i].Title := A[i].Caption;
             end;
             If A[i].Complex then
             begin
                  CreateIt(tmpVec);
                  try
                     tmpVec.Mag(A[i]);
                     DrawValues(tmpVec,Series[i],0,1,PixelDownSample);
                  finally
                     FreeIt(tmpVec);
                  end;
             end else DrawValues(A[i],Series[i],0,1,PixelDownSample);
         end;
         AxForm.ShowModal;
    end;
end;

{ --------------------------------------------------------- }
procedure DrawIt(A: TVec; Width, Height: Integer);
var tmpY, tmpX,
    tmpVec    : TVec;
    Step      : TSample;
begin
    AxForm.Caption := 'Vector values';
    AxForm.ClientWidth := Width;
    AxForm.ClientHeight := Height;
    CreateIt(tmpX,tmpY);
    With AxChart do
    try
      Legend.Visible := false;
      With BottomAxis do
      begin
        Automatic := false;
        SetMinMax(0,A.Length-1);
        Increment := 1.0;
        OtherSide := False;
        Title.Caption := 'Index';
      end;
      With LeftAxis do
      begin
        Automatic := True;
        if A.Complex then Title.Caption := 'Magnitude'
        else Title.Caption := 'Value';
      end;
      AddSeries(TFastLineSeries.Create(AxForm));
      If A.Complex then
      begin
        CreateIt(tmpVec);
        try
          tmpVec.Mag(A);
          tmpY.PixelDownSample(Width,tmpVec);
        finally
          FreeIt(tmpVec);
        end;
      end else tmpY.PixelDownSample(Width,A);

      tmpX.Size(tmpY);
      Step := Trunc(A.Length/Width);
      If Step < 1 then Step := 1;
      tmpX.Ramp(0,Step);
      DrawValues(tmpX,tmpY,Series[0]);
      AxForm.ShowModal;
    finally
      FreeIt(tmpX,tmpY);
    end;
end;

{ TTeeItemDrawMtx }

constructor TTeeItemDrawRows.Create(Form: TMtxVecEditForm);
begin
  inherited;
  IAction.Caption := 'Series in rows';
  IAction.OnExecute := DrawRows;
  IAction.OnUpdate := IdleRows;
  MenuItem.Action := IAction;
end;

procedure TTeeItemDrawRows.DrawRows(Sender: TObject);
var tmpVec : TVec;
    BMtx: TMtx;
begin
  if (IForm.AMtx.Rows = 1) then
  begin
    CreateIt(tmpVec);
    try
      tmpVec.Size(IForm.AMtx.Cols,IForm.AMtx.Complex);
      if Iform.Editable then tmpVec.GridToValues(IForm.StringGrid1,IForm.AMtx.Complex,false,true)
                        else tmpVec.CopyMtx(IForm.AMtx);

      DrawIt(tmpVec);
    finally
      FreeIt(tmpVec);
    end;
  end
  else
  begin
    CreateIt(BMtx);
    try
      BMtx.Size(IForm.AMtx);
      if IForm.Editable then BMtx.GridToValues(IForm.StringGrid1,IForm.AMtx.Complex,true)
                        else BMtx.Copy(IForm.Amtx);
      DrawIt(BMtx);
    finally
      FreeIt(BMtx);
    end;
  end;
end;

procedure TTeeItemDrawRows.IdleRows(Sender: TObject);
begin
  IAction.Enabled := IForm.AMtx.Cols > 1;
end;

{ TTeeItemDrawCols }

constructor TTeeItemDrawCols.Create(Form: TMtxVecEditForm);
begin
  inherited;
  IAction.Caption := 'Series in cols';
  IAction.OnExecute := DrawCols;
  IAction.OnUpdate := IdleCols;
  MenuItem.Action := IAction;
end;

procedure TTeeItemDrawCols.DrawCols(Sender: TObject);
var tmpVec : TVec;
    BMtx: TMtx;
begin
  if (IForm.AMtx.Cols = 1) then
  begin
    CreateIt(tmpVec);
    try
      tmpVec.Size(IForm.AMtx.Rows,IForm.AMtx.Complex);
      if IForm.Editable then tmpVec.GridToValues(IForm.StringGrid1,IForm.AMtx.Complex,true,true)
                        else tmpVec.CopyMtx(IForm.Amtx);
      DrawIt(tmpVec);
    finally
      FreeIt(tmpVec);
    end;
  end
  else
  begin
    CreateIt(BMtx);
    try
      BMtx.Size(IForm.AMtx);
      if IForm.Editable then BMtx.GridToValues(IForm.StringGrid1,IForm.AMtx.Complex,true)
                        else BMtx.Copy(IForm.AMtx);
      BMtx.Transp;
      DrawIt(BMtx);
    finally
      FreeIt(BMtx);
    end;
  end;
end;

procedure TTeeItemDrawCols.IdleCols(Sender: TObject);
begin
  IAction.Enabled := IForm.AMtx.Rows > 1;
end;

{ TMtxGridSeries }

function TMtxGridSeries.AddPalette(const AValue: TSample;
  AColor: TColor): Integer;
var t   : Integer;
    tt  : Integer;
Begin
  for t:=0 to Length(FPalette)-1 do
  begin
    if AValue<FPalette[t].UpToValue then
    begin
      SetLength(FPalette,Length(FPalette)+1);
      for tt:=Length(FPalette)-1 downto t+1 do
          FPalette[tt]:=FPalette[tt-1];
      With FPalette[t] do
      begin
        UpToValue:=AValue;
        Color:=AColor;
      end;
      result:=t;
      exit;
    end;
  end;
  result:=Length(FPalette);
  SetLength(FPalette,result+1);
  With FPalette[result] do
  begin
    UpToValue:=AValue;
    Color:=AColor;
  end;
End;

procedure TMtxGridSeries.CalcColorRange(ABottom,ATop: TColor);
begin
  IBottomRed := Byte(ABottom);
  IBottomGreen := Byte(ABottom shr 8);
  IBottomBlue := Byte(ABottom shr 16);
  IRangeRed  := Integer(Byte(ATop))-IBottomRed;
  IRangeGreen:= Integer(Byte(ATop shr 8))-IBottomGreen;
  IRangeBlue := Integer(Byte(ATop shr 16))-IBottomBlue;
end;

procedure TMtxGridSeries.CheckPalette;
begin
  if Assigned(FMatrix) and (FMatrix.Rows*FMatrix.Cols > 0) and (Length(FPalette)=0) then
    CreateDefaultPalette;
end;

constructor TMtxGridSeries.Create(AOwner: TComponent);
begin
  inherited;
  FBitmap:=TBitmap.Create;
  XValues.Order := loNone;
  FBitmap.PixelFormat:=pf24bit;
  FPaletteSteps := 10;
  FBottomColor := clNavy;
  FTopColor := clWhite;
  FPaletteStyle := palRange;
  CalcColorRange(FBottomColor,FTopColor);
end;

procedure TMtxGridSeries.CreateDefaultPalette;
begin
  if Assigned(FMatrix) then
    CreateCustomRangePalette(FBottomColor,FTopColor,FMatrix.Min,FMatrix.Max,FPaletteSteps);
end;

procedure TMtxGridSeries.CreateCustomRangePalette(ABottomColor, ATopColor: TColor;
          AMin, AMax: TSample; Steps: integer);
var i: Integer;
  Percent: TSample;
  InvSteps, StepValue: TSample;
begin
  InternalRangeSet(AMin,AMax);
  CalcColorRange(ABottomColor,ATopColor);
  if IRange > 0 then
  begin
      FPaletteSteps := Steps;
      SetLength(FPalette,FPaletteSteps);
      InvSteps := 1.0/(FPaletteSteps -1);
      StepValue := IRange*InvSteps;
      for i := 0 to FPaletteSteps-1 do
      begin
          FPalette[i].UpToValue := IMinValue + StepValue*i; //Not needed, but then again..
          Percent := i*InvSteps;
          FPalette[i].Color :=  RGB( IBottomRed +Round(Percent*IRangeRed),
                                     IBottomGreen + Round(Percent*IRangeGreen),
                                     IBottomBlue + Round(Percent*IRangeBlue));
      end;
  end;
end;

destructor TMtxGridSeries.Destroy;
begin
  FBitmap.Free;
  FPalette:=nil;
  inherited;
end;

procedure TMtxGridSeries.DoBeforeDrawChart;
begin
  inherited;
  if Assigned(FMatrix) then CheckPalette;
end;

procedure TMtxGridSeries.DrawAllValues;

  Procedure FillBitmap;
  type TColors=packed Array[0..100000000] of TRGBTriple;
  var x,y,i,PalCount   : Integer;
      P        : ^TColors;
      tmp      : TColor;
      mtxvalues: PSArray;
      Percent: TSample;
      cw: Word;
  begin

    for y:= 0 to FBitmap.Height-1 do
    begin
        P:=FBitmap.ScanLine[y];
        mtxValues := Pointer(@FMatrix.Values1D[FBitmap.Width*y]);
        PalCount := Length(FPalette);
        case FPaletteStyle of
        palRange :  begin
                        if IRangeInv = 0.0 then
                        for x := 0 to FBitmap.Width-1 do
                        begin
                             With P^[x] do
                             begin
                                 rgbtRed  :=Byte(FTopColor);
                                 rgbtGreen:=Byte(FTopColor shr 8);
                                 rgbtBlue :=Byte(FTopColor shr 16);
                             end;
                        end else
                        for x := 0 to FBitmap.Width-1 do
                        begin
                            Percent := (mtxValues[x]-IMinValue)*IRangeInv;
                            tmp:=RGB( IBottomRed +Round(Percent*IRangeRed),
                                      IBottomGreen + Round(Percent*IRangeGreen),
                                      IBottomBlue + Round(Percent*IRangeBlue));
                            With P^[x] do
                            begin
                              rgbtRed  :=Byte(tmp);
                              rgbtGreen:=Byte(tmp shr 8);
                              rgbtBlue :=Byte(tmp shr 16);
                            end;
                        end;
                    end;
        palCustom : for x := 0 to FBitmap.Width-1 do
                    begin
                        tmp := FBottomColor;
                        if PalCount>0 then
                        begin
                            tmp := FPalette[PalCount-1].Color;
                            for i := 0 to PalCount-1 do
                            begin
                                With FPalette[i]do
                                if UpToValue >= mtxValues[x] then
                                begin
                                    tmp := Color;
                                    Break;
                                end;
                            end;
                        end;
                        With P^[x] do
                        begin
                             rgbtRed  :=Byte(tmp);
                             rgbtGreen:=Byte(tmp shr 8);
                             rgbtBlue :=Byte(tmp shr 16);
                        end;
                    end;
        palRangeCustom:
                    begin
                        Percent := (FPalette[PalCount-1].UpToValue-FPalette[0].UpToValue); // = IRange
                        if Percent <> 0 then Percent := palCount/Percent else Percent := palCount; //= IRangeInv*PalCount
                        cw := SetRoundToTrunc;
                        for x := 0 to FBitmap.Width-1 do
                        begin
                            i := EnsureRange(0,Integer(Round((mtxValues[x]-IMinValue)*Percent)),palCount-1);
                            tmp := FPalette[i].Color;
                            With P^[x] do
                            begin
                                 rgbtRed  :=Byte(tmp);
                                 rgbtGreen:=Byte(tmp shr 8);
                                 rgbtBlue :=Byte(tmp shr 16);
                            end;
                        end;
                        RestoreRound(cw);
                    end;
        end;
    end;
  end;

Var R : TRect;

  Procedure DrawBitmap;
  begin
    With GetHorizAxis do
    begin
      R.Left:=CalcPosValue(-0.5);
      R.Right:=CalcPosValue(FMatrix.Cols-0.5);
    end;
    With GetVertAxis do
    begin
      R.Top:=CalcPosValue(FMatrix.Rows-0.5);
      R.Bottom:=CalcPosValue(-0.5);
    end;
    With ParentChart.Canvas do
         StretchDraw(R,FBitmap);
  end;

begin
  if Assigned(FMatrix) and (FMatrix.Rows*FMatrix.Cols > 0) then
  begin
    FBitmap.Width := FMatrix.Cols;
    FBitmap.Height:= FMatrix.Rows;
    FillBitmap;
    DrawBitmap;
  end;
end;

function TMtxGridSeries.CountLegendItems: Integer;
begin
  Result:=Length(FPalette);
end;

{$IFNDEF TEESTD}
function TMtxGridSeries.LegendItemColor(LegendIndex: Integer): TColor;
begin
  Result:=FPalette[Length(FPalette)-LegendIndex-1].Color;
end;

function TMtxGridSeries.LegendString(LegendIndex: Integer;
  LegendTextStyle: TLegendTextStyle): String;
var tmp: TSample;
begin
  if CountLegendItems>LegendIndex then
  begin
    tmp:=FPalette[Length(FPalette)-LegendIndex-1].UpToValue;
    result:=FormatSample(ValueFormat,tmp);
  end
  else result:='';
end;
{$ENDIF}

procedure TMtxGridSeries.SetBottomColor(const Value: TColor);
begin
  if FBottomColor <> Value then
  begin
    FBottomColor := Value;
    CalcColorRange(FBottomColor,FTopColor);
    if FPaletteStyle = palRange then CreateDefaultPalette;
    Repaint;
  end;
end;

procedure TMtxGridSeries.SetMatrix(const Value: TMtx);
begin
  FMatrix := Value;
  FillIndexes;
end;

procedure TMtxGridSeries.SetPaletteSteps(const Value: Integer);
begin
  if Value <> FPaletteSteps then
  begin
    FPaletteSteps := Value;
    if FPaletteStyle = palRange then
      CreateDefaultPalette;
    Repaint;
  end;
end;

procedure TMtxGridSeries.SetTopColor(const Value: TColor);
begin
  if FTopColor <> Value then
  begin
    FTopColor := Value;
    CalcColorRange(FBottomColor,FTopColor);
    if FPaletteStyle = palRange then CreateDefaultPalette;
    Repaint;
  end;
end;

procedure TMtxGridSeries.FillIndexes;
var i : Integer;
    irows,icols: Integer;
    imin : Integer;
    imax : Integer;
begin
  Clear;
  if Assigned(FMatrix) then
  begin
    {$IFDEF TEE5}
    BeginUpdate;
    {$ENDIF}
    irows := FMatrix.Rows;
    icols := FMatrix.Cols;
    imin := Min(irows,icols);
    imax := Max(irows,icols);
    for i := 0 to imin do Add(i,'',clTeeColor);
    if irows >= icols then
      for i := imin to imax do AddXY(imin,i,'',clTeeColor)
    else
      for i := imin to imax do AddXY(i,imin,'',clTeeColor);
    {$IFDEF TEE5}
    EndUpdate;
    {$ENDIF}
  end;
end;

procedure TMtxGridSeries.SetPaletteStyle(const Value: TGridPaletteStyle);
begin
  if FPaletteStyle <> Value then
  begin
    FPaletteStyle := Value;
    FPalette := nil;
    Repaint;
  end;
end;

procedure TMtxGridSeries.ClearPalette;
begin
  FPalette := nil;
end;

procedure TMtxGridSeries.SizeChanged;
begin
  FillIndexes;
end;

{$IFDEF TEE5}class{$ENDIF}function TMtxGridSeries.GetEditorClass: String;
begin
  Result := 'TMtxGridSeriesEditor';
end;

function TMtxGridSeries.Clicked(X, Y: Integer): Integer;
var col, row: Integer;
begin
  Result := -1;
  if Assigned(FMatrix) and (FMatrix.Rows*FMatrix.Cols > 0) then
  begin
    col := Trunc(GetHorizAxis.CalcPosPoint(X)+0.5);
    row := FMatrix.Rows - 1 - Trunc(GetVertAxis.CalcPosPoint(Y)+0.5);
    if (col>=0) and (col <FMatrix.Cols) and
    (row>=0) and (row <FMatrix.Rows) then
      Result := col + row*FMatrix.Cols;
  end;
end;

function TMtxGridSeries.MaxXValue: double;
begin
  if Assigned(FMatrix) then Result := FMatrix.Cols -1
  else Result := 0.0;
end;

function TMtxGridSeries.MaxYValue: double;
begin
  if Assigned(FMatrix) then Result := FMatrix.Rows -1
  else Result := 0.0;
end;

function TMtxGridSeries.MinXValue: double;
begin
  Result := 0.0;
end;

function TMtxGridSeries.MinYValue: double;
begin
  Result := 0.0;
end;

procedure TMtxGridSeries.InternalRangeSet(AMin,AMax: TSample);
begin
  if Assigned(FMatrix) then
  begin
    IMinValue := AMin;
    IMaxValue := AMax;
    IRange := IMaxValue - IMinValue;
    IRangeInv := IRange;
    if IRange <> 0.0 then IRangeInv := 1.0/IRange;
  end;
end;

initialization
  RegisterMtxEditItem(TTeeItemDrawRows);
  RegisterMtxEditItem(TTeeItemDrawCols);
  {$IFDEF TEE5}
  Tee_DewGallery := 'Dew Research';
  Tee_MtxGridSeries := 'Mtx Grid';
  RegisterTeeSeries(TMtxGridSeries,@Tee_MtxGridSeries,@Tee_DewGallery,1);
  {$ELSE}
  RegisterTeeSeries(TMtxGridSeries,'Mtx Grid','Dew Research',1);
  {$ENDIF}

finalization
  UnRegisterTeeSeries([TMtxGridSeries]);
  UnRegisterMtxEditItem(TTeeItemDrawCols);
  UnRegisterMtxEditItem(TTeeItemDrawRows);

end.

