mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-11-02 13:40:00 +01:00
TAChart: Support NaNs in bar series
git-svn-id: trunk@32321 -
This commit is contained in:
parent
b1dc4a2a6c
commit
6cad306a34
@ -275,6 +275,7 @@ function FormatIfNotEmpty(AFormat, AStr: String): String; inline;
|
||||
function InterpolateRGB(AColor1, AColor2: Integer; ACoeff: Double): Integer;
|
||||
function IntToColorHex(AColor: Integer): String; inline;
|
||||
function IsNan(const APoint: TDoublePoint): Boolean; overload; inline;
|
||||
function NumberOr(ANum: Double; ADefault: Double = 0.0): Double; inline;
|
||||
|
||||
function OrientToRad(AOrient: Integer): Double; inline;
|
||||
|
||||
@ -285,6 +286,7 @@ function RoundChecked(A: Double): Integer; inline;
|
||||
|
||||
function SafeInfinity: Double; inline;
|
||||
function SafeInRange(AValue, ABound1, ABound2: Double): Boolean;
|
||||
function SafeMin(A, B: Double): Double;
|
||||
function SafeNan: Double; inline;
|
||||
|
||||
procedure SetPropDefaults(AObject: TPersistent; APropNames: array of String);
|
||||
@ -421,6 +423,11 @@ begin
|
||||
Result := IsNan(APoint.X) or IsNan(APoint.Y);
|
||||
end;
|
||||
|
||||
function NumberOr(ANum: Double; ADefault: Double): Double;
|
||||
begin
|
||||
Result := IfThen(IsNan(ANum), ADefault, ANum);
|
||||
end;
|
||||
|
||||
function OrientToRad(AOrient: Integer): Double;
|
||||
begin
|
||||
Result := DegToRad(AOrient / ORIENTATION_UNITS_PER_DEG);
|
||||
@ -454,6 +461,18 @@ begin
|
||||
Result := InRange(AValue, ABound1, ABound2);
|
||||
end;
|
||||
|
||||
function SafeMin(A, B: Double): Double;
|
||||
begin
|
||||
if IsNan(A) then
|
||||
Result := B
|
||||
else if IsNan(B) then
|
||||
Result := A
|
||||
else if A < B then
|
||||
Result := A
|
||||
else
|
||||
Result := B;
|
||||
end;
|
||||
|
||||
function SafeNan: Double;
|
||||
begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
|
||||
@ -219,6 +219,7 @@ type
|
||||
procedure DrawLabels(ADrawer: IChartDrawer);
|
||||
procedure DrawPointers(ADrawer: IChartDrawer);
|
||||
procedure GetLegendItemsRect(AItems: TChartLegendItems; ABrush: TBrush);
|
||||
function NearestXNumber(var AIndex: Integer; ADir: Integer): Double;
|
||||
function GetXRange(AX: Double; AIndex: Integer): Double;
|
||||
function GetZeroLevel: Double; virtual;
|
||||
procedure PrepareGraphPoints(
|
||||
@ -931,15 +932,15 @@ begin
|
||||
end;
|
||||
|
||||
function TBasicPointSeries.GetXRange(AX: Double; AIndex: Integer): Double;
|
||||
var
|
||||
wl, wr: Double;
|
||||
i: Integer;
|
||||
begin
|
||||
case CASE_OF_TWO[AIndex > 0, AIndex < Count - 1] of
|
||||
cotNone: Result := 1.0;
|
||||
cotFirst: Result := Abs(AX - GetGraphPointX(AIndex - 1));
|
||||
cotSecond: Result := Abs(AX - GetGraphPointX(AIndex + 1));
|
||||
cotBoth: Result := Min(
|
||||
Abs(AX - GetGraphPointX(AIndex - 1)),
|
||||
Abs(AX - GetGraphPointX(AIndex + 1)));
|
||||
end;
|
||||
i := AIndex - 1;
|
||||
wl := Abs(AX - NearestXNumber(i, -1));
|
||||
i := AIndex + 1;
|
||||
wr := Abs(AX - NearestXNumber(i, +1));
|
||||
Result := NumberOr(SafeMin(wl, wr), 1.0);
|
||||
end;
|
||||
|
||||
function TBasicPointSeries.GetZeroLevel: Double;
|
||||
@ -960,6 +961,18 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBasicPointSeries.NearestXNumber(
|
||||
var AIndex: Integer; ADir: Integer): Double;
|
||||
begin
|
||||
while InRange(AIndex, 0, Count - 1) do
|
||||
with Source[AIndex]^ do
|
||||
if IsNan(X) then
|
||||
AIndex += ADir
|
||||
else
|
||||
exit(AxisToGraphX(X));
|
||||
Result := SafeNan;
|
||||
end;
|
||||
|
||||
procedure TBasicPointSeries.PrepareGraphPoints(
|
||||
const AExtent: TDoubleRect; AFilterByExtent: Boolean);
|
||||
var
|
||||
@ -1062,7 +1075,7 @@ begin
|
||||
FMinXRange := Abs(x - prevX);
|
||||
for i := 2 to Count - 1 do begin
|
||||
x := Source[i]^.X;
|
||||
FMinXRange := Min(Abs(x - prevX), FMinXRange);
|
||||
FMinXRange := SafeMin(Abs(x - prevX), FMinXRange);
|
||||
prevX := x;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -552,9 +552,9 @@ begin
|
||||
Result := Extent;
|
||||
if YCount < 2 then exit;
|
||||
for i := 0 to Count - 1 do begin
|
||||
h := Item[i]^.Y;
|
||||
h := NumberOr(Item[i]^.Y);
|
||||
for j := 0 to YCount - 2 do begin
|
||||
h += Item[i]^.YList[j];
|
||||
h += NumberOr(Item[i]^.YList[j]);
|
||||
// If some of Y values are negative, h may be non-monotonic.
|
||||
UpdateMinMax(h, Result.a.Y, Result.b.Y);
|
||||
end;
|
||||
@ -607,10 +607,10 @@ begin
|
||||
end
|
||||
else begin
|
||||
ALB := 0;
|
||||
while (ALB < Count) and (Item[ALB]^.X < AXMin) do
|
||||
while (ALB < Count) and (IsNan(Item[ALB]^.X) or (Item[ALB]^.X < AXMin)) do
|
||||
Inc(ALB);
|
||||
AUB := Count - 1;
|
||||
while (AUB >= 0) and (Item[AUB]^.X > AXMax) do
|
||||
while (AUB >= 0) and (IsNan(Item[AUB]^.X) or (Item[AUB]^.X > AXMax)) do
|
||||
Dec(AUB);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -873,7 +873,7 @@ var
|
||||
end;
|
||||
|
||||
var
|
||||
z, ofs: Double;
|
||||
z, ofs, y: Double;
|
||||
begin
|
||||
if IsEmpty then exit;
|
||||
|
||||
@ -894,12 +894,15 @@ begin
|
||||
p := FGraphPoints[pointIndex - FLoBound];
|
||||
if IsRotated then
|
||||
Exchange(p.X, p.Y);
|
||||
if IsNan(p.X) then continue;
|
||||
p.X += ofs;
|
||||
heights[0] := z;
|
||||
heights[1] := p.Y;
|
||||
for stackIndex := 1 to Source.YCount - 1 do
|
||||
heights[stackIndex + 1] :=
|
||||
heights[stackIndex] + Source[pointIndex]^.YList[stackIndex - 1];
|
||||
heights[1] := NumberOr(p.Y, z);
|
||||
for stackIndex := 1 to Source.YCount - 1 do begin
|
||||
y := Source[pointIndex]^.YList[stackIndex - 1];
|
||||
if not IsNan(y) then
|
||||
heights[stackIndex + 1] := heights[stackIndex] + y;
|
||||
end;
|
||||
for stackIndex := 0 to Source.YCount - 1 do
|
||||
BuildBar;
|
||||
end;
|
||||
@ -910,6 +913,7 @@ end;
|
||||
function TBarSeries.Extent: TDoubleRect;
|
||||
var
|
||||
x, ofs, w: Double;
|
||||
i: Integer;
|
||||
begin
|
||||
Result := inherited Extent;
|
||||
if IsEmpty then exit;
|
||||
@ -917,12 +921,18 @@ begin
|
||||
UpdateMinXRange;
|
||||
UpdateMinMax(ZeroLevel, Result.a.Y, Result.b.Y);
|
||||
// Show first and last bars fully.
|
||||
x := GetGraphPointX(0);
|
||||
BarOffsetWidth(x, 0, ofs, w);
|
||||
Result.a.X := Min(Result.a.X, x + ofs - w);
|
||||
x := GetGraphPointX(Count - 1);
|
||||
BarOffsetWidth(x, Count - 1, ofs, w);
|
||||
Result.b.X := Max(Result.b.X, x + ofs + w);
|
||||
i := 0;
|
||||
x := NearestXNumber(i, +1);
|
||||
if not IsNan(x) then begin
|
||||
BarOffsetWidth(x, i, ofs, w);
|
||||
Result.a.X := Min(Result.a.X, x + ofs - w);
|
||||
end;
|
||||
i := Count - 1;
|
||||
x := NearestXNumber(i, -1);
|
||||
if not IsNan(x) then begin
|
||||
BarOffsetWidth(x, i, ofs, w);
|
||||
Result.b.X := Max(Result.b.X, x + ofs + w);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TBarSeries.GetLegendItems(AItems: TChartLegendItems);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user