mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-28 13:33:40 +02:00
320 lines
8.6 KiB
ObjectPascal
320 lines
8.6 KiB
ObjectPascal
unit TAColorMap;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics,
|
|
TAChartUtils, TACustomSource, TASources, TACustomSeries;
|
|
|
|
type
|
|
TColorMapPalette = (cmpHot, cmpCold, cmpRainbow, cmpMonochrome);
|
|
|
|
TColorMap = class(TComponent)
|
|
private
|
|
FBuiltinColorSource: TListChartSource;
|
|
FBuiltinPalette: TColormapPalette;
|
|
FColorExtentMin, FColorExtentMax: Double;
|
|
FColorSource: TCustomChartSource;
|
|
FColorSourceListener: TListener;
|
|
FInterpolate: Boolean;
|
|
FNumXCountNeeded: Integer;
|
|
FNumYCountNeeded: Integer;
|
|
FPaletteMax: Double;
|
|
FPaletteMin: Double;
|
|
FOnChanged: TNotifyEvent;
|
|
function GetColorSource: TCustomChartSource;
|
|
procedure SetBuiltinPalette(AValue: TColorMapPalette);
|
|
procedure SetColorSource(AValue: TCustomChartSource);
|
|
procedure SetPaletteMax(AValue: Double);
|
|
procedure SetPaletteMin(AValue: Double);
|
|
protected
|
|
procedure BuildPalette(APalette: TColorMapPalette);
|
|
procedure CheckColorSource(ASource: TCustomChartSource);
|
|
procedure ColorMapChanged(Sender: TObject); virtual;
|
|
procedure ColorSourceChanged(Sender: TObject); virtual;
|
|
function OwnerSeries: TCustomChartSeries; inline;
|
|
procedure UpdateColorExtent;
|
|
public
|
|
constructor Create(AOwner: TCustomChartSeries; ANumXCountNeeded, ANumYCountNeeded: Integer); reintroduce;
|
|
destructor Destroy; override;
|
|
procedure Assign(ASource: TPersistent); override;
|
|
function ColorByValue(AValue: Double): TColor;
|
|
function IsColorSourceStored: boolean;
|
|
function IsPaletteMaxStored: Boolean;
|
|
function IsPaletteMinStored: Boolean;
|
|
|
|
// Read-only properties
|
|
property BuiltinColorSource: TListChartSource read FBuiltinColorSource;
|
|
property ColorExtentMax: Double read FColorExtentMax;
|
|
property ColorExtentMin: Double read FColorExtentMin;
|
|
|
|
// Read/write properties
|
|
property BuiltinPalette: TColormapPalette
|
|
read FBuiltInPalette write SetBuiltinPalette;
|
|
property ColorSource: TCustomChartSource
|
|
read GetColorSource write SetColorSource;
|
|
property Interpolate: Boolean
|
|
read FInterpolate write FInterpolate;
|
|
property PaletteMax: Double
|
|
read FPaletteMax write SetPaletteMax;
|
|
property PaletteMin: Double
|
|
read FPaletteMin write SetPaletteMin;
|
|
property OnChanged: TNotifyEvent
|
|
read FOnChanged write FOnChanged;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
GraphUtil, Math,
|
|
TAChartStrConsts, TAMath;
|
|
|
|
constructor TColorMap.Create(AOwner: TCustomChartSeries;
|
|
ANumXCountNeeded, ANumYCountNeeded: Integer);
|
|
const
|
|
BUILTIN_SOURCE_NAME = 'BuiltinColors';
|
|
begin
|
|
inherited Create(AOwner);
|
|
FNumXCountNeeded := ANumXCountNeeded;
|
|
FNumYCountNeeded := ANumYCountNeeded;
|
|
FColorSourceListener := TListener.Create(@FColorSource, @ColorSourceChanged);
|
|
FBuiltinColorSource := TBuiltinListChartSource.Create(self, ANumXCountNeeded, ANumYCountNeeded);
|
|
FBuiltinColorSource.XCount := ANumXCountNeeded;
|
|
FBuiltinColorSource.YCount := ANumYCountNeeded;
|
|
FBuiltinColorSource.Name := BUILTIN_SOURCE_NAME;
|
|
FBuiltinColorSource.Broadcaster.Subscribe(FColorSourceListener);
|
|
end;
|
|
|
|
destructor TColorMap.Destroy;
|
|
begin
|
|
FreeAndNil(FBuiltinColorSource);
|
|
FreeAndNil(FColorSourceListener);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TColorMap.Assign(ASource: TPersistent);
|
|
begin
|
|
if ASource is TColorMap then
|
|
with TColorMap(ASource) do begin
|
|
Self.BuiltinPalette := FBuiltinPalette;
|
|
{
|
|
Self.BuiltinPaletteMax := FPaletteMax;
|
|
Self.BuiltinPaletteMin := FPaletteMin;
|
|
}
|
|
Self.ColorSource := FColorSource;
|
|
Self.FInterpolate := FInterpolate;
|
|
Self.FNumXCountNeeded := FNumXCountNeeded;
|
|
Self.FNumYCountNeeded := FNumYCountNeeded;
|
|
end;
|
|
inherited Assign(ASource);
|
|
end;
|
|
|
|
procedure TColorMap.BuildPalette(APalette: TColorMapPalette);
|
|
var
|
|
i: Integer;
|
|
h, s, l: Byte;
|
|
cmax, cmin, factor: Double;
|
|
ex: TDoubleRect;
|
|
begin
|
|
with FBuiltinColorSource do begin
|
|
BeginUpdate;
|
|
try
|
|
Clear;
|
|
case APalette of
|
|
cmpHot:
|
|
begin
|
|
Add(0, 0, '', clBlack);
|
|
Add(1/3, 0, '', clRed);
|
|
Add(2/3, 0, '', clYellow);
|
|
Add(1, 0, '', clWhite);
|
|
end;
|
|
cmpCold:
|
|
begin
|
|
ColorToHLS(clBlue, h, l, s);
|
|
i := 0;
|
|
while i <= 255 do begin
|
|
Add(i, 0, '', HLSToColor(h, i, s));
|
|
inc(i, 32);
|
|
end;
|
|
Add(255, 0, '', clWhite);
|
|
end;
|
|
cmpRainbow:
|
|
begin
|
|
i := 0;
|
|
while i <= 255 do begin // i is hue
|
|
Add(i, 0, '', HLSToColor(i, 128, 255));
|
|
inc(i, 32);
|
|
end;
|
|
Add(255, 0, '', HLSToColor(255, 128, 255));
|
|
end;
|
|
cmpMonochrome:
|
|
begin
|
|
i := 0;
|
|
while i <= 255 do begin
|
|
Add(i, 0, '', RgbToColor(i, i, i));
|
|
inc(i, 32);
|
|
end;
|
|
Add(255, 0, '', clWhite);
|
|
end;
|
|
else
|
|
raise EChartError.Create('[TAColorMap.BuildPalette] Palette kind not supported.');
|
|
end;
|
|
|
|
if FPaletteMin < FPaletteMax then begin
|
|
cmin := FPaletteMin;
|
|
cmax := FPaletteMax;
|
|
end else
|
|
if FPaletteMax < FPaletteMin then begin
|
|
cmin := FPaletteMax;
|
|
cmax := FPaletteMin;
|
|
end else
|
|
exit;
|
|
|
|
ex := Extent;
|
|
if (ex.a.x = ex.b.x) then
|
|
exit;
|
|
factor := (cmax - cmin) / (ex.b.x - ex.a.x);
|
|
for i:=0 to Count-1 do
|
|
Item[i]^.X := (Item[i]^.X - ex.a.x) * factor + cmin;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TColorMap.ColorByValue(AValue: Double): TColor;
|
|
var
|
|
lb, ub: Integer;
|
|
c1, c2: TColor;
|
|
v1, v2: Double;
|
|
begin
|
|
if (ColorSource = nil) or (ColorSource.Count = 0) then
|
|
exit(clTAColor);
|
|
ColorSource.FindBounds(AValue, SafeInfinity, lb, ub);
|
|
if Interpolate and InRange(lb, 1, ColorSource.Count - 1) then begin
|
|
with ColorSource[lb - 1]^ do begin
|
|
v1 := X;
|
|
c1 := Color;
|
|
end;
|
|
with ColorSource[lb]^ do begin
|
|
v2 := X;
|
|
c2 := Color;
|
|
end;
|
|
if v2 <= v1 then
|
|
Result := c1
|
|
else
|
|
Result := InterpolateRGB(c1, c2, (AValue - v1) / (v2 - v1));
|
|
end
|
|
else
|
|
Result := ColorSource[EnsureRange(lb, 0, ColorSource.Count - 1)]^.Color;
|
|
end;
|
|
|
|
procedure TColorMap.CheckColorSource(ASource: TCustomChartSource);
|
|
begin
|
|
if ASource = nil then
|
|
exit;
|
|
if ASource.XCount < FNumXCountNeeded then
|
|
raise EXCountError.CreateFmt(rsSourceCountError, [ClassName, FNumXCountNeeded, 'x']);
|
|
if ASource.YCount < FNumYCountNeeded then
|
|
raise EYCountError.CreateFmt(rsSourceCountError, [ClassName, FNumYCountNeeded, 'y']);
|
|
end;
|
|
|
|
procedure TColorMap.ColorMapChanged(Sender: TObject);
|
|
begin
|
|
if Assigned(FOnChanged) then
|
|
FOnChanged(Sender);
|
|
end;
|
|
|
|
procedure TColorMap.ColorSourceChanged(Sender: TObject);
|
|
begin
|
|
if (Sender <> FBuiltinColorSource) and (Sender is TCustomChartSource) then
|
|
try
|
|
CheckColorSource(TCustomChartSource(Sender));
|
|
except
|
|
ColorSource := nil; // revert to built-in source
|
|
raise;
|
|
end;
|
|
UpdateColorExtent;
|
|
ColorMapChanged(Sender);
|
|
end;
|
|
|
|
function TColorMap.GetColorSource: TCustomChartSource;
|
|
begin
|
|
if Assigned(FColorSource) then
|
|
Result := FColorSource
|
|
else
|
|
Result := FBuiltinColorSource;
|
|
end;
|
|
|
|
function TColorMap.IsColorSourceStored: boolean;
|
|
begin
|
|
Result := FColorSource <> nil;
|
|
end;
|
|
|
|
function TColorMap.IsPaletteMaxStored: Boolean;
|
|
begin
|
|
Result := FPaletteMax <> 0;
|
|
end;
|
|
|
|
function TColorMap.IsPaletteMinStored: Boolean;
|
|
begin
|
|
Result := FPaletteMin <> 0;
|
|
end;
|
|
|
|
function TColorMap.OwnerSeries: TCustomChartSeries;
|
|
begin
|
|
Result := Owner as TCustomChartSeries;
|
|
end;
|
|
|
|
procedure TColorMap.SetBuiltinPalette(AValue: TColorMapPalette);
|
|
begin
|
|
FBuiltinPalette := AValue;
|
|
BuildPalette(FBuiltinPalette);
|
|
end;
|
|
|
|
procedure TColorMap.SetColorSource(AValue: TCustomChartSource);
|
|
begin
|
|
if AValue = FBuiltinColorSource then
|
|
AValue := nil;
|
|
if FColorSource = AValue then
|
|
exit;
|
|
CheckColorSource(AValue);
|
|
if FColorSourceListener.IsListening then
|
|
ColorSource.Broadcaster.Unsubscribe(FColorSourceListener);
|
|
FColorSource := AValue;
|
|
ColorSource.Broadcaster.Subscribe(FColorSourceListener);
|
|
ColorSourceChanged(Self);
|
|
end;
|
|
|
|
procedure TColorMap.SetPaletteMax(AValue: Double);
|
|
begin
|
|
if AValue = FPaletteMax then exit;
|
|
FPaletteMax := AValue;
|
|
BuildPalette(FBuiltinPalette);
|
|
end;
|
|
|
|
procedure TColorMap.SetPaletteMin(AValue: Double);
|
|
begin
|
|
if AValue = FPaletteMin then exit;
|
|
FPaletteMin := AValue;
|
|
BuildPalette(FBuiltinPalette);
|
|
end;
|
|
|
|
procedure TColorMap.UpdateColorExtent;
|
|
var
|
|
ext: TDoubleRect;
|
|
begin
|
|
if (csDestroying in ComponentState) then
|
|
exit;
|
|
|
|
ext := ColorSource.Extent;
|
|
FColorExtentMin := ext.a.x;
|
|
FColorExtentMax := ext.b.x;
|
|
end;
|
|
|
|
end.
|
|
|