mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-17 23:49:34 +02:00
TAChart: Add new properties Orientation and ViewAngle to TPieSeries. Update demo.
git-svn-id: trunk@60728 -
This commit is contained in:
parent
4259c682c9
commit
50b7e5b2a4
components/tachart
@ -2,10 +2,10 @@ object Form1: TForm1
|
||||
Left = 443
|
||||
Height = 559
|
||||
Top = 340
|
||||
Width = 580
|
||||
Width = 659
|
||||
Caption = 'Form1'
|
||||
ClientHeight = 559
|
||||
ClientWidth = 580
|
||||
ClientWidth = 659
|
||||
OnCreate = FormCreate
|
||||
Position = poScreenCenter
|
||||
LCLVersion = '2.1.0.0'
|
||||
@ -13,7 +13,7 @@ object Form1: TForm1
|
||||
Left = 0
|
||||
Height = 559
|
||||
Top = 0
|
||||
Width = 580
|
||||
Width = 659
|
||||
ActivePage = tsPie
|
||||
Align = alClient
|
||||
TabIndex = 0
|
||||
@ -21,12 +21,12 @@ object Form1: TForm1
|
||||
object tsPie: TTabSheet
|
||||
Caption = 'Pie'
|
||||
ClientHeight = 531
|
||||
ClientWidth = 572
|
||||
ClientWidth = 651
|
||||
object ChartPie: TChart
|
||||
Left = 0
|
||||
Height = 407
|
||||
Top = 124
|
||||
Width = 572
|
||||
Width = 651
|
||||
AxisList = <
|
||||
item
|
||||
Visible = False
|
||||
@ -71,14 +71,14 @@ object Form1: TForm1
|
||||
Left = 0
|
||||
Height = 124
|
||||
Top = 0
|
||||
Width = 572
|
||||
Width = 651
|
||||
Align = alTop
|
||||
Alignment = taLeftJustify
|
||||
Anchors = [akTop, akRight]
|
||||
AutoSize = True
|
||||
BevelOuter = bvNone
|
||||
ClientHeight = 124
|
||||
ClientWidth = 572
|
||||
ClientWidth = 651
|
||||
TabOrder = 1
|
||||
object seWords: TSpinEdit
|
||||
AnchorSideLeft.Control = lblWords
|
||||
@ -400,6 +400,64 @@ object Form1: TForm1
|
||||
OnChange = seStartAngleChange
|
||||
TabOrder = 12
|
||||
end
|
||||
object seViewAngle: TSpinEdit
|
||||
AnchorSideLeft.Control = lblViewAngle
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = seDistance
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 549
|
||||
Height = 23
|
||||
Top = 64
|
||||
Width = 62
|
||||
BorderSpacing.Right = 6
|
||||
Enabled = False
|
||||
MaxValue = 89
|
||||
OnChange = seViewAngleChange
|
||||
TabOrder = 13
|
||||
Value = 60
|
||||
end
|
||||
object lblViewAngle: TLabel
|
||||
AnchorSideLeft.Control = seDepth
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = seViewAngle
|
||||
AnchorSideTop.Side = asrCenter
|
||||
Left = 484
|
||||
Height = 15
|
||||
Top = 68
|
||||
Width = 57
|
||||
BorderSpacing.Left = 12
|
||||
BorderSpacing.Right = 8
|
||||
Caption = 'View angle'
|
||||
Enabled = False
|
||||
ParentColor = False
|
||||
end
|
||||
object cmbOrientation: TComboBox
|
||||
AnchorSideLeft.Control = lblViewAngle
|
||||
AnchorSideTop.Control = cbMarkAttachment
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideRight.Control = seViewAngle
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 484
|
||||
Height = 23
|
||||
Top = 93
|
||||
Width = 127
|
||||
Anchors = [akTop, akLeft, akRight]
|
||||
AutoSize = False
|
||||
BorderSpacing.Bottom = 8
|
||||
Enabled = False
|
||||
ItemHeight = 15
|
||||
ItemIndex = 0
|
||||
Items.Strings = (
|
||||
'normal'
|
||||
'horizontal'
|
||||
'vertical'
|
||||
)
|
||||
OnChange = cmbOrientationChange
|
||||
Style = csDropDownList
|
||||
TabOrder = 14
|
||||
Text = 'normal'
|
||||
end
|
||||
end
|
||||
end
|
||||
object tsPolar: TTabSheet
|
||||
|
@ -15,6 +15,7 @@ type
|
||||
|
||||
TForm1 = class(TForm)
|
||||
cbMarkAttachment: TComboBox;
|
||||
cmbOrientation: TComboBox;
|
||||
ChartPolar: TChart;
|
||||
ChartPolarSeries1: TPolarSeries;
|
||||
ChartPolarSeries2: TPolarSeries;
|
||||
@ -29,9 +30,11 @@ type
|
||||
Cb3D: TCheckBox;
|
||||
cbShowLabels: TCheckBox;
|
||||
cbMarkPositionsCentered: TCheckBox;
|
||||
lblViewAngle: TLabel;
|
||||
lblDistance: TLabel;
|
||||
lblStartAngle: TLabel;
|
||||
seDepth: TSpinEdit;
|
||||
seViewAngle: TSpinEdit;
|
||||
seDepthBrightnessDelta: TSpinEdit;
|
||||
lblInnerRadius: TLabel;
|
||||
lblDepth: TLabel;
|
||||
@ -63,6 +66,7 @@ type
|
||||
Shift: TShiftState; X, Y: Integer);
|
||||
procedure cbShowPointsChange(Sender: TObject);
|
||||
procedure Cb3DChange(Sender: TObject);
|
||||
procedure cmbOrientationChange(Sender: TObject);
|
||||
procedure seDepthBrightnessDeltaChange(Sender: TObject);
|
||||
procedure seDepthChange(Sender: TObject);
|
||||
procedure seDistanceChange(Sender: TObject);
|
||||
@ -70,6 +74,7 @@ type
|
||||
procedure FormCreate(Sender: TObject);
|
||||
procedure sbTransparencyChange(Sender: TObject);
|
||||
procedure seStartAngleChange(Sender: TObject);
|
||||
procedure seViewAngleChange(Sender: TObject);
|
||||
procedure seWordsChange(Sender: TObject);
|
||||
procedure seLabelAngleChange(Sender: TObject);
|
||||
end;
|
||||
@ -152,6 +157,14 @@ begin
|
||||
lblDepth.Enabled := cb3D.Checked;
|
||||
seDepthBrightnessDelta.Enabled := cb3D.Checked;
|
||||
lblDepthBrightnessDelta.Enabled := cb3D.Checked;
|
||||
lblViewAngle.Enabled := cb3D.Checked;
|
||||
seViewAngle.Enabled := cb3D.Checked;
|
||||
cmbOrientation.Enabled := cb3D.Checked;
|
||||
end;
|
||||
|
||||
procedure TForm1.cmbOrientationChange(Sender: TObject);
|
||||
begin
|
||||
ChartPiePieSeries1.Orientation := TPieOrientation(cmbOrientation.ItemIndex);
|
||||
end;
|
||||
|
||||
procedure TForm1.seDepthBrightnessDeltaChange(Sender: TObject);
|
||||
@ -202,6 +215,11 @@ begin
|
||||
ChartPiePieSeries1.StartAngle := seStartAngle.Value;
|
||||
end;
|
||||
|
||||
procedure TForm1.seViewAngleChange(Sender: TObject);
|
||||
begin
|
||||
ChartPiePieSeries1.ViewAngle := seViewAngle.Value;
|
||||
end;
|
||||
|
||||
procedure TForm1.seLabelAngleChange(Sender: TObject);
|
||||
begin
|
||||
ChartPiePieSeries1.Marks.LabelFont.Orientation := seLabelAngle.Value * 10;
|
||||
|
@ -62,13 +62,16 @@ type
|
||||
{ TCustomPieSeries }
|
||||
|
||||
TSliceArray = array of TPieSlice;
|
||||
TPieOrientation = (poNormal, poHorizontal, poVertical);
|
||||
|
||||
TCustomPieSeries = class(TChartSeries)
|
||||
private
|
||||
FAspectRatio: Double;
|
||||
FCenter: TPoint;
|
||||
FMarkDistancePercent: Boolean;
|
||||
FMarkPositionCentered: Boolean;
|
||||
FMarkPositions: TPieMarkPositions;
|
||||
FOrientation: TPieOrientation;
|
||||
FRadius: Integer;
|
||||
FInnerRadiusPercent: Integer;
|
||||
FSlices: array of TPieSlice;
|
||||
@ -78,6 +81,8 @@ type
|
||||
FExploded: Boolean;
|
||||
FFixedRadius: TChartDistance;
|
||||
FRotateLabels: Boolean;
|
||||
function FixAspectRatio(P: TPoint): TPoint;
|
||||
function GetViewAngle: Integer;
|
||||
procedure Measure(ADrawer: IChartDrawer);
|
||||
procedure SetEdgePen(AValue: TPen);
|
||||
procedure SetExploded(AValue: Boolean);
|
||||
@ -86,9 +91,12 @@ type
|
||||
procedure SetMarkDistancePercent(AValue: Boolean);
|
||||
procedure SetMarkPositionCentered(AValue: Boolean);
|
||||
procedure SetMarkPositions(AValue: TPieMarkPositions);
|
||||
procedure SetOrientation(AValue: TPieOrientation);
|
||||
procedure SetRotateLabels(AValue: Boolean);
|
||||
procedure SetStartAngle(AValue: Integer);
|
||||
procedure SetViewAngle(AValue: Integer);
|
||||
function SliceColor(AIndex: Integer): TColor;
|
||||
function SliceExploded(ASlice: TPieSlice): Boolean; inline;
|
||||
function TryRadius(ADrawer: IChartDrawer): TRect;
|
||||
protected
|
||||
function CalcInnerRadius: Integer; inline;
|
||||
@ -98,8 +106,13 @@ type
|
||||
read FInnerRadiusPercent write SetInnerRadiusPercent default 0;
|
||||
property MarkPositionCentered: Boolean
|
||||
read FMarkPositionCentered write SetMarkPositionCentered default false;
|
||||
property Orientation: TPieOrientation
|
||||
read FOrientation write SetOrientation default poNormal;
|
||||
property Radius: Integer read FRadius;
|
||||
property StartAngle: Integer read FStartAngle write SetStartAngle default 0;
|
||||
property StartAngle: Integer
|
||||
read FStartAngle write SetStartAngle default 0;
|
||||
property ViewAngle: Integer
|
||||
read GetViewAngle write SetViewAngle default 60;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
@ -283,11 +296,13 @@ procedure TCustomPieSeries.Assign(ASource: TPersistent);
|
||||
begin
|
||||
if ASource is TCustomPieSeries then
|
||||
with TCustomPieSeries(ASource) do begin
|
||||
Self.FAspectRatio := FAspectRatio;
|
||||
Self.FExploded := FExploded;
|
||||
Self.FFixedRadius := FFixedRadius;
|
||||
Self.FInnerRadiusPercent := FInnerRadiusPercent;
|
||||
Self.FMarkDistancePercent := FMarkDistancePercent;
|
||||
Self.FMarkPositionCentered := FMarkPositionCentered;
|
||||
Self.FOrientation := FOrientation;
|
||||
Self.FRotateLabels := FRotateLabels;
|
||||
Self.FStartAngle := FStartAngle;
|
||||
end;
|
||||
@ -302,6 +317,7 @@ end;
|
||||
constructor TCustomPieSeries.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
ViewAngle := 60;
|
||||
FEdgePen := TPen.Create;
|
||||
FEdgePen.OnChange := @StyleChanged;
|
||||
|
||||
@ -345,16 +361,18 @@ var
|
||||
end;
|
||||
end;
|
||||
|
||||
function SliceExploded(ASlice: TPieSlice): Boolean;
|
||||
begin
|
||||
Result := ASlice.FBase <> FCenter;
|
||||
end;
|
||||
|
||||
function StartEdgeVisible(ASlice: TPieSlice): Boolean;
|
||||
var
|
||||
prev: TPieSlice;
|
||||
begin
|
||||
Result := InRange(ASlice.FPrevAngle, PI_1_4, PI_5_4);
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
Result := InRange(ASlice.FPrevAngle, PI_1_4, PI_5_4);
|
||||
poHorizontal:
|
||||
Result := not InRange(ASlice.FPrevAngle, PI_1_2, PI_3_2);
|
||||
poVertical:
|
||||
Result := InRange(ASlice.FPrevAngle, 0, pi);
|
||||
end;
|
||||
if Result then begin
|
||||
prev := PrevSlice(ASlice);
|
||||
Result := SliceExploded(ASlice) or SliceExploded(prev) or not prev.FVisible;
|
||||
@ -365,7 +383,14 @@ var
|
||||
var
|
||||
next: TPieSlice;
|
||||
begin
|
||||
Result := not InRange(ASlice.FNextAngle, PI_1_4, PI_5_4);
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
Result := not InRange(ASlice.FNextAngle, PI_1_4, PI_5_4);
|
||||
poHorizontal:
|
||||
Result := InRange(ASlice.FNextAngle, PI_1_2, PI_3_2);
|
||||
poVertical:
|
||||
Result := InRange(ASlice.FNextAngle, pi, TWO_PI);
|
||||
end;
|
||||
if Result then begin
|
||||
next := NextSlice(ASlice);
|
||||
Result := SliceExploded(ASlice) or SliceExploded(next) or not next.FVisible;
|
||||
@ -381,6 +406,7 @@ var
|
||||
clr: TColor;
|
||||
r: Integer;
|
||||
isVisible: Boolean;
|
||||
ofs: TPoint;
|
||||
begin
|
||||
if AInside and (FInnerRadiusPercent = 0) then
|
||||
exit;
|
||||
@ -389,29 +415,84 @@ var
|
||||
isVisible := true
|
||||
else
|
||||
if AInside then
|
||||
isVisible := InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4) or InRange(ASlice.FNextAngle, PI_3_4, PI_7_4)
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
isVisible := InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4) or
|
||||
InRange(ASlice.FNextAngle, PI_3_4, PI_7_4);
|
||||
poHorizontal:
|
||||
isVisible := InRange(ASlice.FPrevAngle, 0, pi) or
|
||||
InRange(ASlice.FNextAngle, 0, pi);
|
||||
poVertical:
|
||||
isVisible := InRange(ASlice.FPrevAngle, PI_1_2, PI_3_2) or
|
||||
InRange(ASlice.FNextAngle, PI_1_2, PI_3_2);
|
||||
end
|
||||
else
|
||||
isVisible := (ASlice.FPrevAngle >= PI_7_4) or (ASlice.FPrevAngle <= PI_3_4) or
|
||||
(ASlice.FNextAngle >= PI_7_4) or (ASlice.FNextAngle <= PI_3_4);
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
isVisible := (ASlice.FPrevAngle >= PI_7_4) or (ASlice.FPrevAngle <= PI_3_4) or
|
||||
(ASlice.FNextAngle >= PI_7_4) or (ASlice.FNextAngle <= PI_3_4);
|
||||
poHorizontal:
|
||||
isVisible := InRange(ASlice.FPrevAngle, pi, TWO_PI) or
|
||||
InRange(ASlice.FNextAngle, pi, TWO_PI);
|
||||
poVertical:
|
||||
isVisible := (ASlice.FPrevAngle >= PI_3_2) or (ASlice.FPrevAngle <= PI_1_2) or
|
||||
(ASlice.FNextAngle >= PI_3_2) or (ASlice.FNextAngle <= PI_1_2);
|
||||
end;
|
||||
if not isVisible then
|
||||
exit;
|
||||
|
||||
if AInside then begin
|
||||
r := innerRadius;
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4), ASlice.FPrevAngle, PI_3_4);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, PI_3_4, PI_7_4), ASlice.FNextAngle, PI_7_4);
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
begin
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4), ASlice.FPrevAngle, PI_3_4);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, PI_3_4, PI_7_4), ASlice.FNextAngle, PI_7_4);
|
||||
end;
|
||||
poHorizontal:
|
||||
begin
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, 0, pi), ASlice.FPrevAngle, 0);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, 0, pi), ASlice.FNextAngle, pi);
|
||||
end;
|
||||
poVertical:
|
||||
begin
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, PI_1_2, PI_3_2), ASlice.FPrevAngle, PI_1_2);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, PI_1_2, PI_3_2), ASlice.FNextAngle, PI_3_2);
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
r := FRadius;
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4), PI_7_4, ASlice.FPrevAngle);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, PI_3_4, PI_7_4), PI_3_4, ASlice.FNextAngle);
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
begin
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4), PI_7_4, ASlice.FPrevAngle);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, PI_3_4, PI_7_4), PI_3_4, ASlice.FNextAngle);
|
||||
end;
|
||||
poHorizontal:
|
||||
begin
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, 0, pi), pi, ASlice.FPrevAngle);
|
||||
angle2 := Ifthen(InRange(ASlice.FNextAngle, 0, pi), TWO_PI, ASlice.FNextAngle);
|
||||
end;
|
||||
poVertical:
|
||||
begin
|
||||
angle1 := IfThen(InRange(ASlice.FPrevAngle, PI_1_2, PI_3_2), PI_3_2, ASlice.FPrevAngle);
|
||||
angle2 := IfThen(InRange(ASlice.FNextAngle, PI_1_2, PI_3_2), PI_1_2, ASlice.FNextAngle);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
if angle2 < angle1 then angle2 += TWO_PI;
|
||||
|
||||
numSteps := Max(Round(TWO_PI * (angle2 - angle1) * r / STEP), 2);
|
||||
SetLength(p, 2 * numSteps + 1);
|
||||
case FOrientation of
|
||||
poNormal: ofs := Point(scaled_depth, -scaled_depth);
|
||||
poHorizontal: ofs := Point(0, scaled_depth);
|
||||
poVertical: ofs := Point(scaled_depth, 0);
|
||||
end;
|
||||
for i := 0 to numSteps - 1 do begin
|
||||
a := WeightedAverage(angle1, angle2, i / (numSteps - 1));
|
||||
p[i] := ASlice.FBase + RotatePointX(r, -a);
|
||||
p[High(p) - i - 1] := p[i] + Point(scaled_depth, -scaled_depth);
|
||||
p[i] := ASlice.FBase + FixAspectRatio(RotatePointX(r, -a));
|
||||
p[High(p) - i - 1] := p[i] + ofs;
|
||||
end;
|
||||
p[High(p)] := p[0];
|
||||
clr := GetDepthColor(SliceColor(ASlice.FOrigIndex));
|
||||
@ -438,59 +519,101 @@ var
|
||||
p: Array of TPoint;
|
||||
begin
|
||||
angle1 := ASlice.FPrevAngle;
|
||||
if ASlice.FNextAngle < ASlice.FPrevAngle then
|
||||
angle2 := TWO_PI + ASlice.FNextAngle
|
||||
else
|
||||
angle2 := ASlice.FNextAngle;
|
||||
angle2 := ASlice.FixedNextAngle;
|
||||
ni := Max(Round(TWO_PI * (angle2 - angle1) * innerRadius / STEP), 2);
|
||||
no := Max(Round(TWO_PI * (angle2 - angle1) * FRadius / STEP), 2);
|
||||
SetLength(p, ni + no);
|
||||
for i := 0 to no - 1 do begin
|
||||
a := WeightedAverage(angle1, angle2, i / (no - 1));
|
||||
p[i] := ASlice.FBase + RotatePointX(FRadius, -a);
|
||||
p[i] := ASlice.FBase + FixAspectRatio(RotatePointX(FRadius, -a));
|
||||
end;
|
||||
for i := 0 to ni - 1 do begin
|
||||
a := WeightedAverage(angle1, angle2, i / (ni - 1));
|
||||
p[no + ni - 1 - i] := ASlice.FBase + RotatePointX(innerRadius, -a);
|
||||
p[no + ni - 1 - i] := ASlice.FBase + FixAspectRatio(RotatePointX(innerRadius , -a));
|
||||
end;
|
||||
ADrawer.Polygon(p, 0, Length(p));
|
||||
end;
|
||||
|
||||
procedure DrawEdge3D(ASlice: TPieSlice; Angle: Double);
|
||||
var
|
||||
P1, P2, d: TPoint;
|
||||
begin
|
||||
ADrawer.SetBrushParams(
|
||||
bsSolid, GetDepthColor(SliceColor(ASlice.FOrigIndex)));
|
||||
P1 := ASlice.FBase + FixAspectRatio(RotatePointX(innerRadius, -Angle));
|
||||
P2 := ASlice.FBase + FixAspectRatio(RotatePointX(FRadius, -Angle));
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
ADrawer.DrawLineDepth(P1, P2, scaled_depth);
|
||||
poHorizontal:
|
||||
begin
|
||||
d := Point(0, scaled_depth);
|
||||
ADrawer.Polygon([P1, P1 + d, P2 + d, P2], 0, 4);
|
||||
end;
|
||||
poVertical:
|
||||
begin
|
||||
d := Point(scaled_depth, 0);
|
||||
ADrawer.Polygon([P1, P1 + d, P2 + d, P2], 0, 4);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DrawStartEdge3D(ASlice: TPieSlice);
|
||||
begin
|
||||
if StartEdgeVisible(ASlice) then begin
|
||||
ADrawer.SetBrushParams(
|
||||
bsSolid, GetDepthColor(SliceColor(ASlice.FOrigIndex)));
|
||||
ADrawer.DrawLineDepth(
|
||||
ASlice.FBase + RotatePointX(innerRadius, -ASlice.FPrevAngle),
|
||||
ASlice.FBase + RotatePointX(FRadius, -ASlice.FPrevAngle),
|
||||
scaled_depth
|
||||
);
|
||||
end;
|
||||
if StartEdgeVisible(ASlice) then
|
||||
DrawEdge3D(ASlice, ASlice.FPrevAngle);
|
||||
end;
|
||||
|
||||
procedure DrawEndEdge3D(ASlice: TPieSlice);
|
||||
begin
|
||||
if EndEdgeVisible(ASlice) then begin
|
||||
ADrawer.SetBrushParams(
|
||||
bsSolid, GetDepthColor(SliceColor(ASlice.FOrigIndex)));
|
||||
ADrawer.DrawLineDepth(
|
||||
ASlice.FBase + RotatePointX(innerRadius, -ASlice.FNextAngle),
|
||||
ASlice.FBase + RotatePointX(FRadius, -ASlice.FNextAngle),
|
||||
scaled_depth
|
||||
);
|
||||
if EndEdgeVisible(ASlice) then
|
||||
DrawEdge3D(ASlice, ASlice.FNextAngle);
|
||||
end;
|
||||
|
||||
procedure FixJoin(ASlice: TPieSlice);
|
||||
var
|
||||
P1, P2: TPoint;
|
||||
angle: Double;
|
||||
r: Integer;
|
||||
d: TPoint;
|
||||
begin
|
||||
if not ASlice.FVisible then
|
||||
exit;
|
||||
Angle := (ASlice.FPrevAngle + ASlice.FixedNextAngle) * 0.5;
|
||||
case FOrientation of
|
||||
poNormal:
|
||||
begin
|
||||
if not InRange(angle, PI_3_4, PI_7_4) then r := FRadius else r := CalcInnerRadius;
|
||||
d := Point(scaled_depth, -scaled_depth);
|
||||
end;
|
||||
poHorizontal:
|
||||
begin
|
||||
if angle > pi then r := FRadius else r := CalcInnerRadius;
|
||||
d := Point(0, scaled_depth);
|
||||
end;
|
||||
poVertical:
|
||||
begin
|
||||
if not InRange(angle, PI_1_2, PI_3_2) then r := FRadius else r := CalcInnerRadius;
|
||||
d := Point(scaled_depth, 0);
|
||||
end;
|
||||
end;
|
||||
P1 := ASlice.FBase + FixAspectRatio(RotatePointX(r, -Angle));
|
||||
P2 := P1 + d;
|
||||
ADrawer.SetPenParams(psSolid, GetDepthColor(SliceColor(ASlice.FOrigIndex)));
|
||||
ADrawer.Line(P1, P2);
|
||||
end;
|
||||
|
||||
var
|
||||
prevLabelPoly: TPointArray = nil;
|
||||
ps: TPieSlice;
|
||||
r: TPoint;
|
||||
begin
|
||||
if IsEmpty then exit;
|
||||
|
||||
Marks.SetAdditionalAngle(0);
|
||||
Measure(ADrawer);
|
||||
innerRadius := CalcInnerRadius;
|
||||
r := FixAspectRatio(Point(FRadius, FRadius));
|
||||
|
||||
ADrawer.SetPen(EdgePen);
|
||||
if Depth > 0 then begin
|
||||
@ -503,9 +626,9 @@ begin
|
||||
end;
|
||||
// Fix edge of ulta-long slice
|
||||
for ps in FSlices do
|
||||
if ps.Angle >= pi then begin;
|
||||
DrawVisibleArc3D(ps);
|
||||
break;
|
||||
if ps.Angle >= pi then begin
|
||||
//DrawVisibleArc3D(ps);
|
||||
FixJoin(ps);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -515,8 +638,8 @@ begin
|
||||
ADrawer.SetBrushParams(bsSolid, SliceColor(ps.FOrigIndex));
|
||||
if FInnerRadiusPercent = 0 then
|
||||
ADrawer.RadialPie(
|
||||
ps.FBase.X - FRadius, ps.FBase.Y - FRadius,
|
||||
ps.FBase.X + FRadius, ps.FBase.Y + FRadius,
|
||||
ps.FBase.X - r.x, ps.FBase.Y - r.y,
|
||||
ps.FBase.X + r.x, ps.FBase.Y + r.y,
|
||||
RadToDeg16(ps.FPrevAngle), RadToDeg16(ps.Angle))
|
||||
else
|
||||
DrawRing(ps);
|
||||
@ -545,6 +668,12 @@ begin
|
||||
for ps in FSlices do begin
|
||||
if not ps.FVisible then continue;
|
||||
c := APoint - ps.FBase;
|
||||
if (FDepth > 0) then
|
||||
case FOrientation of
|
||||
poNormal: ;
|
||||
poHorizontal: c.Y := round(c.Y / FAspectRatio);
|
||||
poVertical: c.X := round(c.X / FAspectRatio);
|
||||
end;
|
||||
if not InRange(sqr(c.X) + sqr(c.Y), sqr(innerRadius), sqr(FRadius)) then
|
||||
continue;
|
||||
pointAngle := NormalizeAngle(ArcTan2(-c.Y, c.X));
|
||||
@ -561,6 +690,17 @@ begin
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
function TCustomPieSeries.FixAspectRatio(P: TPoint): TPoint;
|
||||
begin
|
||||
Result := P;
|
||||
if FDepth > 0 then
|
||||
case FOrientation of
|
||||
poNormal: ;
|
||||
poVertical: Result.X := round(P.X * FAspectRatio);
|
||||
poHorizontal: Result.Y := round(P.Y * FAspectRatio);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.GetLegendItems(AItems: TChartLegendItems);
|
||||
var
|
||||
i: Integer;
|
||||
@ -603,6 +743,11 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TCustomPieSeries.GetViewAngle: Integer;
|
||||
begin
|
||||
Result := round(arccos(FAspectRatio) / pi * 180);
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.Measure(ADrawer: IChartDrawer);
|
||||
const
|
||||
MIN_RADIUS = 5;
|
||||
@ -706,6 +851,13 @@ begin
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.SetOrientation(AValue: TPieOrientation);
|
||||
begin
|
||||
if FOrientation = AValue then exit;
|
||||
FOrientation := AValue;
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.SetRotateLabels(AValue: Boolean);
|
||||
begin
|
||||
if FRotateLabels = AValue then exit;
|
||||
@ -720,6 +872,13 @@ begin
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.SetViewAngle(AValue: Integer);
|
||||
begin
|
||||
if GetViewAngle = AValue then exit;
|
||||
FAspectRatio := cos(pi * EnsureRange(AValue, 0, 89) / 180);
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
function TCustomPieSeries.SliceColor(AIndex: Integer): TColor;
|
||||
const
|
||||
SLICE_COLORS: array [0..14] of TColor = (
|
||||
@ -733,37 +892,79 @@ begin
|
||||
Result := ColorDef(Result, SLICE_COLORS[AIndex mod Length(SLICE_COLORS)]);
|
||||
end;
|
||||
|
||||
function TCustomPieSeries.SliceExploded(ASlice: TPieSlice): Boolean;
|
||||
begin
|
||||
Result := ASlice.FBase <> FCenter;
|
||||
end;
|
||||
|
||||
type
|
||||
TAngleFunc = function (ASlice: TPieSlice): Double;
|
||||
|
||||
function GetAngleForSortingNormal(ASlice: TPieSlice): Double;
|
||||
var
|
||||
next_angle: Double;
|
||||
begin
|
||||
next_angle := ASlice.FixedNextAngle;
|
||||
if ((ASlice.FPrevAngle >= PI_5_4) or (ASlice.FPrevAngle <= PI_1_4)) and
|
||||
InRange(ASlice.FNextAngle, PI_1_4, PI_5_4)
|
||||
then
|
||||
// Slice crossing the 45° point --> must be last slice to draw
|
||||
Result := 0
|
||||
else
|
||||
if InRange(ASlice.FPrevAngle, PI_1_4, PI_5_4) and InRange(next_angle, PI_5_4, TWO_PI + PI_1_4)
|
||||
then
|
||||
// Slice crossing the 225° point --> must be first slice to draw
|
||||
Result := pi
|
||||
else
|
||||
Result := IfThen(InRange(ASlice.FPrevAngle, PI_1_4, PI_5_4), ASlice.FPrevAngle, next_angle) - PI_1_4;
|
||||
end;
|
||||
|
||||
function GetAngleForSortingHoriz(ASlice: TPieSlice): Double;
|
||||
begin
|
||||
if (InRange(ASlice.FPrevAngle, 0, PI_1_2) or InRange(ASlice.FPrevAngle, PI_3_2, TWO_pi)) and
|
||||
InRange(ASlice.FNextAngle, PI_1_2, PI_3_2)
|
||||
then
|
||||
// Most backward slice --> must be first to draw
|
||||
Result := pi
|
||||
else
|
||||
if InRange(ASlice.FPrevAngle, PI_1_2, PI_3_2) and
|
||||
(InRange(ASlice.FNextAngle, PI_3_2, TWO_PI) or InRange(ASlice.FNextAngle, 0, PI_1_2))
|
||||
then
|
||||
// Most foremost slice --> must be the last to draw
|
||||
Result := 0
|
||||
else
|
||||
Result := IfThen(InRange(ASlice.FPrevAngle, PI_3_2, TWO_PI) or InRange(ASlice.FPrevAngle, 0, PI_1_2),
|
||||
ASlice.FPrevAngle, ASlice.FNextAngle) - PI_3_2
|
||||
end;
|
||||
|
||||
function GetAngleForSortingVert(ASlice: TPieSlice): Double;
|
||||
var
|
||||
next_angle: Double;
|
||||
begin
|
||||
next_angle := ASlice.FixedNextAngle;
|
||||
if (ASlice.FPrevAngle <= pi) and (ASlice.FNextAngle > pi) then
|
||||
// Slice crossing the 180° point, most backward slice --> first to draw
|
||||
Result := pi
|
||||
else
|
||||
if (ASlice.FPrevAngle >= pi) and (ASlice.FNextAngle < pi) then
|
||||
// Slice crossing the 0° point, most foreward slice --> last to draw
|
||||
Result := 0
|
||||
else
|
||||
Result :=IfThen(ASlice.FPrevAngle > pi, ASlice.FPrevAngle, next_angle);
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.SortSlices(out ASlices: TSliceArray);
|
||||
|
||||
function GetAngleForSorting(ASlice: TPieSlice): Double;
|
||||
var
|
||||
next_angle: double;
|
||||
begin
|
||||
next_angle := ASlice.FixedNextAngle;
|
||||
if ((ASlice.FPrevAngle >= PI_5_4) or (ASlice.FPrevAngle <= PI_1_4)) and
|
||||
InRange(ASlice.FNextAngle, PI_1_4, PI_5_4)
|
||||
then
|
||||
// Slice crossing the 45° point --> must be last slice to draw
|
||||
Result := PI_1_4
|
||||
else
|
||||
if InRange(ASlice.FPrevAngle, PI_1_4, PI_5_4) and InRange(next_angle, PI_5_4, TWO_PI + PI_1_4)
|
||||
then
|
||||
// Slice crossing the 225° point --> must be first slice to draw
|
||||
Result := PI_5_4
|
||||
else
|
||||
Result := IfThen(InRange(ASlice.FPrevAngle, PI_1_4, PI_5_4), ASlice.FPrevAngle, next_angle);
|
||||
end;
|
||||
|
||||
function CompareSlices(ASlice1, ASlice2: TPieSlice): Integer;
|
||||
function CompareSlices(ASlice1, ASlice2: TPieSlice; AngleFunc: TAngleFunc): Integer;
|
||||
var
|
||||
angle1, angle2: Double;
|
||||
begin
|
||||
angle1 := GetAngleForSorting(ASlice1) - PI_1_4;
|
||||
angle2 := GetAngleForSorting(ASlice2) - PI_1_4;
|
||||
angle1 := AngleFunc(ASlice1);
|
||||
angle2 := AngleFunc(ASlice2);
|
||||
Result := CompareValue(cos(angle1), cos(angle2));
|
||||
end;
|
||||
|
||||
procedure QuickSort(const L, R: Integer);
|
||||
procedure QuickSort(const L, R: Integer; AngleFunc: TAngleFunc);
|
||||
var
|
||||
i, j, m: Integer;
|
||||
ps: TPieSlice;
|
||||
@ -772,8 +973,8 @@ procedure TCustomPieSeries.SortSlices(out ASlices: TSliceArray);
|
||||
j := R;
|
||||
m := (L + R) div 2;
|
||||
while (i <= j) do begin
|
||||
while CompareSlices(ASlices[i], ASlices[m]) < 0 do inc(i);
|
||||
while CompareSlices(ASlices[j], ASlices[m]) > 0 do dec(j);
|
||||
while CompareSlices(ASlices[i], ASlices[m], AngleFunc) < 0 do inc(i);
|
||||
while CompareSlices(ASlices[j], ASlices[m], AngleFunc) > 0 do dec(j);
|
||||
if i <= j then begin
|
||||
ps := ASlices[i];
|
||||
ASlices[i] := ASlices[j];
|
||||
@ -781,13 +982,14 @@ procedure TCustomPieSeries.SortSlices(out ASlices: TSliceArray);
|
||||
inc(i);
|
||||
dec(j);
|
||||
end;
|
||||
if L < j then QuickSort(L, j);
|
||||
if i < R then QuickSort(i, R);
|
||||
if L < j then QuickSort(L, j, AngleFunc);
|
||||
if i < R then QuickSort(i, R, AngleFunc);
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
i, j: Integer;
|
||||
f: TAngleFunc;
|
||||
begin
|
||||
SetLength(ASlices, Length(FSlices) + 1);
|
||||
j := 0;
|
||||
@ -803,15 +1005,20 @@ begin
|
||||
inc(j);
|
||||
end;
|
||||
end;
|
||||
case FOrientation of
|
||||
poNormal: f := @GetAngleForSortingNormal;
|
||||
poHorizontal: f := @GetAngleForSortingHoriz;
|
||||
poVertical: f := @GetAngleForSortingVert;
|
||||
end;
|
||||
SetLength(ASlices, j);
|
||||
QuickSort(0, High(ASlices));
|
||||
QuickSort(0, High(ASlices), f);
|
||||
end;
|
||||
|
||||
function TCustomPieSeries.TryRadius(ADrawer: IChartDrawer): TRect;
|
||||
|
||||
function EndPoint(AAngle, ARadius: Double): TPoint;
|
||||
begin
|
||||
Result := RotatePointX(ARadius, -AAngle);
|
||||
Result := FixAspectRatio(RotatePointX(ARadius, -AAngle));
|
||||
end;
|
||||
|
||||
function LabelExtraDist(APoly: TPointArray; AAngle: Double): Double;
|
||||
|
@ -133,9 +133,11 @@ type
|
||||
property MarkPositionCentered;
|
||||
property MarkPositions;
|
||||
property Marks;
|
||||
property Orientation;
|
||||
property RotateLabels;
|
||||
property StartAngle;
|
||||
property Source;
|
||||
property ViewAngle;
|
||||
end;
|
||||
|
||||
TConnectType = (ctLine, ctStepXY, ctStepYX);
|
||||
|
Loading…
Reference in New Issue
Block a user