mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-23 01:39:31 +02:00
TAChart: Add new property "StartAngle" to TPieSeries. Update demo (new layout).
git-svn-id: trunk@60717 -
This commit is contained in:
parent
eb0786f83c
commit
0828a25696
@ -24,8 +24,8 @@ object Form1: TForm1
|
||||
ClientWidth = 572
|
||||
object ChartPie: TChart
|
||||
Left = 0
|
||||
Height = 384
|
||||
Top = 147
|
||||
Height = 407
|
||||
Top = 124
|
||||
Width = 572
|
||||
AxisList = <
|
||||
item
|
||||
@ -44,11 +44,13 @@ object Form1: TForm1
|
||||
end>
|
||||
Foot.Brush.Color = clBtnFace
|
||||
Foot.Font.Color = clBlue
|
||||
Title.Brush.Color = clBtnFace
|
||||
Title.Alignment = taLeftJustify
|
||||
Title.Brush.Color = clNone
|
||||
Title.Font.Color = clBlue
|
||||
Title.Text.Strings = (
|
||||
'TAChart'
|
||||
' Click on a slice to explode/unexplode it'
|
||||
)
|
||||
Title.Visible = True
|
||||
Toolset = ChartToolset1
|
||||
Align = alClient
|
||||
Color = clDefault
|
||||
@ -58,6 +60,8 @@ object Form1: TForm1
|
||||
Exploded = True
|
||||
Marks.Distance = 40
|
||||
Marks.Format = '%2:s'
|
||||
Marks.Frame.Color = clSilver
|
||||
Marks.LinkPen.Color = clSilver
|
||||
Marks.Style = smsLabel
|
||||
Source = ListChartSource1
|
||||
end
|
||||
@ -65,25 +69,27 @@ object Form1: TForm1
|
||||
object Panel1: TPanel
|
||||
AnchorSideTop.Side = asrCenter
|
||||
Left = 0
|
||||
Height = 147
|
||||
Height = 124
|
||||
Top = 0
|
||||
Width = 572
|
||||
Align = alTop
|
||||
Alignment = taLeftJustify
|
||||
Anchors = [akTop, akRight]
|
||||
AutoSize = True
|
||||
Caption = ' Click on a slice to explode/unexplode it'
|
||||
ClientHeight = 147
|
||||
BevelOuter = bvNone
|
||||
ClientHeight = 124
|
||||
ClientWidth = 572
|
||||
TabOrder = 1
|
||||
object seWords: TSpinEdit
|
||||
AnchorSideTop.Control = Panel1
|
||||
AnchorSideLeft.Control = lblWords
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = cbShowLabels
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = lblInnerRadius
|
||||
Left = 324
|
||||
Left = 222
|
||||
Height = 23
|
||||
Top = 7
|
||||
Top = 6
|
||||
Width = 72
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Top = 6
|
||||
BorderSpacing.Right = 24
|
||||
MaxValue = 10
|
||||
@ -92,14 +98,16 @@ object Form1: TForm1
|
||||
Value = 1
|
||||
end
|
||||
object lblWords: TLabel
|
||||
AnchorSideLeft.Control = cbMarkPositions
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = seWords
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seWords
|
||||
Left = 253
|
||||
Left = 151
|
||||
Height = 15
|
||||
Top = 11
|
||||
Top = 10
|
||||
Width = 63
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Left = 16
|
||||
BorderSpacing.Right = 8
|
||||
Caption = 'Label words'
|
||||
ParentColor = False
|
||||
@ -110,12 +118,12 @@ object Form1: TForm1
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideRight.Control = seWords
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 324
|
||||
Left = 222
|
||||
Height = 23
|
||||
Top = 34
|
||||
Top = 35
|
||||
Width = 72
|
||||
Anchors = [akTop, akLeft, akRight]
|
||||
BorderSpacing.Top = 4
|
||||
BorderSpacing.Top = 6
|
||||
Increment = 5
|
||||
MaxValue = 360
|
||||
MinValue = -360
|
||||
@ -126,9 +134,9 @@ object Form1: TForm1
|
||||
AnchorSideTop.Control = seLabelAngle
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seLabelAngle
|
||||
Left = 256
|
||||
Left = 154
|
||||
Height = 15
|
||||
Top = 38
|
||||
Top = 39
|
||||
Width = 60
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Right = 8
|
||||
@ -136,16 +144,14 @@ object Form1: TForm1
|
||||
ParentColor = False
|
||||
end
|
||||
object cbRotate: TCheckBox
|
||||
AnchorSideTop.Control = cbMarkAttachment
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideRight.Control = cbMarkAttachment
|
||||
AnchorSideLeft.Control = cbShowLabels
|
||||
AnchorSideTop.Control = seLabelAngle
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 309
|
||||
Left = 8
|
||||
Height = 19
|
||||
Top = 121
|
||||
Top = 37
|
||||
Width = 87
|
||||
Alignment = taLeftJustify
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Top = 6
|
||||
BorderSpacing.Bottom = 6
|
||||
Caption = 'Rotate labels'
|
||||
@ -153,16 +159,17 @@ object Form1: TForm1
|
||||
TabOrder = 2
|
||||
end
|
||||
object cbMarkPositions: TComboBox
|
||||
AnchorSideLeft.Control = cbShowLabels
|
||||
AnchorSideTop.Control = cbMarkAttachment
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideRight.Control = seDepthBrightnessDelta
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 438
|
||||
Left = 8
|
||||
Height = 23
|
||||
Top = 92
|
||||
Top = 93
|
||||
Width = 127
|
||||
Anchors = [akTop, akRight]
|
||||
AutoSize = False
|
||||
BorderSpacing.Bottom = 6
|
||||
BorderSpacing.Bottom = 8
|
||||
ItemHeight = 15
|
||||
ItemIndex = 0
|
||||
Items.Strings = (
|
||||
@ -177,26 +184,25 @@ object Form1: TForm1
|
||||
end
|
||||
object Cb3D: TCheckBox
|
||||
AnchorSideLeft.Control = lblInnerRadius
|
||||
AnchorSideTop.Control = seLabelAngle
|
||||
AnchorSideTop.Control = seDepth
|
||||
AnchorSideTop.Side = asrCenter
|
||||
Left = 420
|
||||
Left = 327
|
||||
Height = 19
|
||||
Top = 36
|
||||
Top = 66
|
||||
Width = 34
|
||||
Caption = '3D'
|
||||
OnChange = Cb3DChange
|
||||
TabOrder = 4
|
||||
end
|
||||
object seInnerRadius: TSpinEdit
|
||||
AnchorSideLeft.Control = seDepthBrightnessDelta
|
||||
AnchorSideTop.Control = seWords
|
||||
AnchorSideRight.Control = Panel1
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 503
|
||||
Left = 410
|
||||
Height = 23
|
||||
Top = 7
|
||||
Top = 6
|
||||
Width = 62
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Right = 6
|
||||
BorderSpacing.Right = 8
|
||||
OnChange = seInnerRadiusChange
|
||||
TabOrder = 5
|
||||
end
|
||||
@ -204,9 +210,9 @@ object Form1: TForm1
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = lblWords
|
||||
AnchorSideRight.Control = seInnerRadius
|
||||
Left = 420
|
||||
Left = 327
|
||||
Height = 15
|
||||
Top = 11
|
||||
Top = 10
|
||||
Width = 75
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Right = 8
|
||||
@ -216,12 +222,12 @@ object Form1: TForm1
|
||||
object lblDepth: TLabel
|
||||
AnchorSideLeft.Control = Cb3D
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = Cb3D
|
||||
AnchorSideTop.Control = seDepth
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seDepth
|
||||
Left = 463
|
||||
Left = 370
|
||||
Height = 15
|
||||
Top = 38
|
||||
Top = 68
|
||||
Width = 32
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Left = 12
|
||||
@ -231,16 +237,14 @@ object Form1: TForm1
|
||||
ParentColor = False
|
||||
end
|
||||
object seDepth: TSpinEdit
|
||||
AnchorSideLeft.Control = seInnerRadius
|
||||
AnchorSideTop.Control = seLabelAngle
|
||||
AnchorSideLeft.Control = seDepthBrightnessDelta
|
||||
AnchorSideTop.Control = seDistance
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = Panel1
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 503
|
||||
Left = 410
|
||||
Height = 23
|
||||
Top = 34
|
||||
Top = 64
|
||||
Width = 62
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Right = 6
|
||||
Enabled = False
|
||||
OnChange = seDepthChange
|
||||
@ -248,17 +252,17 @@ object Form1: TForm1
|
||||
Value = 20
|
||||
end
|
||||
object seDepthBrightnessDelta: TSpinEdit
|
||||
AnchorSideLeft.Control = seInnerRadius
|
||||
AnchorSideLeft.Control = lblDepthBrightnessDelta
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = seDepth
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideRight.Control = Panel1
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 503
|
||||
Left = 410
|
||||
Height = 23
|
||||
Top = 61
|
||||
Top = 93
|
||||
Width = 62
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Top = 4
|
||||
BorderSpacing.Top = 6
|
||||
BorderSpacing.Right = 6
|
||||
Enabled = False
|
||||
MaxValue = 255
|
||||
@ -268,16 +272,17 @@ object Form1: TForm1
|
||||
Value = -32
|
||||
end
|
||||
object lblDepthBrightnessDelta: TLabel
|
||||
AnchorSideLeft.Control = cbMarkPositions
|
||||
AnchorSideLeft.Control = seDistance
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = seDepthBrightnessDelta
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seDepthBrightnessDelta
|
||||
Left = 411
|
||||
Left = 318
|
||||
Height = 15
|
||||
Top = 65
|
||||
Top = 97
|
||||
Width = 84
|
||||
Alignment = taRightJustify
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Left = 24
|
||||
BorderSpacing.Right = 8
|
||||
Caption = 'Brightness delta'
|
||||
Enabled = False
|
||||
@ -285,14 +290,15 @@ object Form1: TForm1
|
||||
WordWrap = True
|
||||
end
|
||||
object cbShowLabels: TCheckBox
|
||||
AnchorSideTop.Control = seWords
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideLeft.Control = Panel1
|
||||
AnchorSideTop.Control = Panel1
|
||||
AnchorSideRight.Control = lblWords
|
||||
Left = 155
|
||||
Left = 8
|
||||
Height = 19
|
||||
Top = 9
|
||||
Top = 8
|
||||
Width = 82
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Left = 8
|
||||
BorderSpacing.Top = 8
|
||||
BorderSpacing.Right = 16
|
||||
Caption = 'Show labels'
|
||||
Checked = True
|
||||
@ -304,9 +310,9 @@ object Form1: TForm1
|
||||
AnchorSideTop.Control = seDistance
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seDistance
|
||||
Left = 271
|
||||
Left = 169
|
||||
Height = 15
|
||||
Top = 67
|
||||
Top = 68
|
||||
Width = 45
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Right = 8
|
||||
@ -318,9 +324,9 @@ object Form1: TForm1
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideRight.Control = seLabelAngle
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 324
|
||||
Left = 222
|
||||
Height = 23
|
||||
Top = 63
|
||||
Top = 64
|
||||
Width = 72
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Top = 6
|
||||
@ -329,16 +335,15 @@ object Form1: TForm1
|
||||
Value = 40
|
||||
end
|
||||
object cbMarkAttachment: TComboBox
|
||||
AnchorSideLeft.Control = lblDistance
|
||||
AnchorSideLeft.Control = cbShowLabels
|
||||
AnchorSideTop.Control = seDistance
|
||||
AnchorSideTop.Side = asrBottom
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seDistance
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 271
|
||||
Left = 8
|
||||
Height = 23
|
||||
Top = 92
|
||||
Top = 64
|
||||
Width = 125
|
||||
Anchors = [akTop, akLeft, akRight]
|
||||
BorderSpacing.Top = 6
|
||||
BorderSpacing.Bottom = 6
|
||||
ItemHeight = 15
|
||||
@ -354,19 +359,47 @@ object Form1: TForm1
|
||||
Text = 'Default'
|
||||
end
|
||||
object cbMarkPositionsCentered: TCheckBox
|
||||
AnchorSideTop.Control = cbRotate
|
||||
AnchorSideLeft.Control = lblWords
|
||||
AnchorSideTop.Control = cbMarkPositions
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = cbMarkPositions
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 418
|
||||
Left = 151
|
||||
Height = 19
|
||||
Top = 121
|
||||
Top = 95
|
||||
Width = 147
|
||||
Alignment = taLeftJustify
|
||||
Anchors = [akTop, akRight]
|
||||
Caption = 'Mark positions centered'
|
||||
OnChange = cbMarkPositionsCenteredChange
|
||||
TabOrder = 11
|
||||
end
|
||||
object lblStartAngle: TLabel
|
||||
AnchorSideLeft.Side = asrBottom
|
||||
AnchorSideTop.Control = seStartAngle
|
||||
AnchorSideTop.Side = asrCenter
|
||||
AnchorSideRight.Control = seStartAngle
|
||||
Left = 346
|
||||
Height = 15
|
||||
Top = 39
|
||||
Width = 56
|
||||
Anchors = [akTop, akRight]
|
||||
BorderSpacing.Right = 8
|
||||
Caption = 'Start angle'
|
||||
ParentColor = False
|
||||
end
|
||||
object seStartAngle: TSpinEdit
|
||||
AnchorSideLeft.Control = seDepthBrightnessDelta
|
||||
AnchorSideTop.Control = seLabelAngle
|
||||
AnchorSideRight.Side = asrBottom
|
||||
Left = 410
|
||||
Height = 23
|
||||
Top = 35
|
||||
Width = 62
|
||||
BorderSpacing.Right = 6
|
||||
MaxValue = 180
|
||||
MinValue = -180
|
||||
OnChange = seStartAngleChange
|
||||
TabOrder = 12
|
||||
end
|
||||
end
|
||||
end
|
||||
object tsPolar: TTabSheet
|
||||
|
@ -30,6 +30,7 @@ type
|
||||
cbShowLabels: TCheckBox;
|
||||
cbMarkPositionsCentered: TCheckBox;
|
||||
lblDistance: TLabel;
|
||||
lblStartAngle: TLabel;
|
||||
seDepth: TSpinEdit;
|
||||
seDepthBrightnessDelta: TSpinEdit;
|
||||
lblInnerRadius: TLabel;
|
||||
@ -44,6 +45,7 @@ type
|
||||
pnlPolar: TPanel;
|
||||
RandomChartSource1: TRandomChartSource;
|
||||
sbTransparency: TScrollBar;
|
||||
seStartAngle: TSpinEdit;
|
||||
seWords: TSpinEdit;
|
||||
seLabelAngle: TSpinEdit;
|
||||
seInnerRadius: TSpinEdit;
|
||||
@ -67,6 +69,7 @@ type
|
||||
procedure seInnerRadiusChange(Sender: TObject);
|
||||
procedure FormCreate(Sender: TObject);
|
||||
procedure sbTransparencyChange(Sender: TObject);
|
||||
procedure seStartAngleChange(Sender: TObject);
|
||||
procedure seWordsChange(Sender: TObject);
|
||||
procedure seLabelAngleChange(Sender: TObject);
|
||||
end;
|
||||
@ -194,6 +197,11 @@ begin
|
||||
lblTransparency.Caption := 'Transparency (' + IntToStr(sbTransparency.Position) + ')';
|
||||
end;
|
||||
|
||||
procedure TForm1.seStartAngleChange(Sender: TObject);
|
||||
begin
|
||||
ChartPiePieSeries1.StartAngle := seStartAngle.Value;
|
||||
end;
|
||||
|
||||
procedure TForm1.seLabelAngleChange(Sender: TObject);
|
||||
begin
|
||||
ChartPiePieSeries1.Marks.LabelFont.Orientation := seLabelAngle.Value * 10;
|
||||
|
@ -4,11 +4,7 @@
|
||||
<Version Value="12"/>
|
||||
<PathDelim Value="\"/>
|
||||
<General>
|
||||
<Flags>
|
||||
<CompatibilityMode Value="True"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<MainUnit Value="0"/>
|
||||
<ResourceType Value="res"/>
|
||||
<UseXPManifest Value="True"/>
|
||||
</General>
|
||||
|
@ -63,6 +63,7 @@ function IsLineIntersectsLine(const AA, AB, AC, AD: TPoint): Boolean;
|
||||
function IsPolygonIntersectsPolygon(const AP1, AP2: array of TPoint): Boolean;
|
||||
function LineIntersectsRect(
|
||||
var AA, AB: TDoublePoint; const ARect: TDoubleRect): Boolean;
|
||||
function NormalizeAngle(Angle: Double): Double;
|
||||
procedure NormalizeRect(var ARect: TRect); overload;
|
||||
procedure NormalizeRect(var ARect: TDoubleRect); overload;
|
||||
function MakeSquare(const ARect: TRect): TRect;
|
||||
@ -499,6 +500,16 @@ begin
|
||||
Result.cy := Max(Abs(pt1.Y), Abs(pt2.Y));
|
||||
end;
|
||||
|
||||
// Normalizes an angle to be in the interval 0 .. 2 pi
|
||||
function NormalizeAngle(Angle: Double): Double;
|
||||
const
|
||||
TWO_PI = 2.0 * pi;
|
||||
begin
|
||||
Result := Angle;
|
||||
while Result > TWO_PI do Result -= TWO_PI;
|
||||
while Result < 0 do Result += TWO_PI;
|
||||
end;
|
||||
|
||||
procedure NormalizeRect(var ARect: TRect);
|
||||
begin
|
||||
with ARect do begin
|
||||
|
@ -49,10 +49,12 @@ type
|
||||
FBase: TPoint;
|
||||
FLabel: TLabelParams;
|
||||
FOrigIndex: Integer;
|
||||
FPrevAngle, FNextAngle: Double;
|
||||
FPrevAngle, FNextAngle: Double; // in CCW direction
|
||||
// FNextAngle may become less than FPrevAngle when crossing 360°
|
||||
FVisible: Boolean;
|
||||
function Angle: Double; inline;
|
||||
function CenterAngle: Double; inline;
|
||||
function FixedNextAngle: Double; inline;
|
||||
end;
|
||||
|
||||
TPieMarkPositions = (pmpAround, pmpInside, pmpLeftRight);
|
||||
@ -70,6 +72,7 @@ type
|
||||
FRadius: Integer;
|
||||
FInnerRadiusPercent: Integer;
|
||||
FSlices: array of TPieSlice;
|
||||
FStartAngle: Integer;
|
||||
private
|
||||
FEdgePen: TPen;
|
||||
FExploded: Boolean;
|
||||
@ -84,6 +87,7 @@ type
|
||||
procedure SetMarkPositionCentered(AValue: Boolean);
|
||||
procedure SetMarkPositions(AValue: TPieMarkPositions);
|
||||
procedure SetRotateLabels(AValue: Boolean);
|
||||
procedure SetStartAngle(AValue: Integer);
|
||||
function SliceColor(AIndex: Integer): TColor;
|
||||
function TryRadius(ADrawer: IChartDrawer): TRect;
|
||||
protected
|
||||
@ -95,6 +99,7 @@ type
|
||||
property MarkPositionCentered: Boolean
|
||||
read FMarkPositionCentered write SetMarkPositionCentered default false;
|
||||
property Radius: Integer read FRadius;
|
||||
property StartAngle: Integer read FStartAngle write SetStartAngle default 0;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
@ -194,6 +199,8 @@ uses
|
||||
|
||||
const
|
||||
TWO_PI = 2 * pi;
|
||||
PI_1_2 = pi / 2;
|
||||
PI_3_2 = (3 / 2) * pi;
|
||||
PI_1_4 = pi / 4;
|
||||
PI_3_4 = (3 / 4) * pi;
|
||||
PI_5_4 = (5 / 4) * pi;
|
||||
@ -203,14 +210,30 @@ const
|
||||
|
||||
function TPieSlice.Angle: Double;
|
||||
begin
|
||||
Result := FNextAngle - FPrevAngle;
|
||||
if FNextAngle < FPrevAngle then
|
||||
Result := TWO_PI + FNextAngle - FPrevAngle
|
||||
else
|
||||
Result := FNextAngle - FPrevAngle;
|
||||
end;
|
||||
|
||||
function TPieSlice.CenterAngle: Double;
|
||||
begin
|
||||
Result := (FNextAngle + FPrevAngle) / 2;
|
||||
if FNextAngle <= FPrevAngle then
|
||||
Result := NormalizeAngle((TWO_PI + FNextAngle + FPrevAngle) * 0.5)
|
||||
else
|
||||
Result := NormalizeAngle((FNextAngle + FPrevAngle) * 0.5);
|
||||
end;
|
||||
|
||||
{ FixedNextAngle is guaranteed to be greater than FPrevAngle }
|
||||
function TPieSlice.FixedNextAngle: Double;
|
||||
begin
|
||||
if FPrevAngle > FNextAngle then
|
||||
Result := FNextAngle + TWO_PI
|
||||
else
|
||||
Result := FNextAngle;
|
||||
end;
|
||||
|
||||
|
||||
{ TLegendItemPieS }
|
||||
|
||||
procedure TLegendItemPie.Draw(ADrawer: IChartDrawer; const ARect: TRect);
|
||||
@ -234,6 +257,7 @@ begin
|
||||
FColors[AIndex] := AValue;
|
||||
end;
|
||||
|
||||
|
||||
{ TLegendItemPieSlice }
|
||||
|
||||
procedure TLegendItemPieSlice.Draw(ADrawer: IChartDrawer; const ARect: TRect);
|
||||
@ -265,6 +289,7 @@ begin
|
||||
Self.FMarkDistancePercent := FMarkDistancePercent;
|
||||
Self.FMarkPositionCentered := FMarkPositionCentered;
|
||||
Self.FRotateLabels := FRotateLabels;
|
||||
Self.FStartAngle := FStartAngle;
|
||||
end;
|
||||
inherited Assign(ASource);
|
||||
end;
|
||||
@ -359,11 +384,15 @@ var
|
||||
begin
|
||||
if AInside and (FInnerRadiusPercent = 0) then
|
||||
exit;
|
||||
if AInside then
|
||||
isVisible := (ASlice.FPrevAngle < PI_7_4) and (ASlice.FNextAngle > PI_3_4)
|
||||
|
||||
if ASlice.Angle >= pi then
|
||||
isVisible := true
|
||||
else
|
||||
isVisible := not (InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4) and
|
||||
InRange(ASlice.FNextAngle, PI_3_4, PI_7_4) );
|
||||
if AInside then
|
||||
isVisible := InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4) or InRange(ASlice.FNextAngle, PI_3_4, PI_7_4)
|
||||
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);
|
||||
if not isVisible then
|
||||
exit;
|
||||
|
||||
@ -376,6 +405,7 @@ var
|
||||
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;
|
||||
if angle2 < angle1 then angle2 += TWO_PI;
|
||||
numSteps := Max(Round(TWO_PI * (angle2 - angle1) * r / STEP), 2);
|
||||
SetLength(p, 2 * numSteps + 1);
|
||||
for i := 0 to numSteps - 1 do begin
|
||||
@ -408,7 +438,10 @@ var
|
||||
p: Array of TPoint;
|
||||
begin
|
||||
angle1 := ASlice.FPrevAngle;
|
||||
angle2 := ASlice.FNextAngle;
|
||||
if ASlice.FNextAngle < ASlice.FPrevAngle then
|
||||
angle2 := TWO_PI + ASlice.FNextAngle
|
||||
else
|
||||
angle2 := ASlice.FNextAngle;
|
||||
ni := Max(Round(TWO_PI * (angle2 - angle1) * innerRadius / STEP), 2);
|
||||
no := Max(Round(TWO_PI * (angle2 - angle1) * FRadius / STEP), 2);
|
||||
SetLength(p, ni + no);
|
||||
@ -470,7 +503,7 @@ begin
|
||||
end;
|
||||
// Fix edge of ulta-long slice
|
||||
for ps in FSlices do
|
||||
if ps.FNextAngle - ps.FPrevAngle > pi then begin;
|
||||
if ps.Angle >= pi then begin;
|
||||
DrawVisibleArc3D(ps);
|
||||
break;
|
||||
end;
|
||||
@ -508,18 +541,22 @@ var
|
||||
ps: TPieSlice;
|
||||
innerRadius: Integer;
|
||||
begin
|
||||
innerRadius := CalcInnerRadius;
|
||||
for ps in FSlices do begin
|
||||
if not ps.FVisible then continue;
|
||||
c := APoint - ps.FBase;
|
||||
pointAngle := ArcTan2(-c.Y, c.X);
|
||||
innerRadius := CalcInnerRadius;
|
||||
if pointAngle < 0 then
|
||||
pointAngle += 2 * Pi;
|
||||
if
|
||||
InRange(pointAngle, ps.FPrevAngle, ps.FNextAngle) and
|
||||
InRange(Sqr(c.X) + Sqr(c.Y), Sqr(innerRadius), Sqr(FRadius))
|
||||
then
|
||||
exit(ps.FOrigIndex);
|
||||
if not InRange(sqr(c.X) + sqr(c.Y), sqr(innerRadius), sqr(FRadius)) then
|
||||
continue;
|
||||
pointAngle := NormalizeAngle(ArcTan2(-c.Y, c.X));
|
||||
if ps.FNextAngle <= ps.FPrevAngle then begin
|
||||
if InRange(pointAngle, ps.FPrevAngle - TWO_PI, ps.FNextAngle) or
|
||||
InRange(pointAngle, ps.FPrevAngle, ps.FNextAngle + TWO_PI)
|
||||
then
|
||||
exit(ps.FOrigIndex);
|
||||
end else begin
|
||||
if InRange(pointAngle, ps.FPrevAngle, ps.FNextAngle) then
|
||||
exit(ps.FOrigIndex);
|
||||
end;
|
||||
end;
|
||||
Result := -1;
|
||||
end;
|
||||
@ -676,6 +713,13 @@ begin
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
procedure TCustomPieSeries.SetStartAngle(AValue: Integer);
|
||||
begin
|
||||
if FStartAngle = AValue then exit;
|
||||
FStartAngle := AValue mod 360;
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
function TCustomPieSeries.SliceColor(AIndex: Integer): TColor;
|
||||
const
|
||||
SLICE_COLORS: array [0..14] of TColor = (
|
||||
@ -691,19 +735,32 @@ 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;
|
||||
var
|
||||
angle1, angle2: Double;
|
||||
begin
|
||||
if (ASlice1.FPrevAngle >= PI_3_4) and (ASlice1.FNextAngle >= PI_5_4) then
|
||||
angle1 := ASlice1.FNextAngle
|
||||
else
|
||||
angle1 := IfThen(InRange(ASlice1.FPrevAngle, PI_1_4, PI_5_4), ASlice1.FPrevAngle, ASlice1.FNextAngle);
|
||||
if (ASlice2.FPrevAngle >= PI_3_4) and (ASlice2.FNextAngle >= PI_5_4) then
|
||||
angle2 := ASlice2.FNextAngle
|
||||
else
|
||||
angle2 := IfThen(InRange(ASlice2.FPrevAngle, PI_1_4, PI_5_4), ASlice2.FPrevAngle, ASlice2.FNextAngle);
|
||||
Result := CompareValue(cos(angle1 - PI_1_4), cos(angle2 - PI_1_4));
|
||||
angle1 := GetAngleForSorting(ASlice1) - PI_1_4;
|
||||
angle2 := GetAngleForSorting(ASlice2) - PI_1_4;
|
||||
Result := CompareValue(cos(angle1), cos(angle2));
|
||||
end;
|
||||
|
||||
procedure QuickSort(const L, R: Integer);
|
||||
@ -735,9 +792,9 @@ begin
|
||||
SetLength(ASlices, Length(FSlices) + 1);
|
||||
j := 0;
|
||||
for i:=0 to High(FSlices) do begin
|
||||
if FSlices[i].FNextAngle - FSlices[i].FPrevAngle >= pi then begin
|
||||
if FSlices[i].Angle >= pi then begin
|
||||
ASlices[j] := FSlices[i];
|
||||
ASlices[j].FNextAngle := (FSlices[i].FPrevAngle + FSlices[i].FNextAngle) * 0.5;
|
||||
ASlices[j].FNextAngle := FSlices[i].CenterAngle;
|
||||
ASlices[j+1] := FSlices[i];
|
||||
ASlices[j+1].FPrevAngle := ASlices[j].FNextAngle;
|
||||
inc(j, 2);
|
||||
@ -820,7 +877,7 @@ function TCustomPieSeries.TryRadius(ADrawer: IChartDrawer): TRect;
|
||||
pmpInside:
|
||||
FCenter -= Ofs(AAngle);
|
||||
pmpLeftRight:
|
||||
FCenter += Ofs(IfThen(InRange(AAngle, Pi / 2, 3 * Pi / 2), Pi, 0));
|
||||
FCenter += Ofs(IfThen(InRange(AAngle, PI_1_2, PI_3_2), pi, 0));
|
||||
end;
|
||||
for i := 0 to High(p) do
|
||||
ExpandRect(Result, p[i] + FCenter);
|
||||
@ -832,13 +889,16 @@ const
|
||||
var
|
||||
i, j: Integer;
|
||||
di: PChartDataItem;
|
||||
prevAngle: Double = 0;
|
||||
prevAngle: Double;
|
||||
a, total: Double;
|
||||
scaled_depth: Integer;
|
||||
start_angle: Double;
|
||||
next_angle: Double;
|
||||
begin
|
||||
Result.TopLeft := FCenter;
|
||||
Result.BottomRight := FCenter;
|
||||
scaled_depth := ADrawer.Scale(Depth);
|
||||
start_angle := NormalizeAngle((FStartAngle mod 360) / 180 * pi);
|
||||
SetLength(FSlices, Count);
|
||||
j := 0;
|
||||
// This is a workaround for db source invalidating the cache due to
|
||||
@ -846,24 +906,26 @@ begin
|
||||
total := Source.ValuesTotal;
|
||||
if total = 0 then
|
||||
exit;
|
||||
prevAngle := start_angle;
|
||||
for i := 0 to Count - 1 do begin
|
||||
di := Source[i];
|
||||
if IsNan(di^.Y) then continue;
|
||||
with FSlices[j] do begin
|
||||
FOrigIndex := i;
|
||||
FPrevAngle := prevAngle;
|
||||
FNextAngle := FPrevAngle + CycleToRad(di^.Y / total);
|
||||
next_angle := FPrevAngle + CycleToRad(di^.Y / total);
|
||||
FNextAngle := NormalizeAngle(next_angle);
|
||||
FVisible := not IsNan(di^.X);
|
||||
if FVisible then begin
|
||||
FBase := FCenter;
|
||||
a := CenterAngle;
|
||||
if Exploded and (di^.X > 0) then
|
||||
FBase += EndPoint(a, FRadius * di^.X);
|
||||
ExpandRect(Result, FBase, FRadius, -FPrevAngle, -FNextAngle);
|
||||
ExpandRect(Result, FBase, FRadius, -FPrevAngle, -next_angle);
|
||||
if Depth > 0 then
|
||||
ExpandRect(
|
||||
Result, FBase + Point(scaled_depth, -scaled_depth),
|
||||
FRadius, -FPrevAngle, -FNextAngle);
|
||||
FRadius, -FPrevAngle, -next_angle);
|
||||
if FMarkPositionCentered then
|
||||
FLabel.FAttachment := EndPoint(a, (CalcInnerRadius + FRadius) div 2) + FBase
|
||||
else
|
||||
|
@ -134,6 +134,7 @@ type
|
||||
property MarkPositions;
|
||||
property Marks;
|
||||
property RotateLabels;
|
||||
property StartAngle;
|
||||
property Source;
|
||||
end;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user