mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-17 17:19:22 +02:00
TAChart: Rewrite PieSeries.Draw method. Greatly simplify code. Fix multiple issues with scaling.
patch by: Alexander Klenin part 1 of issue #13196 git-svn-id: trunk@18729 -
This commit is contained in:
parent
efe7beb3f2
commit
c42b11a253
@ -190,8 +190,6 @@ type
|
|||||||
TPieSeries = class(TChartSeries)
|
TPieSeries = class(TChartSeries)
|
||||||
private
|
private
|
||||||
ColorIndex: Integer;
|
ColorIndex: Integer;
|
||||||
FPiePen: TPen;
|
|
||||||
procedure SetPiePen(Value: TPen);
|
|
||||||
protected
|
protected
|
||||||
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
|
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
|
||||||
function GetLegendCount: Integer; override;
|
function GetLegendCount: Integer; override;
|
||||||
@ -201,14 +199,12 @@ type
|
|||||||
procedure SetSeriesColor(const AValue: TColor); override;
|
procedure SetSeriesColor(const AValue: TColor); override;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
|
||||||
|
|
||||||
{from parent}
|
{from parent}
|
||||||
procedure Draw(ACanvas: TCanvas); override;
|
procedure Draw(ACanvas: TCanvas); override;
|
||||||
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; override;
|
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; override;
|
||||||
function AddPie(Value: Double; Text: String; Color: TColor): Longint;
|
function AddPie(Value: Double; Text: String; Color: TColor): Longint;
|
||||||
published
|
published
|
||||||
property PiePen: TPen read FPiePen write SetPiePen;
|
|
||||||
property Title;
|
property Title;
|
||||||
property Active;
|
property Active;
|
||||||
end;
|
end;
|
||||||
@ -345,7 +341,7 @@ type
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
math, types;
|
GraphMath, Math, Types;
|
||||||
|
|
||||||
constructor TChartSeries.Create(AOwner: TComponent);
|
constructor TChartSeries.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
@ -1291,34 +1287,11 @@ begin
|
|||||||
Result := FBarBrush.Color;
|
Result := FBarBrush.Color;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TPieSeries.Create(AOwner: TComponent);
|
{ TPieSeries }
|
||||||
|
|
||||||
|
function TPieSeries.AddPie(Value: Double; Text: String; Color: TColor): Longint;
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
Result := AddXY(getXMinVal + 1, Value, Text, Color);
|
||||||
|
|
||||||
ColorIndex := 1;
|
|
||||||
|
|
||||||
FPiePen := TPen.Create;
|
|
||||||
FPiePen.OnChange := StyleChanged;
|
|
||||||
FPiePen.Mode := pmCopy;
|
|
||||||
FPiePen.Style := psSolid;
|
|
||||||
FPiePen.Width := 1;
|
|
||||||
FPiePen.Color := clBlack;
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TPieSeries.Destroy;
|
|
||||||
begin
|
|
||||||
FPiePen.Free;
|
|
||||||
inherited Destroy;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TPieSeries.SetPiePen(Value: TPen);
|
|
||||||
begin
|
|
||||||
FPiePen.Assign(Value);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TPieSeries.SetSeriesColor(const AValue: TColor);
|
|
||||||
begin
|
|
||||||
// SeriesColor is meaningless for PieSeries
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPieSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
|
function TPieSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
|
||||||
@ -1339,129 +1312,94 @@ begin
|
|||||||
ParentChart.BottomAxis.Visible := false;
|
ParentChart.BottomAxis.Visible := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPieSeries.AddPie(Value: Double; Text: String; Color: TColor): Longint;
|
constructor TPieSeries.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
Result := AddXY(getXMinVal + 1, Value, Text, Color);
|
inherited Create(AOwner);
|
||||||
end;
|
ColorIndex := 1;
|
||||||
|
|
||||||
procedure conv_angle(nOrigoX, nOrigoY, nLen: Integer; Angle: Double; out PX, PY: Integer);
|
|
||||||
begin
|
|
||||||
PX := nOrigoX + round(Cos(Angle) * nLen);
|
|
||||||
PY := nOrigoY - round(Sin(Angle) * nLen);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPieSeries.Draw(ACanvas: TCanvas);
|
procedure TPieSeries.Draw(ACanvas: TCanvas);
|
||||||
var
|
var
|
||||||
nOrigoX, nOrigoY: LongInt;
|
i, radius: Integer;
|
||||||
nLen: Integer;
|
yTotal, prevAngle, angleStep: Double;
|
||||||
n100Sum: Double;
|
|
||||||
i, AX, AY, BX, BY: Integer;
|
|
||||||
curangle, prevangle, midleangle, prop: Double;
|
|
||||||
maxtextw: Integer;
|
|
||||||
CircleRect: TRect;
|
|
||||||
markrect: TRect;
|
|
||||||
W: Integer;
|
|
||||||
graphCoord: PChartCoord;
|
graphCoord: PChartCoord;
|
||||||
MarkTxtWidth: Integer;
|
labelWidths, labelHeights: TIntegerDynArray;
|
||||||
SPACE_InMarks: integer;
|
labelTexts: TStringDynArray;
|
||||||
labelText: String;
|
a, b, center: TPoint;
|
||||||
const
|
const
|
||||||
TAGSMargin = 20;
|
MARGIN = 8;
|
||||||
|
MARKS_DIST = 32;
|
||||||
MarkYMargin = 2;
|
MarkYMargin = 2;
|
||||||
MarkXMargin = 4;
|
MarkXMargin = 4;
|
||||||
begin
|
begin
|
||||||
SPACE_InMarks := 40;
|
|
||||||
//no elements to draw
|
|
||||||
if FCoordList.Count = 0 then exit;
|
if FCoordList.Count = 0 then exit;
|
||||||
|
|
||||||
//center the ParentChart on the canvas
|
yTotal := 0;
|
||||||
if ParentChart.XImageMax - ParentChart.XImageMin > ParentChart.YImageMin - ParentChart.YImageMax then begin
|
for i := 0 to FCoordList.Count - 1 do
|
||||||
W := ParentChart.YImageMin - ParentChart.YImageMax;
|
yTotal += PChartCoord(FCoordList[i])^.y;
|
||||||
nOrigoX := (ParentChart.XImageMax - ParentChart.XImageMin) div 2 + ParentChart.XImageMin;
|
|
||||||
nOrigoY := Round((W - 1.01) / 2) + ParentChart.YImageMax;
|
SetLength(labelWidths, FCoordList.Count);
|
||||||
end
|
SetLength(labelHeights, FCoordList.Count);
|
||||||
else begin
|
SetLength(labelTexts, FCoordList.Count);
|
||||||
W := ParentChart.XImageMax - ParentChart.XImageMin;
|
for i := 0 to FCoordList.Count - 1 do
|
||||||
nOrigoX := Round((W - 1.01) / 2) + ParentChart.XImageMin;
|
with PChartCoord(FCoordList[i])^ do begin
|
||||||
nOrigoY := (ParentChart.YImageMin - ParentChart.YImageMax) div 2 + ParentChart.YImageMax;
|
case MarksStyle of
|
||||||
|
smsLabel:
|
||||||
|
labelTexts[i] := Text;
|
||||||
|
smsLabelPercent:
|
||||||
|
labelTexts[i] := Text + Format(' %1.3g%%', [y / yTotal * 100]);
|
||||||
|
end;
|
||||||
|
with ACanvas.TextExtent(labelTexts[i]) do begin
|
||||||
|
labelWidths[i] := cx;
|
||||||
|
labelHeights[i] := cy;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
with ParentChart do begin
|
||||||
|
center.x := (XImageMin + XImageMax) div 2;
|
||||||
|
center.y := (YImageMin + YImageMax) div 2;
|
||||||
|
// Reserve space for labels.
|
||||||
|
radius := Min(
|
||||||
|
XImageMax - center.x - MaxIntValue(labelWidths),
|
||||||
|
YImageMin - center.y - MaxIntValue(labelHeights));
|
||||||
end;
|
end;
|
||||||
|
radius := Max(radius - MARKS_DIST - MARGIN, 0);
|
||||||
|
|
||||||
prevangle := 0;
|
prevAngle := 0;
|
||||||
maxtextw := 0;
|
|
||||||
n100Sum := 0;
|
|
||||||
|
|
||||||
for i := 0 to FCoordList.Count - 1 do begin
|
for i := 0 to FCoordList.Count - 1 do begin
|
||||||
|
// if y < 0 then y := -y;
|
||||||
|
// if y = 0 then y := 0.1; // just to simulate tchart when y=0
|
||||||
|
|
||||||
graphCoord := FCoordList[i];
|
graphCoord := FCoordList[i];
|
||||||
n100Sum := n100Sum + graphCoord^.y;
|
angleStep := graphCoord^.y / yTotal * 360 * 16;
|
||||||
if ACanvas.TextWidth(graphCoord^.Text) > maxtextw then
|
|
||||||
maxtextw := ACanvas.TextWidth(graphCoord^.Text);
|
|
||||||
end;
|
|
||||||
|
|
||||||
//we can only draw if enought space for text is saved
|
|
||||||
//so we remove maxtextw and MarkXMargin
|
|
||||||
nLen := Round((W - maxtextw * 2) / 2);
|
|
||||||
CircleRect.Left := nOrigoX - nLen - TAGSMargin;
|
|
||||||
CircleRect.Top := nOrigoY - nLen - TAGSMargin;
|
|
||||||
CircleRect.Right := nOrigoX + nlen + TAGSMargin;
|
|
||||||
CircleRect.Bottom := nOrigoY + nlen + TAGSMargin;
|
|
||||||
|
|
||||||
// ACanvas.Rectangle(CircleRect.Left, CircleRect.Top, CircleRect.Right, CircleRect.Bottom);
|
|
||||||
|
|
||||||
for i := 0 to FCoordList.Count - 1 do begin
|
|
||||||
graphCoord := FCoordList[i];
|
|
||||||
if graphCoord^.y < 0 then graphCoord^.y := -graphCoord^.y;
|
|
||||||
//if graphCoord^.y = 0 then graphCoord^.y := 0.1; //just to simulate tchart when y=0
|
|
||||||
|
|
||||||
conv_angle(nOrigoX, nOrigoY, nLen, prevangle, AX, AY);
|
|
||||||
prop := graphCoord^.y / n100Sum;
|
|
||||||
curangle := prop * (2 * pi);
|
|
||||||
conv_angle(nOrigoX, nOrigoY, nLen, prevangle+curangle, BX, BY);
|
|
||||||
|
|
||||||
ACanvas.Brush.Color := graphCoord^.Color;
|
ACanvas.Brush.Color := graphCoord^.Color;
|
||||||
ACanvas.Pie(
|
|
||||||
CircleRect.Left, CircleRect.Top, CircleRect.Right, CircleRect.Bottom,
|
|
||||||
AX, AY, BX, BY);
|
|
||||||
|
|
||||||
if nLen < SPACE_InMarks then
|
ACanvas.RadialPie(
|
||||||
SPACE_InMarks := nLen;
|
center.x - radius, center.y - radius,
|
||||||
|
center.x + radius, center.y + radius, round(prevAngle), round(angleStep));
|
||||||
|
|
||||||
//marks
|
a := LineEndPoint(center, prevAngle + angleStep / 2, radius);
|
||||||
midleangle := prevangle + curangle / 2;
|
b := LineEndPoint(center, prevAngle + angleStep / 2, radius + MARKS_DIST);
|
||||||
conv_angle(nOrigoX, nOrigoY, nLen + SPACE_InMarks, midleangle, BX, BY);
|
|
||||||
conv_angle(nOrigoX, nOrigoY, nLen, midleangle, aX, aY);
|
|
||||||
|
|
||||||
|
// line from mark to pie
|
||||||
ACanvas.Pen.Color := clWhite;
|
ACanvas.Pen.Color := clWhite;
|
||||||
ACanvas.MoveTo(ax, ay);
|
ACanvas.MoveTo(a.x, a.y);
|
||||||
ACanvas.LineTo(bx, by);
|
ACanvas.LineTo(b.x, b.y);
|
||||||
ACanvas.Pen.Color := clBlack;
|
ACanvas.Pen.Color := clBlack;
|
||||||
|
|
||||||
case MarksStyle of
|
if b.x < center.x then
|
||||||
smsLabel:
|
b.x -= labelWidths[i];
|
||||||
labelText := graphCoord^.Text;
|
if b.y < center.y then
|
||||||
smsLabelPercent:
|
b.y -= labelHeights[i];
|
||||||
labelText := graphCoord^.Text + Format(' %1.3g%%', [prop * 100]);
|
|
||||||
end;
|
|
||||||
MarkTxtWidth := ACanvas.TextWidth(labelText);
|
|
||||||
|
|
||||||
//line from mark to pie
|
|
||||||
if Bx < nOrigoX then
|
|
||||||
markrect.Left := BX - MarkTxtWidth - MarkXMargin
|
|
||||||
else
|
|
||||||
markrect.Left := BX - MarkXMargin;
|
|
||||||
markrect.Right := markrect.Left + MarkTxtWidth + MarkXMargin * 2;
|
|
||||||
|
|
||||||
markrect.Top := BY - MarkYMargin;
|
|
||||||
markrect.Bottom := BY + ACanvas.TextHeight(graphCoord^.Text) + MarkYMargin;
|
|
||||||
|
|
||||||
ACanvas.Brush.Color := clYellow;
|
ACanvas.Brush.Color := clYellow;
|
||||||
ACanvas.Rectangle(markrect);
|
ACanvas.Rectangle(
|
||||||
|
b.x - MarkXMargin, b.y - MarkYMargin,
|
||||||
|
b.x + labelWidths[i] + MarkXMargin, b.y + labelHeights[i] + MarkYMargin);
|
||||||
|
ACanvas.TextOut(b.x, b.y, labelTexts[i]);
|
||||||
|
|
||||||
if Bx < nOrigoX then BX := BX - MarkTxtWidth;
|
prevAngle += angleStep;
|
||||||
ACanvas.Brush.Color := clYellow;
|
end;
|
||||||
ACanvas.TextOut(BX, BY, labelText);
|
|
||||||
|
|
||||||
prevangle := prevangle + curangle;
|
|
||||||
end; // for
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPieSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
|
procedure TPieSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
|
||||||
@ -1507,6 +1445,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TPieSeries.SetSeriesColor(const AValue: TColor);
|
||||||
|
begin
|
||||||
|
// SeriesColor is meaningless for PieSeries
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TAreaSeries }
|
||||||
|
|
||||||
constructor TAreaSeries.Create(AOwner: TComponent);
|
constructor TAreaSeries.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
|
Loading…
Reference in New Issue
Block a user