diff --git a/components/tachart/demo/radial/main.lfm b/components/tachart/demo/radial/main.lfm index 99072a81c7..e301991f19 100644 --- a/components/tachart/demo/radial/main.lfm +++ b/components/tachart/demo/radial/main.lfm @@ -1,32 +1,32 @@ object Form1: TForm1 Left = 459 - Height = 433 + Height = 482 Top = 346 - Width = 566 + Width = 580 Caption = 'Form1' - ClientHeight = 433 - ClientWidth = 566 + ClientHeight = 482 + ClientWidth = 580 OnCreate = FormCreate Position = poScreenCenter LCLVersion = '2.1.0.0' object PageControl1: TPageControl Left = 0 - Height = 433 + Height = 482 Top = 0 - Width = 566 + Width = 580 ActivePage = tsPie Align = alClient TabIndex = 0 TabOrder = 0 object tsPie: TTabSheet Caption = 'Pie' - ClientHeight = 405 - ClientWidth = 558 + ClientHeight = 454 + ClientWidth = 572 object ChartPie: TChart Left = 0 - Height = 337 - Top = 68 - Width = 558 + Height = 336 + Top = 118 + Width = 572 AxisList = < item Visible = False @@ -54,6 +54,7 @@ object Form1: TForm1 Color = clDefault OnMouseDown = ChartPieMouseDown object ChartPiePieSeries1: TPieSeries + DepthBrightnessDelta = -32 Exploded = True Marks.Distance = 40 Marks.Format = '%2:s' @@ -64,27 +65,26 @@ object Form1: TForm1 object Panel1: TPanel AnchorSideTop.Side = asrCenter Left = 0 - Height = 68 + Height = 118 Top = 0 - Width = 558 + Width = 572 Align = alTop Alignment = taLeftJustify AutoSize = True Caption = ' Click on a slice to explode/unexplode it' - ClientHeight = 68 - ClientWidth = 558 + ClientHeight = 118 + ClientWidth = 572 TabOrder = 1 object seWords: TSpinEdit AnchorSideTop.Control = Panel1 - AnchorSideRight.Control = Panel1 - AnchorSideRight.Side = asrBottom - Left = 491 + AnchorSideRight.Control = lblInnerRadius + Left = 326 Height = 23 - Top = 9 - Width = 58 + Top = 7 + Width = 72 Anchors = [akTop, akRight] - BorderSpacing.Top = 8 - BorderSpacing.Right = 8 + BorderSpacing.Top = 6 + BorderSpacing.Right = 16 MaxValue = 10 OnChange = seWordsChange TabOrder = 0 @@ -94,9 +94,9 @@ object Form1: TForm1 AnchorSideTop.Control = seWords AnchorSideTop.Side = asrCenter AnchorSideRight.Control = seWords - Left = 420 + Left = 255 Height = 15 - Top = 13 + Top = 11 Width = 63 Anchors = [akTop, akRight] BorderSpacing.Right = 8 @@ -104,14 +104,17 @@ object Form1: TForm1 ParentColor = False end object seLabelAngle: TSpinEdit + AnchorSideLeft.Control = seWords AnchorSideTop.Control = seWords - AnchorSideRight.Control = lblWords - Left = 354 + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = seWords + AnchorSideRight.Side = asrBottom + Left = 326 Height = 23 - Top = 9 - Width = 50 - Anchors = [akTop, akRight] - BorderSpacing.Right = 16 + Top = 34 + Width = 72 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 4 Increment = 5 MaxValue = 360 MinValue = -360 @@ -121,43 +124,43 @@ object Form1: TForm1 object lblLabelAngle: TLabel AnchorSideTop.Control = seLabelAngle AnchorSideTop.Side = asrCenter - Left = 291 + AnchorSideRight.Control = seLabelAngle + Left = 258 Height = 15 - Top = 13 + Top = 38 Width = 60 Anchors = [akTop, akRight] + BorderSpacing.Right = 8 Caption = 'Label angle' ParentColor = False end object cbRotate: TCheckBox - AnchorSideLeft.Control = Cb3D - AnchorSideLeft.Side = asrBottom + AnchorSideLeft.Control = cbMarkPositions AnchorSideTop.Control = cbMarkPositions - AnchorSideTop.Side = asrCenter + AnchorSideTop.Side = asrBottom AnchorSideRight.Side = asrBottom - Left = 462 + Left = 255 Height = 19 - Top = 38 + Top = 90 Width = 87 - BorderSpacing.Left = 8 - BorderSpacing.Top = 4 BorderSpacing.Bottom = 8 Caption = 'Rotate labels' OnChange = cbRotateChange TabOrder = 2 end object cbMarkPositions: TComboBox + AnchorSideLeft.Control = lblWords AnchorSideTop.Control = lblLabelAngle AnchorSideTop.Side = asrBottom AnchorSideRight.Control = seLabelAngle AnchorSideRight.Side = asrBottom - Left = 291 + Left = 255 Height = 23 - Top = 36 - Width = 113 - Anchors = [akTop, akRight] + Top = 61 + Width = 143 + Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 8 - BorderSpacing.Bottom = 8 + BorderSpacing.Bottom = 6 ItemHeight = 15 ItemIndex = 0 Items.Strings = ( @@ -171,30 +174,126 @@ object Form1: TForm1 Text = 'Around' end object Cb3D: TCheckBox - AnchorSideLeft.Control = cbMarkPositions - AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = cbMarkPositions + AnchorSideLeft.Control = lblInnerRadius + AnchorSideTop.Control = seLabelAngle AnchorSideTop.Side = asrCenter - Left = 420 + Left = 414 Height = 19 - Top = 38 + Top = 36 Width = 34 - BorderSpacing.Left = 16 Caption = '3D' OnChange = Cb3DChange TabOrder = 4 end + object seInnerRadius: TSpinEdit + AnchorSideTop.Control = seWords + AnchorSideRight.Control = Panel1 + AnchorSideRight.Side = asrBottom + Left = 497 + Height = 23 + Top = 7 + Width = 62 + Anchors = [akTop, akRight] + BorderSpacing.Right = 12 + OnChange = seInnerRadiusChange + TabOrder = 5 + end + object lblInnerRadius: TLabel + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = lblWords + AnchorSideRight.Control = seInnerRadius + Left = 414 + Height = 15 + Top = 11 + Width = 75 + Anchors = [akTop, akRight] + BorderSpacing.Right = 8 + Caption = 'Inner radius %' + ParentColor = False + end + object lblDepth: TLabel + AnchorSideLeft.Control = Cb3D + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = Cb3D + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = seDepth + Left = 457 + Height = 15 + Top = 38 + Width = 32 + Anchors = [akTop, akRight] + BorderSpacing.Left = 12 + BorderSpacing.Right = 8 + Caption = 'Depth' + Enabled = False + ParentColor = False + end + object seDepth: TSpinEdit + AnchorSideLeft.Control = seInnerRadius + AnchorSideTop.Control = seLabelAngle + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = Panel1 + AnchorSideRight.Side = asrBottom + Left = 497 + Height = 23 + Top = 34 + Width = 62 + Anchors = [akTop, akRight] + BorderSpacing.Right = 12 + Enabled = False + OnChange = seDepthChange + TabOrder = 6 + Value = 20 + end + object seDepthBrightnessDelta: TSpinEdit + AnchorSideLeft.Control = seInnerRadius + AnchorSideTop.Control = seDepth + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Panel1 + AnchorSideRight.Side = asrBottom + Left = 497 + Height = 23 + Top = 61 + Width = 62 + Anchors = [akTop, akRight] + BorderSpacing.Top = 4 + BorderSpacing.Right = 12 + Enabled = False + MaxValue = 255 + MinValue = -255 + OnChange = seDepthBrightnessDeltaChange + TabOrder = 7 + Value = -32 + end + object lblDepthBrightnessDelta: TLabel + AnchorSideLeft.Control = cbMarkPositions + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = seDepthBrightnessDelta + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = seDepthBrightnessDelta + Left = 398 + Height = 15 + Top = 65 + Width = 91 + Alignment = taRightJustify + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Right = 8 + Caption = 'Brightness delta' + Enabled = False + ParentColor = False + WordWrap = True + end end end object tsPolar: TTabSheet Caption = 'Polar' - ClientHeight = 405 - ClientWidth = 558 + ClientHeight = 454 + ClientWidth = 572 object ChartPolar: TChart Left = 0 - Height = 405 + Height = 454 Top = 0 - Width = 420 + Width = 457 AxisList = < item Marks.LabelBrush.Style = bsClear @@ -237,14 +336,14 @@ object Form1: TForm1 end end object pnlPolar: TPanel - Left = 420 - Height = 405 + Left = 457 + Height = 454 Top = 0 - Width = 138 + Width = 115 Align = alRight AutoSize = True - ClientHeight = 405 - ClientWidth = 138 + ClientHeight = 454 + ClientWidth = 115 TabOrder = 1 object cbCloseCircle: TCheckBox AnchorSideLeft.Control = pnlPolar @@ -296,7 +395,7 @@ object Form1: TForm1 Left = 9 Height = 17 Top = 117 - Width = 120 + Width = 97 BorderSpacing.Left = 8 BorderSpacing.Top = 4 BorderSpacing.Right = 8 @@ -324,23 +423,18 @@ object Form1: TForm1 end object ListChartSource1: TListChartSource DataPoints.Strings = ( - '0|1|?|' - '0|2|?|' - '0|3|?|' - '0|1|?|' - '0|2|?|' - '0|3|?|' - '0|1|?|' - '0|2|?|' - '0|3|?|' '0|5|?|' + '0|3|?|' + '0|1|?|' + '0.20000000000000001|0.20000000000000001|?|' + '0|1|?|' ) - left = 488 - top = 296 + left = 64 + top = 256 end object ChartToolset1: TChartToolset - left = 488 - top = 240 + left = 64 + top = 200 end object RandomChartSource1: TRandomChartSource PointsNumber = 15 @@ -350,7 +444,7 @@ object Form1: TForm1 YCount = 2 YMax = 10 YMin = 2 - left = 488 - top = 356 + left = 64 + top = 316 end end diff --git a/components/tachart/demo/radial/main.pas b/components/tachart/demo/radial/main.pas index 7172838fa3..91c3f290af 100644 --- a/components/tachart/demo/radial/main.pas +++ b/components/tachart/demo/radial/main.pas @@ -26,6 +26,11 @@ type cbShowPoints: TCheckBox; cbFilled: TCheckBox; Cb3D: TCheckBox; + seDepth: TSpinEdit; + seDepthBrightnessDelta: TSpinEdit; + lblInnerRadius: TLabel; + lblDepth: TLabel; + lblDepthBrightnessDelta: TLabel; lblTransparency: TLabel; lblWords: TLabel; lblLabelAngle: TLabel; @@ -37,6 +42,7 @@ type sbTransparency: TScrollBar; seWords: TSpinEdit; seLabelAngle: TSpinEdit; + seInnerRadius: TSpinEdit; tsPolar: TTabSheet; tsPie: TTabSheet; procedure cbCloseCircleChange(Sender: TObject); @@ -47,6 +53,9 @@ type Shift: TShiftState; X, Y: Integer); procedure cbShowPointsChange(Sender: TObject); procedure Cb3DChange(Sender: TObject); + procedure seDepthBrightnessDeltaChange(Sender: TObject); + procedure seDepthChange(Sender: TObject); + procedure seInnerRadiusChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure sbTransparencyChange(Sender: TObject); procedure seWordsChange(Sender: TObject); @@ -95,10 +104,30 @@ begin end; procedure TForm1.Cb3DChange(Sender: TObject); -var - DEPTH: array[boolean] of Integer = (0, 10); begin - ChartPiePieSeries1.Depth := DEPTH[Cb3D.Checked]; + if cb3D.Checked then + ChartPiePieSeries1.Depth := seDepth.Value + else + ChartPiePieSeries1.Depth := 0; + seDepth.Enabled := cb3D.Checked; + lblDepth.Enabled := cb3D.Checked; + seDepthBrightnessDelta.Enabled := cb3D.Checked; + lblDepthBrightnessDelta.Enabled := cb3D.Checked; +end; + +procedure TForm1.seDepthBrightnessDeltaChange(Sender: TObject); +begin + ChartPiePieSeries1.DepthBrightnessDelta := seDepthBrightnessDelta.Value; +end; + +procedure TForm1.seDepthChange(Sender: TObject); +begin + ChartPiePieSeries1.Depth := seDepth.Value; +end; + +procedure TForm1.seInnerRadiusChange(Sender: TObject); +begin + ChartPiePieSeries1.InnerRadiusPercent := seInnerRadius.Value; end; procedure TForm1.FormCreate(Sender: TObject); diff --git a/components/tachart/taradialseries.pas b/components/tachart/taradialseries.pas index 628401ace7..e363c60756 100644 --- a/components/tachart/taradialseries.pas +++ b/components/tachart/taradialseries.pas @@ -65,6 +65,7 @@ type FMarkDistancePercent: Boolean; FMarkPositions: TPieMarkPositions; FRadius: Integer; + FInnerRadiusPercent: Integer; FSlices: array of TPieSlice; private FEdgePen: TPen; @@ -75,14 +76,18 @@ type procedure SetEdgePen(AValue: TPen); procedure SetExploded(AValue: Boolean); procedure SetFixedRadius(AValue: TChartDistance); + procedure SetInnerRadiusPercent(AValue: Integer); procedure SetMarkDistancePercent(AValue: Boolean); procedure SetMarkPositions(AValue: TPieMarkPositions); procedure SetRotateLabels(AValue: Boolean); function SliceColor(AIndex: Integer): TColor; function TryRadius(ADrawer: IChartDrawer): TRect; protected + function CalcInnerRadius: Integer; inline; procedure GetLegendItems(AItems: TChartLegendItems); override; property Radius: Integer read FRadius; + property InnerRadiusPercent: Integer + read FInnerRadiusPercent write SetInnerRadiusPercent default 0; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -243,11 +248,17 @@ begin with TCustomPieSeries(ASource) do begin Self.FExploded := FExploded; Self.FFixedRadius := FFixedRadius; + Self.FInnerRadiusPercent := FInnerRadiusPercent; Self.FRotateLabels := FRotateLabels; end; inherited Assign(ASource); end; +function TCustomPieSeries.CalcInnerRadius: Integer; +begin + Result := Round(0.01 * FRadius * FInnerRadiusPercent); +end; + constructor TCustomPieSeries.Create(AOwner: TComponent); begin inherited Create(AOwner); @@ -275,6 +286,7 @@ const var ps: TPieSlice; scaled_depth: Integer; + innerRadius: Integer; function PrevSlice(ASlice: TPieSlice): TPieSlice; begin @@ -309,23 +321,42 @@ var (SliceExploded(ASlice) or SliceExploded(NextSlice(ASlice))); end; - procedure DrawArc3D(ASlice: TPieSlice); + procedure DrawArc3D(ASlice: TPieSlice; AInside: Boolean); var i, numSteps: Integer; a: Double; p: Array of TPoint; angle1, angle2: Double; clr: TColor; + r: Integer; + isVisible: Boolean; + outsideVisible: Boolean; + insideVisible: Boolean; begin - if InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4) and InRange(ASlice.FNextAngle, PI_3_4, PI_7_4) then + if AInside and (FInnerRadiusPercent = 0) then exit; - 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); - numSteps := Max(Round(TWO_PI * (angle2 - angle1) * FRadius / STEP), 2); + if AInside then + isVisible := (ASlice.FPrevAngle < PI_7_4) and (ASlice.FNextAngle > PI_3_4) + else + isVisible := not (InRange(ASlice.FPrevAngle, PI_3_4, PI_7_4) and + InRange(ASlice.FNextAngle, PI_3_4, PI_7_4) ); + 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); + 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); + end; + numSteps := Max(Round(TWO_PI * (angle2 - angle1) * r / STEP), 2); SetLength(p, 2 * numSteps + 1); for i := 0 to numSteps - 1 do begin a := WeightedAverage(angle1, angle2, i / (numSteps - 1)); - p[i] := ASlice.FBase + RotatePointX(FRadius, -a); + p[i] := ASlice.FBase + RotatePointX(r, -a); p[High(p) - i - 1] := p[i] + Point(scaled_depth, -scaled_depth); end; p[High(p)] := p[0]; @@ -337,12 +368,46 @@ var ADrawer.PolyLine(p, numSteps-1, numSteps+2); end; + procedure DrawVisibleArc3D(ASlice: TPieSlice); + begin + if ASlice.FVisible then begin + DrawArc3D(ASlice, false); + DrawArc3D(ASlice, true); + end; + end; + + procedure DrawPieRing(ASlice: TPieSlice); + var + i: Integer; + a, angle1, angle2: Double; + ni, no: Integer; + p: Array of TPoint; + begin + angle1 := ASlice.FPrevAngle; + 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); + for i := 0 to no - 1 do begin + a := WeightedAverage(angle1, angle2, i / (no - 1)); + p[i] := ASlice.FBase + 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); + end; + ADrawer.Polygon(p, 0, Length(p)); + end; + procedure DrawStartEdge3D(ASlice: TPieSlice); begin ADrawer.SetBrushParams( bsSolid, GetDepthColor(SliceColor(ASlice.FOrigIndex))); ADrawer.DrawLineDepth( - ASlice.FBase, ASlice.FBase + RotatePointX(FRadius, -ASlice.FPrevAngle), scaled_depth); + ASlice.FBase + RotatePointX(innerRadius, -ASlice.FPrevAngle), + ASlice.FBase + RotatePointX(FRadius, -ASlice.FPrevAngle), + scaled_depth + ); end; procedure DrawEndEdge3D(ASlice: TPieSlice); @@ -350,7 +415,10 @@ var ADrawer.SetBrushParams( bsSolid, GetDepthColor(SliceColor(ASlice.FOrigIndex))); ADrawer.DrawLineDepth( - ASlice.FBase, ASlice.FBase + RotatePointX(FRadius, -ASlice.FNextAngle), scaled_depth); + ASlice.FBase + RotatePointX(innerRadius, -ASlice.FNextAngle), + ASlice.FBase + RotatePointX(FRadius, -ASlice.FNextAngle), + scaled_depth + ); end; procedure FindLeftMostIndex(out AIndex: Integer); @@ -373,6 +441,7 @@ begin Marks.SetAdditionalAngle(0); Measure(ADrawer); + innerRadius := CalcInnerRadius; ADrawer.SetPen(EdgePen); if Depth > 0 then begin @@ -384,7 +453,8 @@ begin DrawStartEdge3D(FSlices[iL]); if EndEdgeVisible(FSlices[iL]) then DrawEndEdge3D(FSlices[iL]); - DrawArc3D(FSlices[iL]); + DrawArc3D(FSlices[iL], false); + DrawArc3D(FSlices[iL], true); end; for i:=iL+1 to High(FSlices) do @@ -393,7 +463,8 @@ begin DrawStartEdge3D(FSlices[i]); if EndEdgeVisible(FSlices[i]) then DrawEndEdge3D(FSlices[i]); - DrawArc3D(FSlices[i]); + DrawArc3D(FSlices[i], false); + DrawArc3d(FSlices[i], true); end; if iL <> 0 then @@ -407,17 +478,20 @@ begin // Draw arcs for i:= iL-1 downto 0 do - if FSlices[i].FVisible then DrawArc3D(FSlices[i]); + DrawVisibleArc3D(FSlices[i]); end; ADrawer.SetPen(EdgePen); for ps in FSlices do begin if not ps.FVisible then continue; ADrawer.SetBrushParams(bsSolid, SliceColor(ps.FOrigIndex)); - ADrawer.RadialPie( - ps.FBase.X - FRadius, ps.FBase.Y - FRadius, - ps.FBase.X + FRadius, ps.FBase.Y + FRadius, - RadToDeg16(ps.FPrevAngle), RadToDeg16(ps.Angle)); + if FInnerRadiusPercent = 0 then + ADrawer.RadialPie( + ps.FBase.X - FRadius, ps.FBase.Y - FRadius, + ps.FBase.X + FRadius, ps.FBase.Y + FRadius, + RadToDeg16(ps.FPrevAngle), RadToDeg16(ps.Angle)) + else + DrawPieRing(ps); end; if not Marks.IsMarkLabelsVisible then exit; @@ -437,16 +511,18 @@ var c: TPoint; pointAngle: Double; ps: TPieSlice; + innerRadius: Integer; begin 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 - (Sqr(c.X) + Sqr(c.Y) <= Sqr(FRadius)) + InRange(Sqr(c.X) + Sqr(c.Y), Sqr(innerRadius), Sqr(FRadius)) then exit(ps.FOrigIndex); end; @@ -569,6 +645,14 @@ begin UpdateParentChart; end; +procedure TCustomPieSeries.SetInnerRadiusPercent(AValue: Integer); +begin + AValue := EnsureRange(AValue, 0, 100); + if FInnerRadiusPercent = AValue then exit; + FInnerRadiusPercent := AValue; + UpdateParentChart; +end; + procedure TCustomPieSeries.SetMarkPositions(AValue: TPieMarkPositions); begin if FMarkPositions = AValue then exit; diff --git a/components/tachart/taseries.pas b/components/tachart/taseries.pas index 8f7d1b08cf..c41abf86a1 100644 --- a/components/tachart/taseries.pas +++ b/components/tachart/taseries.pas @@ -128,6 +128,7 @@ type property DepthBrightnessDelta; property Exploded; property FixedRadius; + property InnerRadiusPercent; property MarkDistancePercent; property MarkPositions; property Marks;