TAChart: Fix severe speed issue when switching to logarithmic axis. Improved label positioning for logarithmic bar series. Fix drawing of logarithmic bar series.

git-svn-id: trunk@60065 -
This commit is contained in:
wp 2019-01-12 22:28:30 +00:00
parent 6831ce0d0b
commit a9b6333383
2 changed files with 64 additions and 65 deletions

View File

@ -287,11 +287,7 @@ type
UseDataColors: Boolean = false); UseDataColors: Boolean = false);
procedure FindExtentInterval( procedure FindExtentInterval(
const AExtent: TDoubleRect; AFilterByExtent: Boolean); const AExtent: TDoubleRect; AFilterByExtent: Boolean);
{ function GetLabelDataPoint(AIndex, AYIndex: Integer): TDoublePoint; virtual;
function GetErrorBars(APointIndex: Integer; IsXError: Boolean;
out AGraphPointPos, AGraphPointNeg: Double): Boolean;
}
function GetLabelDataPoint(AIndex: Integer): TDoublePoint; virtual;
procedure GetLegendItemsRect(AItems: TChartLegendItems; ABrush: TBrush); procedure GetLegendItemsRect(AItems: TChartLegendItems; ABrush: TBrush);
function GetXRange(AX: Double; AIndex: Integer): Double; function GetXRange(AX: Double; AIndex: Integer): Double;
function GetZeroLevel: Double; virtual; function GetZeroLevel: Double; virtual;
@ -1242,12 +1238,13 @@ var
end; end;
var var
y: Double; y, ysum: Double;
g, gl: TDoublePoint; g, gl: TDoublePoint;
i, si: Integer; i, si: Integer;
ld: TLabelDirection; ld: TLabelDirection;
style: TChartStyle; style: TChartStyle;
lfont: TFont; lfont: TFont;
curr, prev: Double;
begin begin
if not Marks.IsMarkLabelsVisible then exit; if not Marks.IsMarkLabelsVisible then exit;
@ -1257,8 +1254,10 @@ begin
ParentChart.DisableRedrawing; ParentChart.DisableRedrawing;
for i := FLoBound to FUpBound do begin for i := FLoBound to FUpBound do begin
if IsNan(Source[i]^.Point) then continue; if IsNaN(Source[i]^.Point) then continue;
g := GetLabelDataPoint(i); y := Source[i]^.Y;
ysum := y;
prev := GetZeroLevel;
for si := 0 to Source.YCount - 1 do begin for si := 0 to Source.YCount - 1 do begin
if Styles <> nil then begin if Styles <> nil then begin
style := Styles.StyleByIndex(si); style := Styles.StyleByIndex(si);
@ -1267,26 +1266,31 @@ begin
else else
Marks.LabelFont.Assign(lfont); Marks.LabelFont.Assign(lfont);
end; end;
if not Stacked then begin g := GetLabelDataPoint(i, si);
g := GetGraphPoint(i, 0, si); if si > 0 then begin
y := Source[i]^.y;
end else
if si = 0 then
y := Source[i]^.y - GetZeroLevel // wp: does this survive nonlinear transformations?
else begin
y := Source[i]^.YList[si-1]; y := Source[i]^.YList[si-1];
if IsRotated then if Stacked then begin
g.X += AxisToGraphY(y) ysum := ysum + y;
else y := ysum;
g.Y += AxisToGraphY(y); end;
end; end;
gl := g; if not IsNaN(y) then begin
if FMarkPositions = lmpInsideCenter then begin
if IsRotated then if IsRotated then
gl.X -= AxisToGraphX(y)/2 g.X := AxisToGraphX(y)
else else
gl.Y -= AxisToGraphY(y)/2; g.Y := AxisToGraphY(y);
end; end;
curr := TDoublePointBoolArr(g)[not IsRotated];
if MarkPositions = lmpInsideCenter then begin
if IsRotated then
gl := DoublePoint((curr + prev) * 0.5, g.y)
else
gl := DoublePoint(g.X, (curr + prev) * 0.5);
end else
gl := g;
prev := curr;
ld := GetLabelDirection(TDoublePointBoolArr(gl)[not IsRotated]); ld := GetLabelDirection(TDoublePointBoolArr(gl)[not IsRotated]);
with ParentChart do with ParentChart do
if if
@ -1383,7 +1387,7 @@ begin
Result := FErrorBars[AIndex]; Result := FErrorBars[AIndex];
end; end;
function TBasicPointSeries.GetLabelDataPoint(AIndex: Integer): TDoublePoint; function TBasicPointSeries.GetLabelDataPoint(AIndex, AYIndex: Integer): TDoublePoint;
begin begin
Result := GetGraphPoint(AIndex); Result := GetGraphPoint(AIndex);
end; end;
@ -1736,15 +1740,16 @@ end;
procedure TBasicPointSeries.UpdateMargins( procedure TBasicPointSeries.UpdateMargins(
ADrawer: IChartDrawer; var AMargins: TRect); ADrawer: IChartDrawer; var AMargins: TRect);
var var
i, j: Integer; //, distX, distY: Integer; i, j: Integer;
labelText: String; labelText: String;
dir: TLabelDirection; dir: TLabelDirection;
ap: TDoublePoint;
gp: TDoublePoint; gp: TDoublePoint;
p: TPoint; p: TPoint;
dist: TPoint; dist: TPoint;
prev, curr, zero: Integer; prev, curr, zero: Integer;
r, rExtent: TRect; r, rExtent: TRect;
y: Double; y, ysum: Double;
begin begin
if not Marks.IsMarkLabelsVisible or not Marks.AutoMargins then exit; if not Marks.IsMarkLabelsVisible or not Marks.AutoMargins then exit;
@ -1753,6 +1758,7 @@ begin
rExtent.BottomRight := FChart.GraphToImage(DoublePoint(b.x, a.y)); rExtent.BottomRight := FChart.GraphToImage(DoublePoint(b.x, a.y));
end; end;
r := rExtent; r := rExtent;
with FChart do with FChart do
zero := IfThen(IsRotated, XGraphToImage(GetZeroLevel), YGraphToImage(GetZeroLevel)); zero := IfThen(IsRotated, XGraphToImage(GetZeroLevel), YGraphToImage(GetZeroLevel));
@ -1760,6 +1766,10 @@ begin
labelText := FormattedMark(i); labelText := FormattedMark(i);
if labelText = '' then Continue; if labelText = '' then Continue;
ap := Source[i]^.Point;
if IsNaN(ap.X) then
Continue;
with Marks.MeasureLabel(ADrawer, labelText) do with Marks.MeasureLabel(ADrawer, labelText) do
if IsRotated then if IsRotated then
dist := Point(cx + Marks.Distance - IfThen(Marks.DistanceToCenter, cx div 2), cy div 2) dist := Point(cx + Marks.Distance - IfThen(Marks.DistanceToCenter, cx div 2), cy div 2)
@ -1767,15 +1777,21 @@ begin
dist := Point(cx div 2, cy + Marks.Distance - IfThen(Marks.DistanceToCenter, cy div 2)); dist := Point(cx div 2, cy + Marks.Distance - IfThen(Marks.DistanceToCenter, cy div 2));
prev := zero; prev := zero;
gp := GetLabelDataPoint(i); y := ap.Y;
ysum := y;
for j := 0 to Source.YCount - 1 do begin for j := 0 to Source.YCount - 1 do begin
if Stacked and (j > 0) then begin gp := GetLabelDataPoint(i, j);
if (j > 0) then begin
y := Source[i]^.YList[j-1]; y := Source[i]^.YList[j-1];
if IsRotated then if Stacked and not IsNaN(y) then begin
gp.x += AxisToGraphX(y) if IsNaN(ysum) then ysum := y else ysum := ysum + y;
else y := ysum;
gp.Y += AxisToGraphY(y); end;
end; end;
if IsRotated then
gp.x := AxisToGraphX(y)
else
gp.y := AxisToGraphY(y);
p := FChart.GraphToImage(gp); p := FChart.GraphToImage(gp);
curr := TPointBoolArr(p)[not IsRotated]; curr := TPointBoolArr(p)[not IsRotated];
if MarkPositions = lmpInsideCenter then begin if MarkPositions = lmpInsideCenter then begin
@ -1785,33 +1801,17 @@ begin
p.y := (curr + prev) div 2; p.y := (curr + prev) div 2;
end; end;
gp := FChart.ImageToGraph(p); gp := FChart.ImageToGraph(p);
dir := GetlabelDirection(TDoublePointBoolArr(gp)[not IsRotated]); if FChart.IsPointInViewPort(gp) then begin
case dir of dir := GetlabelDirection(TDoublePointBoolArr(gp)[not IsRotated]);
ldLeft : UpdateMinMax(p.X - dist.X, r.Left, r.Right); case dir of
ldRight : UpdateMinMax(p.X + dist.X, r.Left, r.Right); ldLeft : UpdateMinMax(p.X - dist.X, r.Left, r.Right);
ldTop : UpdateMinMax(p.Y - dist.Y, r.Top, r.Bottom); ldRight : UpdateMinMax(p.X + dist.X, r.Left, r.Right);
ldBottom : UpdateMinMax(p.Y + dist.Y, r.Top, r.Bottom); ldTop : UpdateMinMax(p.Y - dist.Y, r.Top, r.Bottom);
ldBottom : UpdateMinMax(p.Y + dist.Y, r.Top, r.Bottom);
end;
end; end;
prev := curr; prev := curr;
end; end;
{
gp := GetGraphPoint(i);
p := FChart.GraphToImage(gp);
UpdateMinMax(p.X, rExtent.Left, rExtent.Right);
UpdateMinMax(p.Y, rExtent.Top, rExtent.Bottom);
if MarkPositions = lmpInsideCenter then
p.y := (p.y + FChart.YGraphToImage(zero)) div 2;
gp := FChart.ImageToGraph(p);
dir := GetLabelDirection(TDoublePointBoolArr(gp)[not IsRotated]);
case dir of
ldLeft : UpdateMinMax(p.X - distX, r.Left, r.Right);
ldRight : UpdateMinMax(p.X + distX, r.Left, r.Right);
ldTop : UpdateMinMax(p.Y - distY, r.Top, r.Bottom);
ldBottom : UpdateMinMax(p.Y + distY, r.Top, r.Bottom);
end;
}
if IsRotated then begin if IsRotated then begin
UpdateMinMax(p.Y - dist.Y, r.Top, r.Bottom); UpdateMinMax(p.Y - dist.Y, r.Top, r.Bottom);
UpdateMinMax(p.Y + dist.Y, r.Top, r.Bottom); UpdateMinMax(p.Y + dist.Y, r.Top, r.Bottom);

