From 33a2c1d4532d46302b29e6893946ddc71b0173c0 Mon Sep 17 00:00:00 2001 From: maxim Date: Thu, 2 Apr 2015 22:03:21 +0000 Subject: [PATCH] Merged revision(s) 48496 #7b6b5c3ce1, 48501 #feb46ca59a, 48510-48511 #489724243b-#489724243b, 48531 #3459fff08c, 48534 #5fdf172812, 48594 #c95cc0db19 from trunk: TAChart: Fix arrows at axes and constant line series with respect to rtl bidi mode and line flips ........ TAChart: Fix centering of axis title for chart panes ........ TAChart: Fix paned charts to draw axis lines only in data range. ........ TAChart: Add new demo for paned charts (demo/panes-2) ........ TAChart: Fix TFuncSeries hanging in case of inverted x axis ........ TAChart: Fix panes to work with TFuncSeries. Update panes-2 demo. ........ TAChart: Fix ColorMapSeries painting failure in case of StepY=1 ........ git-svn-id: branches/fixes_1_4@48596 - --- .gitattributes | 4 + components/tachart/demo/panes-2/project1.lpi | 85 +++ components/tachart/demo/panes-2/project1.lpr | 21 + components/tachart/demo/panes-2/unit1.lfm | 603 +++++++++++++++++++ components/tachart/demo/panes-2/unit1.pas | 323 ++++++++++ components/tachart/tachartaxis.pas | 89 ++- components/tachart/tachartaxisutils.pas | 63 +- components/tachart/tacustomfuncseries.pas | 5 +- components/tachart/tacustomseries.pas | 24 + components/tachart/tafuncseries.pas | 4 +- components/tachart/tagraph.pas | 52 ++ components/tachart/taseries.pas | 7 + components/tachart/tatypes.pas | 5 + 13 files changed, 1231 insertions(+), 54 deletions(-) create mode 100644 components/tachart/demo/panes-2/project1.lpi create mode 100644 components/tachart/demo/panes-2/project1.lpr create mode 100644 components/tachart/demo/panes-2/unit1.lfm create mode 100644 components/tachart/demo/panes-2/unit1.pas diff --git a/.gitattributes b/.gitattributes index 4dbfc4e413..dbcc8fbb4e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3690,6 +3690,10 @@ components/tachart/demo/opengl/Main.lfm svneol=native#text/plain components/tachart/demo/opengl/Main.pas svneol=native#text/pascal components/tachart/demo/opengl/opengldemo.lpi svneol=native#text/plain components/tachart/demo/opengl/opengldemo.lpr svneol=native#text/pascal +components/tachart/demo/panes-2/project1.lpi svneol=native#text/pascal +components/tachart/demo/panes-2/project1.lpr svneol=native#text/pascal +components/tachart/demo/panes-2/unit1.lfm svneol=native#text/pascal +components/tachart/demo/panes-2/unit1.pas svneol=native#text/pascal components/tachart/demo/panes/Main.lfm svneol=native#text/plain components/tachart/demo/panes/Main.pas svneol=native#text/pascal components/tachart/demo/panes/panes.lpi svneol=native#text/plain diff --git a/components/tachart/demo/panes-2/project1.lpi b/components/tachart/demo/panes-2/project1.lpi new file mode 100644 index 0000000000..f6d455c2b0 --- /dev/null +++ b/components/tachart/demo/panes-2/project1.lpi @@ -0,0 +1,85 @@ + + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <Icon Value="0"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="2"> + <Item1> + <PackageName Value="TAChartLazarusPkg"/> + </Item1> + <Item2> + <PackageName Value="LCL"/> + </Item2> + </RequiredPackages> + <Units Count="2"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="unit1.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="Form1"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="Unit1"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="project1"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/components/tachart/demo/panes-2/project1.lpr b/components/tachart/demo/panes-2/project1.lpr new file mode 100644 index 0000000000..bb730b0b47 --- /dev/null +++ b/components/tachart/demo/panes-2/project1.lpr @@ -0,0 +1,21 @@ +program project1; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, Unit1, tachartlazaruspkg + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource:=True; + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. + diff --git a/components/tachart/demo/panes-2/unit1.lfm b/components/tachart/demo/panes-2/unit1.lfm new file mode 100644 index 0000000000..d73c75e08b --- /dev/null +++ b/components/tachart/demo/panes-2/unit1.lfm @@ -0,0 +1,603 @@ +object Form1: TForm1 + Left = 394 + Height = 492 + Top = 225 + Width = 881 + Caption = 'Form1' + ClientHeight = 492 + ClientWidth = 881 + OnCreate = FormCreate + LCLVersion = '1.5' + object Panel1: TPanel + Left = 0 + Height = 492 + Top = 0 + Width = 160 + Align = alLeft + ClientHeight = 492 + ClientWidth = 160 + TabOrder = 0 + object CbLineAtDataOnly: TCheckBox + Left = 12 + Height = 19 + Top = 10 + Width = 107 + Caption = 'Line at data only' + Checked = True + OnChange = CbLineAtDataOnlyChange + State = cbChecked + TabOrder = 0 + end + object CbShowHorGrid: TCheckBox + Left = 12 + Height = 19 + Top = 184 + Width = 62 + Caption = 'hor grid' + OnChange = CbShowGridChange + TabOrder = 1 + end + object CbShowArrow: TCheckBox + Left = 12 + Height = 19 + Top = 112 + Width = 52 + Caption = 'Arrow' + OnChange = CbShowArrowChange + TabOrder = 2 + end + object CbInverted: TCheckBox + Left = 12 + Height = 19 + Top = 136 + Width = 63 + Caption = 'Inverted' + OnChange = CbInvertedChange + TabOrder = 3 + end + object CbBiDiMode: TCheckBox + Left = 12 + Height = 19 + Top = 160 + Width = 83 + Caption = 'right-to-left' + OnChange = CbBiDiModeChange + TabOrder = 4 + end + object CbMarksAtDataOnly: TCheckBox + Left = 12 + Height = 19 + Top = 37 + Width = 117 + Caption = 'Marks at data only' + Checked = True + OnChange = CbMarksAtDataOnlyChange + State = cbChecked + TabOrder = 5 + end + object Label1: TLabel + Left = 12 + Height = 15 + Top = 256 + Width = 36 + Caption = 'Range:' + ParentColor = False + end + object CbUseMax: TCheckBox + Left = 12 + Height = 19 + Top = 272 + Width = 61 + Caption = 'UseMax' + OnChange = CbUseMaxChange + TabOrder = 6 + end + object CbUseMin: TCheckBox + Left = 12 + Height = 19 + Top = 296 + Width = 60 + Caption = 'UseMin' + OnChange = CbUseMinChange + TabOrder = 7 + end + object CbGrouped: TCheckBox + Left = 12 + Height = 19 + Top = 64 + Width = 66 + Caption = 'Grouped' + OnChange = CbGroupedChange + TabOrder = 8 + end + object CbShowFrame: TCheckBox + Left = 12 + Height = 19 + Top = 88 + Width = 53 + Caption = 'Frame' + Checked = True + OnChange = CbShowFrameChange + State = cbChecked + TabOrder = 9 + end + object CbShowVertGrid: TCheckBox + Left = 12 + Height = 19 + Top = 208 + Width = 64 + Caption = 'vert grid' + OnChange = CbShowGridChange + TabOrder = 10 + end + object Label2: TLabel + Left = 12 + Height = 51 + Top = 424 + Width = 135 + Anchors = [akTop, akLeft, akRight] + AutoSize = False + Caption = 'Drag the dashed lines...' + ParentColor = False + WordWrap = True + end + object CheckBox1: TCheckBox + Left = 12 + Height = 19 + Top = 336 + Width = 132 + Caption = 'TFuncSeries in center' + OnChange = CheckBox1Change + TabOrder = 11 + end + object LblXExtentIgnored: TLabel + Left = 12 + Height = 52 + Top = 356 + Width = 140 + AutoSize = False + Caption = '(x extent ignored - see docs)' + ParentColor = False + WordWrap = True + end + end + object PageControl1: TPageControl + Left = 160 + Height = 492 + Top = 0 + Width = 721 + ActivePage = TabSheet1 + Align = alClient + TabIndex = 0 + TabOrder = 1 + OnChange = PageControl1Change + OnChanging = PageControl1Changing + object TabSheet1: TTabSheet + Caption = 'Horizontal panes' + ClientHeight = 464 + ClientWidth = 713 + object Chart1: TChart + Left = 0 + Height = 464 + Top = 0 + Width = 713 + AxisList = < + item + Grid.Color = clGray + Grid.Visible = False + Alignment = calBottom + AxisPen.Visible = True + Minors = <> + Title.Visible = True + Title.Caption = 'x axis' + end + item + Grid.Color = clGray + Grid.Visible = False + Intervals.MaxLength = 40 + TickColor = clBlue + AtDataOnly = True + AxisPen.Color = clBlue + AxisPen.Visible = True + Marks.LabelFont.Color = clBlue + Marks.AtDataOnly = True + Minors = <> + Range.Max = 5 + Range.Min = -5 + Title.LabelFont.Color = clBlue + Title.LabelFont.Orientation = 900 + Title.Visible = True + Title.Caption = 'y1 axis' + Title.PositionOnMarks = True + Transformations = ChartAxisTransformations1 + end + item + Grid.Color = clGray + Grid.Visible = False + TickColor = clRed + AtDataOnly = True + AxisPen.Color = clRed + AxisPen.Visible = True + Marks.LabelFont.Color = clRed + Marks.AtDataOnly = True + Minors = <> + Range.Max = 20 + Title.LabelFont.Color = clRed + Title.LabelFont.Orientation = 900 + Title.Visible = True + Title.Caption = 'y2 axis' + Title.PositionOnMarks = True + Transformations = ChartAxisTransformations2 + end + item + Grid.Color = clGray + Grid.Visible = False + AtDataOnly = True + AxisPen.Color = clFuchsia + AxisPen.Visible = True + Marks.LabelFont.Color = clFuchsia + Marks.AtDataOnly = True + Minors = <> + Range.Max = 100 + Range.Min = -100 + Title.LabelFont.Color = clFuchsia + Title.LabelFont.Orientation = 900 + Title.Visible = True + Title.Caption = 'y3 axis' + Title.PositionOnMarks = True + Transformations = ChartAxisTransformations3 + end> + BackColor = clNone + Extent.UseYMax = True + Extent.UseYMin = True + Extent.YMax = 10 + Foot.Brush.Color = clBtnFace + Foot.Font.Color = clBlue + Title.Brush.Color = clBtnFace + Title.Font.Color = clBlue + Title.Text.Strings = ( + 'TAChart' + ) + Toolset = ChartToolset1 + Align = alClient + Color = clNone + ParentColor = False + object Chart1LineSeries1: TLineSeries + AxisIndexX = 0 + AxisIndexY = 1 + LinePen.Color = clBlue + Source = RandomChartSource11 + end + object Chart1LineSeries2: TLineSeries + AxisIndexX = 0 + AxisIndexY = 2 + LinePen.Color = clRed + Source = RandomChartSource12 + end + object Chart1LineSeries3: TLineSeries + AxisIndexX = 0 + AxisIndexY = 3 + LinePen.Color = clFuchsia + Source = RandomChartSource13 + end + object Chart1LineSeries4: TLineSeries + AxisIndexX = 0 + AxisIndexY = 3 + LinePen.Color = clFuchsia + LinePen.Style = psDot + Source = RandomChartSource14 + end + object Chart1ConstantLine1: TConstantLine + Pen.Color = clBlue + Pen.Style = psDash + Position = 2 + end + object Chart1ConstantLine2: TConstantLine + Pen.Color = clRed + Pen.Style = psDash + Position = 3 + end + object Chart1ConstantLine3: TConstantLine + Pen.Color = clRed + Pen.Style = psDash + Position = 5 + end + object Chart1ConstantLine4: TConstantLine + Pen.Color = clFuchsia + Pen.Style = psDash + Position = 6 + end + object Chart1FuncSeries2: TFuncSeries + Active = False + Extent.UseYMax = True + Extent.UseYMin = True + Extent.YMax = 1 + Extent.YMin = -1 + AxisIndexX = 0 + AxisIndexY = 2 + OnCalculate = Chart1FuncSeries2Calculate + ExtentAutoY = True + Pen.Color = clRed + Step = 1 + end + end + end + object TabSheet2: TTabSheet + Caption = 'Vertical panes' + ClientHeight = 464 + ClientWidth = 713 + object Chart2: TChart + Left = 0 + Height = 464 + Top = 0 + Width = 713 + AxisList = < + item + Grid.Color = clGray + Grid.Visible = False + AxisPen.Visible = True + Minors = <> + Title.LabelFont.Orientation = 900 + Title.Visible = True + Title.Caption = 'y axis' + end + item + Grid.Color = clGray + Grid.Visible = False + TickColor = clBlue + Alignment = calBottom + AtDataOnly = True + AxisPen.Color = clBlue + AxisPen.Visible = True + Marks.LabelFont.Color = clBlue + Marks.AtDataOnly = True + Minors = <> + Range.Max = 5 + Range.Min = -5 + Title.LabelFont.Color = clBlue + Title.Visible = True + Title.Caption = 'x1 axis' + Title.PositionOnMarks = True + Transformations = ChartAxisTransformations1 + end + item + Grid.Color = clGray + Grid.Visible = False + TickColor = clRed + Alignment = calBottom + AtDataOnly = True + AxisPen.Color = clRed + AxisPen.Visible = True + Marks.LabelFont.Color = clRed + Marks.AtDataOnly = True + Minors = <> + Range.Max = 20 + Title.LabelFont.Color = clRed + Title.Visible = True + Title.Caption = 'x2 axis' + Title.PositionOnMarks = True + Transformations = ChartAxisTransformations2 + end + item + Grid.Color = clGray + Grid.Visible = False + Alignment = calBottom + AtDataOnly = True + AxisPen.Color = clFuchsia + AxisPen.Visible = True + Marks.LabelFont.Color = clFuchsia + Marks.AtDataOnly = True + Minors = <> + Range.Max = 100 + Range.Min = -100 + Title.LabelFont.Color = clFuchsia + Title.Visible = True + Title.Caption = 'x3 axis' + Title.PositionOnMarks = True + Transformations = ChartAxisTransformations3 + end> + BackColor = clNone + Extent.UseXMax = True + Extent.UseXMin = True + Extent.XMax = 10 + Foot.Brush.Color = clBtnFace + Foot.Font.Color = clBlue + Title.Brush.Color = clBtnFace + Title.Font.Color = clBlue + Title.Text.Strings = ( + 'TAChart' + ) + Toolset = ChartToolset2 + Align = alClient + Color = clNone + ParentColor = False + object Chart2LineSeries1: TLineSeries + AxisIndexX = 1 + AxisIndexY = 0 + LinePen.Color = clBlue + Source = RandomChartSource21 + end + object Chart2LineSeries2: TLineSeries + AxisIndexX = 2 + AxisIndexY = 0 + LinePen.Color = clRed + Source = RandomChartSource22 + end + object Chart2LineSeries3: TLineSeries + AxisIndexX = 3 + AxisIndexY = 0 + LinePen.Color = clFuchsia + Source = RandomChartSource23 + end + object Chart2LineSeries4: TLineSeries + AxisIndexX = 3 + AxisIndexY = 0 + LinePen.Color = clFuchsia + LinePen.Style = psDot + Source = RandomChartSource24 + end + object Chart2ConstantLine1: TConstantLine + LineStyle = lsVertical + Pen.Color = clBlue + Pen.Style = psDash + Position = 2 + end + object Chart2ConstantLine2: TConstantLine + LineStyle = lsVertical + Pen.Color = clRed + Pen.Style = psDash + Position = 3 + end + object Chart2ConstantLine3: TConstantLine + LineStyle = lsVertical + Pen.Color = clRed + Pen.Style = psDash + Position = 5 + end + object Chart2ConstantLine4: TConstantLine + LineStyle = lsVertical + Pen.Color = clFuchsia + Pen.Style = psDash + Position = 6 + end + object Chart2FuncSeries2: TFuncSeries + Active = False + Extent.UseXMax = True + Extent.UseXMin = True + Extent.UseYMax = True + Extent.UseYMin = True + Extent.XMax = 1 + Extent.YMax = 1 + AxisIndexX = 2 + AxisIndexY = 0 + OnCalculate = Chart2FuncSeries2Calculate + Pen.Color = clRed + Step = 1 + end + end + end + end + object RandomChartSource11: TRandomChartSource + PointsNumber = 10 + RandSeed = 2088531169 + XMax = 1 + XMin = 0 + YMax = 1 + YMin = 0 + left = 200 + top = 48 + end + object RandomChartSource12: TRandomChartSource + PointsNumber = 10 + RandSeed = 2088635735 + XMax = 1 + XMin = 0 + YMax = 10 + YMin = 0 + left = 200 + top = 115 + end + object RandomChartSource13: TRandomChartSource + PointsNumber = 10 + RandSeed = 2088693796 + XMax = 1 + XMin = 0 + YMax = 10 + YMin = 0 + left = 200 + top = 188 + end + object ChartAxisTransformations1: TChartAxisTransformations + left = 776 + top = 48 + object ChartAxisTransformations1AutoScaleAxisTransform1: TAutoScaleAxisTransform + MaxValue = 2 + end + end + object ChartAxisTransformations2: TChartAxisTransformations + left = 776 + top = 115 + object ChartAxisTransformations2AutoScaleAxisTransform1: TAutoScaleAxisTransform + MaxValue = 5 + MinValue = 3 + end + end + object ChartAxisTransformations3: TChartAxisTransformations + left = 776 + top = 188 + object ChartAxisTransformations3AutoScaleAxisTransform1: TAutoScaleAxisTransform + MaxValue = 10 + MinValue = 6 + end + end + object RandomChartSource14: TRandomChartSource + PointsNumber = 5 + RandSeed = 999856828 + XMax = 1 + XMin = 0 + YMax = 100 + YMin = 0 + left = 200 + top = 256 + end + object RandomChartSource21: TRandomChartSource + PointsNumber = 10 + RandSeed = 2088531169 + XMax = 1 + XMin = 0 + YMax = 1 + YMin = 0 + left = 336 + top = 48 + end + object RandomChartSource22: TRandomChartSource + PointsNumber = 10 + RandSeed = 2088635735 + XMax = 10 + XMin = 0 + YMax = 1 + YMin = 0 + left = 336 + top = 115 + end + object RandomChartSource23: TRandomChartSource + PointsNumber = 10 + RandSeed = 2088693796 + XMax = 10 + XMin = 0 + YMax = 1 + YMin = 0 + left = 336 + top = 188 + end + object RandomChartSource24: TRandomChartSource + PointsNumber = 5 + RandSeed = 999856828 + XMax = 100 + XMin = 0 + YMax = 1 + YMin = 0 + left = 336 + top = 256 + end + object ChartToolset1: TChartToolset + left = 200 + top = 392 + object ChartToolset1DataPointDragTool1: TDataPointDragTool + Shift = [ssLeft] + OnAfterMouseMove = DataPointDragTool_AfterMouseMove + AffectedSeries = '4,5,6,7' + ActiveCursor = crSizeNS + end + end + object ChartToolset2: TChartToolset + left = 336 + top = 392 + object ChartToolset2DataPointDragTool1: TDataPointDragTool + Shift = [ssLeft] + OnAfterMouseMove = DataPointDragTool_AfterMouseMove + AffectedSeries = '4,5,6,7' + ActiveCursor = crSizeWE + end + end +end diff --git a/components/tachart/demo/panes-2/unit1.pas b/components/tachart/demo/panes-2/unit1.pas new file mode 100644 index 0000000000..73c4c726b7 --- /dev/null +++ b/components/tachart/demo/panes-2/unit1.pas @@ -0,0 +1,323 @@ +unit Unit1; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, TAGraph, TASources, TASeries, TATransformations, + Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, ComCtrls, types, + TACustomSeries, TATools, TAFuncSeries; + +type + + { TForm1 } + + TForm1 = class(TForm) + CbShowVertGrid: TCheckBox; + Chart1ConstantLine2: TConstantLine; + Chart1ConstantLine3: TConstantLine; + Chart1ConstantLine4: TConstantLine; + Chart1FuncSeries2: TFuncSeries; + Chart2ConstantLine1: TConstantLine; + Chart2ConstantLine2: TConstantLine; + Chart2ConstantLine3: TConstantLine; + Chart2ConstantLine4: TConstantLine; + Chart1ConstantLine1: TConstantLine; + Chart1LineSeries2: TLineSeries; + Chart1LineSeries3: TLineSeries; + Chart1LineSeries4: TLineSeries; + Chart2FuncSeries2: TFuncSeries; + Chart2LineSeries1: TLineSeries; + Chart2LineSeries2: TLineSeries; + Chart2LineSeries3: TLineSeries; + Chart2LineSeries4: TLineSeries; + Chart1LineSeries1: TLineSeries; + Chart2: TChart; + Chart1: TChart; + ChartAxisTransformations1: TChartAxisTransformations; + ChartAxisTransformations1AutoScaleAxisTransform1: TAutoScaleAxisTransform; + ChartAxisTransformations2: TChartAxisTransformations; + ChartAxisTransformations2AutoScaleAxisTransform1: TAutoScaleAxisTransform; + ChartAxisTransformations3: TChartAxisTransformations; + ChartAxisTransformations3AutoScaleAxisTransform1: TAutoScaleAxisTransform; + ChartToolset1: TChartToolset; + ChartToolset1DataPointDragTool1: TDataPointDragTool; + ChartToolset2: TChartToolset; + ChartToolset2DataPointDragTool1: TDataPointDragTool; + CbLineAtDataOnly: TCheckBox; + CbShowHorGrid: TCheckBox; + CbShowArrow: TCheckBox; + CbInverted: TCheckBox; + CbBiDiMode: TCheckBox; + CbMarksAtDataOnly: TCheckBox; + CbUseMax: TCheckBox; + CbUseMin: TCheckBox; + CbGrouped: TCheckBox; + CbShowFrame: TCheckBox; + CheckBox1: TCheckBox; + Label1: TLabel; + Label2: TLabel; + LblXExtentIgnored: TLabel; + PageControl1: TPageControl; + Panel1: TPanel; + RandomChartSource11: TRandomChartSource; + RandomChartSource12: TRandomChartSource; + RandomChartSource13: TRandomChartSource; + RandomChartSource14: TRandomChartSource; + RandomChartSource21: TRandomChartSource; + RandomChartSource22: TRandomChartSource; + RandomChartSource23: TRandomChartSource; + RandomChartSource24: TRandomChartSource; + TabSheet1: TTabSheet; + TabSheet2: TTabSheet; + procedure CbShowFrameChange(Sender: TObject); + procedure CbGroupedChange(Sender: TObject); + procedure CbUseMaxChange(Sender: TObject); + procedure CbUseMinChange(Sender: TObject); + procedure Chart1FuncSeries2Calculate(const AX: Double; out AY: Double); + procedure Chart2FuncSeries2Calculate(const AX: Double; out AY: Double); + procedure CheckBox1Change(Sender: TObject); + procedure DataPointDragTool_AfterMouseMove(ATool: TChartTool; APoint: TPoint); + procedure CbLineAtDataOnlyChange(Sender: TObject); + procedure CbShowGridChange(Sender: TObject); + procedure CbShowArrowChange(Sender: TObject); + procedure CbInvertedChange(Sender: TObject); + procedure CbBiDiModeChange(Sender: TObject); + procedure CbMarksAtDataOnlyChange(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure PageControl1Change(Sender: TObject); + procedure PageControl1Changing(Sender: TObject; var AllowChange: Boolean); + private + { private declarations } + public + { public declarations } + end; + +var + Form1: TForm1; + +implementation + +{$R *.lfm} + +uses + Math, TAChartUtils; + +{ TForm1 } + +procedure TForm1.CbLineAtDataOnlyChange(Sender: TObject); +var + i: Integer; +begin + for i:=1 to 3 do begin + Chart1.AxisList[i].AtDataOnly := CbLineAtDataOnly.Checked; + Chart2.AxisList[i].AtDataOnly := CbLineAtDataOnly.Checked; + end; +end; + +procedure TForm1.CbUseMaxChange(Sender: TObject); +var + i: Integer; +begin + for i:=1 to 3 do begin + Chart1.AxisList[i].Range.UseMax := CbUseMax.Checked; + Chart2.AxisList[i].Range.UseMax := CbUseMax.Checked; + end; +end; + +procedure TForm1.CbGroupedChange(Sender: TObject); +var + i: Integer; +begin + if CbGrouped.Checked then begin + for i:=1 to 3 do begin + Chart1.AxisList[i].Group := 1; + Chart2.AxisList[i].Group := 1; + end; + end else begin + for i:=1 to 3 do begin + Chart1.AxisList[i].Group := 0; + Chart2.AxisList[i].Group := 0; + end; + end; +end; + +procedure TForm1.CbShowFrameChange(Sender: TObject); +begin + Chart1.Frame.Visible := CbShowFrame.Checked; + Chart2.Frame.Visible := CbShowFrame.Checked; +end; + +procedure TForm1.CbUseMinChange(Sender: TObject); +var + i: Integer; +begin + for i:=1 to 3 do begin + Chart1.AxisList[i].Range.UseMin := CbUseMin.Checked; + Chart2.AxisList[i].Range.UseMin := CbUseMin.Checked; + end; +end; + +procedure TForm1.Chart1FuncSeries2Calculate(const AX: Double; out AY: Double); +begin + AY := sin(5 * AX); +end; + +procedure TForm1.Chart2FuncSeries2Calculate(const AX: Double; out AY: Double); +begin + AY := sin(5*AX)*0.5 + 0.5; +end; + +procedure TForm1.CheckBox1Change(Sender: TObject); +begin + Chart1FuncSeries2.Active := Checkbox1.Checked; + Chart1LineSeries2.Active := not Checkbox1.Checked; + Chart2FuncSeries2.Active := Checkbox1.Checked; + Chart2LineSeries2.Active := not Checkbox1.Checked; +end; + +// This code is used by both ChartToolSets, for Chart1 and Chart2. +procedure TForm1.DataPointDragTool_AfterMouseMove(ATool: TChartTool; + APoint: TPoint); +const + MIN_SIZE = 0.5; + MIN_DISTANCE = 0.1; +var + pos, prevPos, nextPos: Double; + ser: TConstantLine; + ex: TDoubleRect; + ls: array[1..4] of TConstantLine; +begin + UnUsed(APoint); + + ser := TConstantLine(TDataPointDragTool(ATool).Series); + if ser = nil then + exit; + + pos := ser.Position; + ex := ser.ParentChart.GetFullExtent; + + if ser.ParentChart = Chart1 then begin + ls[1] := Chart1ConstantLine1; + ls[2] := Chart1ConstantLine2; + ls[3] := Chart1ConstantLine3; + ls[4] := Chart1ConstantLine4; + end else begin + ls[1] := Chart2ConstantLine1; + ls[2] := Chart2ConstantLine2; + ls[3] := Chart2ConstantLine3; + ls[4] := Chart2ConstantLine4; + end; + + if ser = ls[1] then begin + prevPos := TDoublePointBoolArr(ex.a)[PageControl1.ActivepageIndex=0] + MIN_SIZE; + nextPos := ls[2].Position - MIN_DISTANCE; + ser.Position := EnsureRange(pos, prevPos, nextPos); + ChartAxisTransformations1AutoscaleAxisTransform1.MaxValue := ser.Position; + end else + if ser = ls[2] then begin + prevPos := ls[1].Position + MIN_DISTANCE; + nextPos := ls[3].Position - MIN_SIZE; + ser.Position := EnsureRange(pos, prevPos, nextPos); + ChartAxisTransformations2AutoscaleAxisTransform1.MinValue := ser.Position; + end else + if ser = ls[3] then begin + prevPos := ls[2].Position + MIN_SIZE; + nextPos := ls[4].Position - MIN_DISTANCE; + ser.Position := EnsureRange(pos, prevPos, nextPos); + ChartAxisTransformations2AutoscaleAxisTransform1.MaxValue := ser.Position; + end else + if ser = ls[4] then begin + prevPos := ls[3].Position + MIN_DISTANCE; + nextPos := TDoublePointBoolArr(ex.b)[PageControl1.ActivepageIndex=0] - MIN_SIZE; + ser.Position := EnsureRange(pos, prevPos, nextPos); + ChartAxisTransformations3AutoscaleAxisTransform1.MinValue := ser.Position; + end; +end; + +procedure TForm1.CbShowGridChange(Sender: TObject); +var + i: Integer; +begin + Chart1.AxisList[0].Grid.Visible := CbShowVertGrid.Checked; + Chart2.AxisList[0].Grid.Visible := cbShowHorGrid.Checked; + for i:=1 to 3 do begin + Chart1.AxisList[i].Grid.Visible := CbShowHorGrid.Checked; + Chart2.AxisList[i].Grid.Visible := CbShowVertGrid.Checked; + end; +end; + +procedure TForm1.CbShowArrowChange(Sender: TObject); +var + i: Integer; +begin + for i:=0 to 3 do begin + Chart1.AxisList[i].Arrow.Visible := CbShowArrow.Checked; + Chart2.AxisList[i].Arrow.Visible := CbShowArrow.Checked; + end; +end; + +procedure TForm1.CbInvertedChange(Sender: TObject); +var + i: Integer; +begin + for i:=0 to 3 do begin + Chart1.AxisList[i].Inverted := CbInverted.Checked; + Chart2.AxisList[i].Inverted := CbInverted.Checked; + end; +end; + +procedure TForm1.CbBiDiModeChange(Sender: TObject); +begin + if CbBiDiMode.Checked then + Chart1.BiDiMode := bdRightToLeft + else + Chart1.BiDiMode := bdLeftToRight; + Chart2.BiDiMode := Chart1.BiDiMode; +end; + +procedure TForm1.CbMarksAtDataOnlyChange(Sender: TObject); +var + i: Integer; +begin + for i:=0 to 3 do begin + Chart1.AxisList[i].Marks.AtDataOnly := CbMarksAtDataOnly.Checked; + Chart2.AxisList[i].Marks.AtDataOnly := CbMarksAtDataOnly.Checked; + end; +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin + PageControl1.ActivePageIndex := 0; +end; + +procedure TForm1.PageControl1Change(Sender: TObject); +begin + LblXExtentIgnored.Visible := PageControl1.ActivePageIndex = 1; +end; + +{ The autoscale transformations are shared between both pages. Before changing + to the new page we make sure that the constant lines series which indicate the + pane limits are at the correct position. } +procedure TForm1.PageControl1Changing(Sender: TObject; var AllowChange: Boolean); +begin + UnUsed(AllowChange); + case PageControl1.ActivePageIndex of + 0: begin + Chart2ConstantLine1.Position := ChartAxisTransformations1AutoScaleAxisTransform1.Maxvalue; + Chart2ConstantLine2.Position := ChartAxisTransformations2AutoScaleAxisTransform1.MinValue; + Chart2ConstantLine3.Position := ChartAxisTransformations2AutoScaleAxisTransform1.Maxvalue; + Chart2ConstantLine4.Position := ChartAxisTransformations3AutoScaleAxisTransform1.Minvalue; + end; + 1: begin + Chart1ConstantLine1.Position := ChartAxisTransformations1AutoScaleAxisTransform1.Maxvalue; + Chart1ConstantLine2.Position := ChartAxisTransformations2AutoScaleAxisTransform1.MinValue; + Chart1ConstantLine3.Position := ChartAxisTransformations2AutoScaleAxisTransform1.Maxvalue; + Chart1ConstantLine4.Position := ChartAxisTransformations3AutoScaleAxisTransform1.Minvalue; + end; + end; +end; + +end. + diff --git a/components/tachart/tachartaxis.pas b/components/tachart/tachartaxis.pas index e333f026cd..2b52336084 100644 --- a/components/tachart/tachartaxis.pas +++ b/components/tachart/tachartaxis.pas @@ -87,7 +87,7 @@ type FMarkValues: TChartValueTextArray; FTitlePos: Integer; procedure GetMarkValues; - procedure VisitSource(ASource: TCustomChartSource; var AData); +// procedure VisitSource(ASource: TCustomChartSource; var AData); private FAxisRect: TRect; FGroupIndex: Integer; @@ -95,6 +95,7 @@ type function MakeValuesInRangeParams(AMin, AMax: Double): TValuesInRangeParams; strict private FAlignment: TChartAxisAlignment; + FAtDataOnly: Boolean; FAxisPen: TChartAxisPen; FGroup: Integer; FHelper: TAxisDrawHelper; @@ -115,6 +116,7 @@ type function GetValue(AIndex: Integer): TChartValueText; inline; function GetValueCount: Integer; inline; function PositionIsStored: Boolean; + procedure SetAtDataOnly(AValue: Boolean); procedure SetAxisPen(AValue: TChartAxisPen); procedure SetGroup(AValue: Integer); procedure SetInverted(AValue: Boolean); @@ -147,6 +149,7 @@ type function GetChart: TCustomChart; inline; function GetTransform: TChartAxisTransformations; function IsDefaultPosition: Boolean; + function IsFlipped: Boolean; override; function IsPointInside(const APoint: TPoint): Boolean; function IsVertical: Boolean; inline; procedure Measure( @@ -163,6 +166,7 @@ type published property Alignment default calLeft; property Arrow; + property AtDataOnly: Boolean read FAtDataOnly write SetAtDataOnly default false; property AxisPen: TChartAxisPen read FAxisPen write SetAxisPen; property Group: Integer read FGroup write SetGroup default 0; // Inverts the axis scale from increasing to decreasing. @@ -551,7 +555,7 @@ const INVERTED_NAME: array [Boolean] of String = ('', ' Inverted'); begin Result := - SIDE_NAME[Alignment] + VISIBLE_NAME[Visible] + INVERTED_NAME[Inverted] + + SIDE_NAME[Alignment] + VISIBLE_NAME[Visible] + INVERTED_NAME[IsFlipped] + FormatIfNotEmpty(' (%s)', Title.Caption); end; @@ -564,9 +568,9 @@ procedure TChartAxis.GetMarkValues; var i: Integer; d: TValuesInRangeParams; - vis: TChartOnVisitSources; - t: TChartValueText; +// vis: TChartOnVisitSources; axisMin, axisMax: Double; + rng: TDoubleInterval; begin with FHelper do begin axisMin := GetTransform.GraphToAxis(FValueMin); @@ -576,25 +580,27 @@ begin Marks.Range.Intersect(axisMin, axisMax); d := MakeValuesInRangeParams(axisMin, axisMax); SetLength(FMarkValues, 0); - vis := TChartAxisList(Collection).OnVisitSources; - if Marks.AtDataOnly and Assigned(vis) then begin - vis(@VisitSource, Self, d); +// vis := TChartAxisList(Collection).OnVisitSources; + if Marks.AtDataOnly {and Assigned(vis)} then begin + // vis(@VisitSource, Self, d); // FIXME: Intersect axisMin/Max with the union of series extents. - end - else - Marks.SourceDef.ValuesInRange(d, FMarkValues); + // wp: - I think this is fixed in what follows... + GetChart.Notify(CMD_QUERY_SERIESEXTENT, self, nil, rng{%H-}); + UpdateBounds(rng.FStart, rng.FEnd); + d.FMin := rng.FStart; + d.FMax := rng.FEnd; + if IsNaN(d.FMin) or IsNaN(d.FMax) then begin + d.FMax := 1.0; d.FMin := 0.0; + end else + if (d.FMin = d.FMax) then d.FMax := d.FMin + 1.0; + end; + Marks.SourceDef.ValuesInRange(d, FMarkValues); with FHelper do begin FValueMin := GetTransform.AxisToGraph(axisMin); FValueMax := GetTransform.AxisToGraph(axisMax); - FMinForMarks := GetTransform.AxisToGraph(d.FMin); - FMaxForMarks := GetTransform.AxisToGraph(d.FMax); + FMinForMarks := Min(FMinForMarks, GetTransform.AxisToGraph(d.FMin)); + FMaxForMarks := Max(FMaxForMarks, GetTransform.AxisToGraph(d.FMax)); end; - if Inverted and (Length(FMarkValues) > 0) then - for i := 0 to High(FMarkValues) div 2 do begin - t := FMarkValues[i]; - FMarkValues[i] := FMarkValues[High(FMarkValues) - i]; - FMarkValues[High(FMarkValues) - i] := t; - end; if Assigned(FOnMarkToText) then for i := 0 to High(FMarkValues) do @@ -623,6 +629,18 @@ begin Result := (PositionUnits = cuPercent) and (Position = 0); end; +function TChartAxis.IsFlipped: Boolean; +{ Returns drawing direction of the axis: + FALSE - left to right, or bottom to tip + TRUE - right to left, or top to bottom } +begin + Result := FInverted; + if (FAlignment in [calBottom, calTop]) and + (GetChart.BiDiMode <> bdLeftToRight) + then + Result := not Result; +end; + function TChartAxis.IsPointInside(const APoint: TPoint): Boolean; begin Result := PtInRect(FTitleRect, APoint) and not PtInRect(FAxisRect, APoint); @@ -737,15 +755,19 @@ begin end; if not Title.PositionOnMarks then FTitlePos := (rmin + rmax) div 2 - else if minc < MaxInt then + else if minc < MaxInt then begin + c := FHelper.GraphToImage(FHelper.FMaxForMarks); + if c < maxc then maxc := c; + c := FHelper.GraphToImage(FHelper.FMinForMarks); + if c > minc then minc := c; FTitlePos := (maxc + minc) div 2 - else + end else FTitlePos := MaxInt; if Arrow.Visible then with AMeasureData do begin FSize := Max(d.Scale(Arrow.Width), FSize); - if Arrow.Inverted then + if IsFlipped then FFirstMark := Max(d.Scale(Arrow.Length), FFirstMark) else FLastMark := Max(d.Scale(Arrow.Length), FLastMark); @@ -797,6 +819,9 @@ begin FHelper.FTransf := ATransf; FHelper.FZOffset.Y := Min(ZPosition, AMaxZPosition); FHelper.FZOffset.X := -FHelper.FZOffset.Y; + FHelper.FAtDataOnly := AtDataOnly; + FHelper.FMaxForMarks := -infinity; + FHelper.FMinForMarks := infinity; end; procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment); @@ -806,6 +831,13 @@ begin StyleChanged(Self); end; +procedure TChartAxis.SetAtDataOnly(AValue: Boolean); +begin + if FAtDataOnly = AValue then exit; + FAtDataOnly := AValue; + StyleChanged(self); +end; + procedure TChartAxis.SetAxisPen(AValue: TChartAxisPen); begin FAxisPen.Assign(AValue); @@ -823,6 +855,7 @@ procedure TChartAxis.SetInverted(AValue: Boolean); begin if FInverted = AValue then exit; FInverted := AValue; + if Arrow <> nil then Arrow.Inverted := IsFlipped; StyleChanged(Self); end; @@ -936,12 +969,8 @@ begin Alignment := calLeft; Title.LabelFont.Orientation := -Title.LabelFont.Orientation; end; - calBottom, - calTop: - begin - Inverted := not Inverted; - if Arrow <> nil then Arrow.Inverted := not Arrow.Inverted; - end; + calBottom, calTop: + if Arrow <> nil then Arrow.Inverted := IsFlipped; end; end; @@ -955,6 +984,7 @@ begin end; end; +{ procedure TChartAxis.VisitSource(ASource: TCustomChartSource; var AData); var ext: TDoubleRect; @@ -966,6 +996,7 @@ begin p.FMax := Min(TDoublePointBoolArr(ext.b)[IsVertical], p.FMax); Marks.SourceDef.ValuesInRange(p, FMarkValues); end; +} { TChartAxisList } @@ -1183,7 +1214,7 @@ end; function TAxisCoeffHelper.CalcScale(ASign: Integer): Double; begin if (FMax^ = FMin^) or (Sign(FHi - FLo) <> ASign) then exit(1.0); - if (FAxis <> nil) and FAxis.Inverted then + if (FAxis <> nil) and FAxis.IsFlipped then Exchange(FLo, FHi); Result := (FHi - FLo) / (FMax^ - FMin^); end; @@ -1197,7 +1228,7 @@ procedure TAxisCoeffHelper.UpdateMinMax(AConv: TAxisConvFunc); begin FMin^ := AConv(FImageLo); FMax^ := AConv(FImageHi); - if (FAxis <> nil) and FAxis.Inverted then + if (FAxis <> nil) and FAxis.IsFlipped then Exchange(FMin^, FMax^); end; diff --git a/components/tachart/tachartaxisutils.pas b/components/tachart/tachartaxisutils.pas index bec23396a8..f8b381ba01 100644 --- a/components/tachart/tachartaxisutils.pas +++ b/components/tachart/tachartaxisutils.pas @@ -172,6 +172,7 @@ type destructor Destroy; override; public procedure Assign(ASource: TPersistent); override; + function IsFlipped: Boolean; virtual; function TryApplyStripes( ADrawer: IChartDrawer; var AIndex: Cardinal): Boolean; @@ -207,6 +208,7 @@ type procedure LineZ(AP1, AP2: TPoint); inline; function TryApplyStripes: Boolean; inline; public + FAtDataOnly: Boolean; FAxis: TChartBasicAxis; FAxisTransf: TTransformFunc; FClipRangeDelta: Integer; @@ -349,16 +351,28 @@ end; procedure TAxisDrawHelper.InternalAxisLine( APen: TChartPen; const AStart, AEnd: TPoint; AAngle: Double); +var + arrowBase: TPoint; + arrowFlipped: boolean; begin if not APen.Visible and not FAxis.Arrow.Visible then exit; FDrawer.Pen := APen; if APen.Visible then LineZ(AStart, AEnd); - if FAxis.Arrow.Visible then - if FAxis.Arrow.Inverted then - FAxis.Arrow.Draw(FDrawer, AStart - FZOffset, AAngle, APen) - else - FAxis.Arrow.Draw(FDrawer, AEnd + FZOffset, AAngle, APen); + if FAxis.Arrow.Visible then begin + arrowFlipped := FAxis.IsFlipped; + if arrowFlipped <> FAxis.Arrow.Inverted then arrowFlipped := not arrowFlipped; + if FAxis.IsFlipped then begin + arrowBase := AStart - FZOffset; + if not arrowFlipped then + arrowBase -= RotatePointX(-FDrawer.Scale(FAxis.Arrow.Length), AAngle); + end else begin + arrowBase := AEnd + FZOffset; + if arrowFlipped then + arrowBase += RotatePointX(-FDrawer.Scale(FAxis.Arrow.Length), AAngle); + end; + FAxis.Arrow.Draw(FDrawer, arrowBase, AAngle, APen) + end; end; function TAxisDrawHelper.IsInClipRange(ACoord: Integer): Boolean; @@ -389,19 +403,20 @@ end; procedure TAxisDrawHelperX.DrawAxisLine(APen: TChartPen; AFixedCoord: Integer); var - p: TPoint; + p1, p2: TPoint; begin - if FAxis.Arrow.Inverted then begin - p := Point(FClipRect^.Left, AFixedCoord); + if FAxis.IsFlipped then begin + p1 := Point(IfThen(FAtDataOnly, GraphToImage(FMaxForMarks), FClipRect^.Left), AFixedCoord); + p2 := Point(IfThen(FAtDataOnly, GraphToImage(FMinForMarks), FClipRect^.Right), AFixedCoord); if FAxis.Arrow.Visible then - p.X -= FDrawer.Scale(FAxis.Arrow.Length); - InternalAxisLine(APen, p, Point(FClipRect^.Right, AFixedCoord), 0); + p1.X -= FDrawer.Scale(FAxis.Arrow.Length); end else begin - p := Point(FClipRect^.Right, AFixedCoord); + p1 := Point(IfThen(FAtDataOnly, GraphToImage(FMinForMarks), FClipRect^.Left), AFixedCoord); + p2 := Point(IfThen(FAtDataOnly, GraphToImage(FMaxForMarks), FClipRect^.Right), AFixedCoord); if FAxis.Arrow.Visible then - p.X += FDrawer.Scale(FAxis.Arrow.Length); - InternalAxisLine(APen, Point(FClipRect^.Left, AFixedCoord), p, 0); + p2.X += FDrawer.Scale(FAxis.Arrow.Length); end; + InternalAxisLine(APen, p1, p2, 0); end; procedure TAxisDrawHelperX.DrawLabelAndTick( @@ -454,19 +469,20 @@ end; procedure TAxisDrawHelperY.DrawAxisLine(APen: TChartPen; AFixedCoord: Integer); var - p: TPoint; + p1, p2: TPoint; begin - if FAxis.Arrow.Inverted then begin - p := Point(AFixedCoord, FClipRect^.Bottom); + if FAxis.IsFlipped then begin + p1 := Point(AFixedCoord, IfThen(FAtDataOnly, GraphToImage(FMaxForMarks), FClipRect^.Bottom)); + p2 := Point(AFixedCoord, IfThen(FAtDataOnly, GraphToImage(FMinForMarks), FClipRect^.Top)); if FAxis.Arrow.Visible then - p.Y += FDrawer.Scale(FAxis.Arrow.Length); - InternalAxisLine(APen, p, Point(AFixedCoord, FClipRect^.Top), -Pi / 2); + p1.Y += FDrawer.Scale(FAxis.Arrow.Length); end else begin - p := Point(AFixedCoord, FClipRect^.Top); + p1 := Point(AFixedCoord, IfThen(FAtDataOnly, GraphToImage(FMinForMarks), FClipRect^.Bottom)); + p2 := Point(AFixedCoord, IfThen(FAtDataOnly, GraphToImage(FMaxForMarks), FClipRect^.Top)); if FAxis.Arrow.Visible then - p.Y -= FDrawer.Scale(FAxis.Arrow.Length); - InternalAxisLine(APen, Point(AFixedCoord, FClipRect^.Bottom), p, -Pi / 2); + p2.Y -= FDrawer.Scale(FAxis.Arrow.Length); end; + InternalAxisLine(APen, p1, p2, -Pi / 2); end; procedure TAxisDrawHelperY.DrawLabelAndTick( @@ -696,6 +712,11 @@ begin Result := Marks.DefaultSource.Params; end; +function TChartBasicAxis.IsFlipped: Boolean; +begin + Result := false; +end; + procedure TChartBasicAxis.SetArrow(AValue: TChartArrow); begin FArrow.Assign(AValue); diff --git a/components/tachart/tacustomfuncseries.pas b/components/tachart/tacustomfuncseries.pas index 19d8fe6b3e..7a5d362213 100644 --- a/components/tachart/tacustomfuncseries.pas +++ b/components/tachart/tacustomfuncseries.pas @@ -171,7 +171,7 @@ procedure TDrawFuncHelper.ForEachPoint( AXg, AXMax: Double; AOnMoveTo, AOnLineTo: TOnPoint); var hint: Integer; - xa, xg1, xa1: Double; + xa, xg1, xa1, dx: Double; begin if FGraphStep = 0 then exit; @@ -183,8 +183,9 @@ begin if AXg < AXMax then AOnMoveTo(AXg, xa); + dx := abs(FGraphStep); while AXg < AXMax do begin - xg1 := AXg + FGraphStep; + xg1 := AXg + dx; xa1 := FGraphToAxisXr(xg1); if FDomainExclusions.Intersect(xa, xa1, hint) then begin AOnLineTo(FAxisToGraphXr(xa), xa); diff --git a/components/tachart/tacustomseries.pas b/components/tachart/tacustomseries.pas index 5f611b1e86..628a760348 100644 --- a/components/tachart/tacustomseries.pas +++ b/components/tachart/tacustomseries.pas @@ -87,6 +87,7 @@ type function AxisToGraphY(AY: Double): Double; override; function GetAxisX: TChartAxis; function GetAxisY: TChartAxis; + function GetAxisBounds(AAxis: TChartAxis; out AMin, AMax: Double): Boolean; override; function GetGraphBounds: TDoubleRect; override; function GraphToAxis(APoint: TDoublePoint): TDoublePoint; function GraphToAxisX(AX: Double): Double; override; @@ -355,6 +356,29 @@ begin inherited; end; +function TCustomChartSeries.GetAxisBounds(AAxis: TChartAxis; + out AMin, AMax: Double): Boolean; +var + ex: TDoubleRect; +begin + if (AAxis.Index = AxisIndexX) or (AAxis.Index = AxisIndexY) then begin + ex := EmptyExtent; + GetBounds(ex); + with ex do begin + UpdateBoundsByAxisRange(FChart.AxisList, AxisIndexX, a.X, b.X); + UpdateBoundsByAxisRange(FChart.AxisList, AxisIndexY, a.Y, b.Y); + if IsRotated then begin + Exchange(a.X, a.Y); + Exchange(b.X, b.Y); + end; + end; + AMin := TDoublePointBoolArr(ex.a)[AAxis.IsVertical]; + AMax := TDoublePointBoolArr(ex.b)[AAxis.IsVertical]; + Result := true; + end else + Result := false; +end; + function TCustomChartSeries.GetAxisX: TChartAxis; begin if InRange(AxisIndexX, 0, FChart.AxisList.Count - 1) then diff --git a/components/tachart/tafuncseries.pas b/components/tachart/tafuncseries.pas index 33e99e7cdd..ff5c3c1419 100644 --- a/components/tachart/tafuncseries.pas +++ b/components/tachart/tafuncseries.pas @@ -1708,8 +1708,8 @@ begin ADrawer.SetPenParams(psClear, clTAColor); end; - if StepX > 1 then scaled_stepX := Max(1, ADrawer.Scale(StepX)); - if StepY > 1 then scaled_stepY := Max(1, ADrawer.Scale(StepY)); + scaled_stepX := IfThen(StepX > 1, Max(1, ADrawer.Scale(StepX)), 1); + scaled_stepY := IfThen(StepY > 1, Max(1, ADrawer.Scale(StepY)), 1); try pt.Y := (r.Top div scaled_stepY - 1) * scaled_stepY + offset.Y mod scaled_stepY; diff --git a/components/tachart/tagraph.pas b/components/tachart/tagraph.pas index 055f2da65c..116fc56805 100644 --- a/components/tachart/tagraph.pas +++ b/components/tachart/tagraph.pas @@ -78,10 +78,12 @@ type public procedure Draw(ADrawer: IChartDrawer); virtual; abstract; + function GetAxisBounds(AAxis: TChartAxis; out AMin, AMax: Double): boolean; virtual; abstract; function GetGraphBounds: TDoubleRect; virtual; abstract; function IsEmpty: Boolean; virtual; abstract; procedure MovePoint(var AIndex: Integer; const ANewPos: TPoint); overload; inline; procedure MovePoint(var AIndex: Integer; const ANewPos: TDoublePoint); overload; virtual; + procedure UpdateBiDiMode; virtual; property Active: Boolean read FActive write SetActive default true; property Depth: TChartDistance read FDepth write SetDepth default 0; @@ -139,6 +141,7 @@ type procedure Clear; function Count: Integer; function GetEnumerator: TBasicChartSeriesEnumerator; + procedure UpdateBiDiMode; public property Items[AIndex: Integer]: TBasicChartSeries read GetItem; default; property List: TIndexedComponentList read FList; @@ -274,6 +277,7 @@ type procedure MouseUp( AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override; protected + function GetAxisBounds(AAxis: TChartAxis): TDoubleInterval; function GetAxisByAlign(AAlign: TChartAxisAlignment): TChartAxis; procedure SetAxisByAlign(AAlign: TChartAxisAlignment; AValue: TChartAxis); inline; protected @@ -318,6 +322,7 @@ type procedure EnableRedrawing; function GetFullExtent: TDoubleRect; function GetLegendItems(AIncludeHidden: Boolean = false): TChartLegendItems; + procedure Notify(ACommand: Integer; AParam1, AParam2: Pointer; var AData); override; procedure PaintOnAuxCanvas(ACanvas: TCanvas; ARect: TRect); procedure PaintOnCanvas(ACanvas: TCanvas; ARect: TRect); procedure Prepare; @@ -984,8 +989,27 @@ begin AClass := nil; end; +function TChart.GetAxisBounds(AAxis: TChartAxis): TDoubleInterval; +var + s: TBasicChartSeries; + mn, mx: Double; +begin + Result.FStart := Infinity; + Result.FEnd := NegInfinity; + for s in Series do + if s.Active and s.GetAxisBounds(AAxis, mn, mx) then begin + Result.FStart := Min(Result.FStart, mn); + Result.FEnd := Max(Result.FEnd, mx); + end; +end; + function TChart.GetAxisByAlign(AAlign: TChartAxisAlignment): TChartAxis; begin + if (BidiMode <> bdLeftToRight) then + case AAlign of + calLeft: AAlign := calRight; + calRight: AAlign := calLeft; + end; Result := FAxisList.GetAxisByAlign(AAlign); end; @@ -1231,6 +1255,20 @@ begin inherited Notification(AComponent, AOperation); end; +{ Notifies the chart of something which is specified by ACommand and both + parameters. Needed for example by the axis to query the extent covered by + all series using this axis (cannot be called directly because TAChartAxis + does not "use" TACustomSeries. } +procedure TChart.Notify(ACommand: Integer; AParam1, AParam2: Pointer; var AData); +begin + UnUsed(AParam2); + case ACommand of + CMD_QUERY_SERIESEXTENT: + TDoubleInterval(AData) := GetAxisBounds(TChartAxis(AParam1)); + end; +end; + + procedure TChart.Paint; var defaultDrawing: Boolean = true; @@ -1418,6 +1456,7 @@ begin Legend.UpdateBidiMode; Title.UpdateBidiMode; Foot.UpdateBidiMode; + Series.UpdateBiDiMode; end; end; @@ -1749,6 +1788,11 @@ begin MovePoint(AIndex, FChart.ImageToGraph(ANewPos)); end; +procedure TBasicChartSeries.UpdateBiDiMode; +begin + // normally nothing to do. Override, e.g., to flip arrows +end; + procedure TBasicChartSeries.UpdateMargins( ADrawer: IChartDrawer; var AMargins: TRect); begin @@ -1804,6 +1848,14 @@ begin Result := TBasicChartSeries(FList.Items[AIndex]); end; +procedure TChartSeriesList.UpdateBiDiMode; +var + s: TBasicChartseries; +begin + for s in self do + s.UpdateBiDiMode; +end; + { TBasicChartTool } procedure TBasicChartTool.Activate; diff --git a/components/tachart/taseries.pas b/components/tachart/taseries.pas index 5730185d93..23c382e87a 100644 --- a/components/tachart/taseries.pas +++ b/components/tachart/taseries.pas @@ -289,6 +289,7 @@ type out AResults: TNearestPointResults): Boolean; override; function IsEmpty: Boolean; override; procedure MovePoint(var AIndex: Integer; const ANewPos: TDoublePoint); override; + procedure UpdateBiDiMode; override; published property Active default true; @@ -894,6 +895,12 @@ begin UpdateParentChart; end; +procedure TConstantLine.UpdateBiDiMode; +begin + if LineStyle = lsHorizontal then + Arrow.Inverted := not Arrow.Inverted; +end; + { TBarSeries } procedure TBarSeries.Assign(ASource: TPersistent); diff --git a/components/tachart/tatypes.pas b/components/tachart/tatypes.pas index 6b400b887c..2fc06cef69 100644 --- a/components/tachart/tatypes.pas +++ b/components/tachart/tatypes.pas @@ -35,9 +35,14 @@ const DEF_SHADOW_OFFSET = 8; DEF_SHADOW_TRANSPARENCY = 128; + // Constants for Chart.Notify commands + CMD_QUERY_SERIESEXTENT = 0; + + type TCustomChart = class(TCustomControl) public + procedure Notify(ACommand: Integer; AParam1, AParam2: Pointer; var Data); virtual; abstract; procedure StyleChanged(Sender: TObject); virtual; abstract; procedure ZoomFull(AImmediateRecalc: Boolean = false); virtual; abstract; end;