TAChart: Add new properties Orientation and ViewAngle to TPieSeries. Update demo.

git-svn-id: trunk@60728 -
This commit is contained in:
wp 2019-03-19 22:11:22 +00:00
parent 4259c682c9
commit 50b7e5b2a4
4 changed files with 366 additions and 81 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);