View File

@ -63,7 +63,7 @@ type
procedure SetSeriesColor(AValue: TColor); procedure SetSeriesColor(AValue: TColor);
procedure SetZeroLevel(AValue: Double); procedure SetZeroLevel(AValue: Double);
strict protected strict protected
function GetLabelDataPoint(AIndex: Integer): TDoublePoint; override; function GetLabelDataPoint(AIndex, AYIndex: Integer): TDoublePoint; override;
function ToolTargetDistance(const AParams: TNearestPointParams; function ToolTargetDistance(const AParams: TNearestPointParams;
AGraphPt: TDoublePoint; APointIdx, AXIdx, AYIdx: Integer): Integer; override; AGraphPt: TDoublePoint; APointIdx, AXIdx, AYIdx: Integer): Integer; override;
protected protected
@ -1136,7 +1136,6 @@ var
Exchange(a.X, a.Y); Exchange(a.X, a.Y);
Exchange(b.X, b.Y); Exchange(b.X, b.Y);
end; end;
if not RectIntersectsRect(graphBar, ext2) then exit; if not RectIntersectsRect(graphBar, ext2) then exit;
with imageBar do begin with imageBar do begin
@ -1177,20 +1176,20 @@ begin
heights[1] := NumberOr(p.Y, ZeroLevel); heights[1] := NumberOr(p.Y, ZeroLevel);
for stackIndex := 1 to Source.YCount - 1 do begin for stackIndex := 1 to Source.YCount - 1 do begin
y := Source[pointIndex]^.YList[stackIndex - 1]; y := Source[pointIndex]^.YList[stackIndex - 1];
if not IsNan(y) then if not IsNaN(y) then
heights[stackIndex + 1] := heights[stackIndex] + y; heights[stackIndex + 1] := heights[stackIndex] + y;
end; end;
for stackIndex := 0 to High(heights) do for stackIndex := 0 to High(heights) do
heights[stackindex] := AxisToGraphY(heights[stackindex]); heights[stackindex] := AxisToGraphY(heights[stackindex]); // wp: what if rotated?
for stackIndex := 0 to Source.YCount - 1 do for stackIndex := 0 to Source.YCount - 1 do
BuildBar(p.X, heights[stackindex], heights[stackIndex+1]); BuildBar(p.X, heights[stackindex], heights[stackIndex+1]);
end else begin end else begin
for stackIndex := 0 to Source.YCount - 1 do begin for stackIndex := 0 to Source.YCount - 1 do begin
y := Source[pointIndex]^.GetY(stackIndex); y := Source[pointIndex]^.GetY(stackIndex);
if not IsNaN(y) then if not IsNaN(y) then
heights[stackIndex + 1] := y heights[stackIndex + 1] := AxisToGraphY(y)
else else
heights[stackIndex + 1] := 0; continue;
end; end;
p.X -= w; p.X -= w;
w := w / High(heights); w := w / High(heights);
@ -1214,7 +1213,7 @@ begin
if IsEmpty then exit; if IsEmpty then exit;
if BarWidthStyle = bwPercentMin then if BarWidthStyle = bwPercentMin then
UpdateMinXRange; UpdateMinXRange;
UpdateMinMax(ZeroLevel, Result.a.Y, Result.b.Y); // UpdateMinMax(ZeroLevel, Result.a.Y, Result.b.Y);
// Show first and last bars fully. // Show first and last bars fully.
i := 0; i := 0;
x := NearestXNumber(i, +1); // --> x is in graph units x := NearestXNumber(i, +1); // --> x is in graph units
@ -1245,11 +1244,11 @@ begin
Result := Abs(f(2 * w) - f(0)); Result := Abs(f(2 * w) - f(0));
end; end;
function TBarSeries.GetLabelDataPoint(AIndex: Integer): TDoublePoint; function TBarSeries.GetLabelDataPoint(AIndex, AYIndex: Integer): TDoublePoint;
var var
ofs, w: Double; ofs, w: Double;
begin begin
Result := inherited GetLabelDataPoint(AIndex); Result := inherited GetLabelDataPoint(AIndex, AYIndex);
BarOffsetWidth(TDoublePointBoolArr(Result)[IsRotated], AIndex, ofs, w); BarOffsetWidth(TDoublePointBoolArr(Result)[IsRotated], AIndex, ofs, w);
TDoublePointBoolArr(Result)[IsRotated] += ofs; TDoublePointBoolArr(Result)[IsRotated] += ofs;
end; end;