~ubuntu-branches/ubuntu/saucy/lazarus/saucy

« back to all changes in this revision

Viewing changes to components/tachart/tasources.pas

  • Committer: Package Import Robot
  • Author(s): Paul Gevers, Abou Al Montacir, Bart Martens, Paul Gevers
  • Date: 2013-06-08 14:12:17 UTC
  • mfrom: (1.1.9)
  • Revision ID: package-import@ubuntu.com-20130608141217-7k0cy9id8ifcnutc
Tags: 1.0.8+dfsg-1
[ Abou Al Montacir ]
* New upstream major release and multiple maintenace release offering many
  fixes and new features marking a new milestone for the Lazarus development
  and its stability level.
  - The detailed list of changes can be found here:
    http://wiki.lazarus.freepascal.org/Lazarus_1.0_release_notes
    http://wiki.lazarus.freepascal.org/Lazarus_1.0_fixes_branch
* LCL changes:
  - LCL is now a normal package.
      + Platform independent parts of the LCL are now in the package LCLBase
      + LCL is automatically recompiled when switching the target platform,
        unless pre-compiled binaries for this target are already installed.
      + No impact on existing projects.
      + Linker options needed by LCL are no more added to projects that do
        not use the LCL package.
  - Minor changes in LCL basic classes behaviour
      + TCustomForm.Create raises an exception if a form resource is not
        found.
      + TNotebook and TPage: a new implementation of these classes was added.
      + TDBNavigator: It is now possible to have focusable buttons by setting
        Options = [navFocusableButtons] and TabStop = True, useful for
        accessibility and for devices with neither mouse nor touch screen.
      + Names of TControlBorderSpacing.GetSideSpace and GetSpace were swapped
        and are now consistent. GetSideSpace = Around + GetSpace.
      + TForm.WindowState=wsFullscreen was added
      + TCanvas.TextFitInfo was added to calculate how many characters will
        fit into a specified Width. Useful for word-wrapping calculations.
      + TControl.GetColorResolvingParent and
        TControl.GetRGBColorResolvingParent were added, simplifying the work
        to obtain the final color of the control while resolving clDefault
        and the ParentColor.
      + LCLIntf.GetTextExtentExPoint now has a good default implementation
        which works in any platform not providing a specific implementation.
        However, Widgetset specific implementation is better, when available.
      + TTabControl was reorganized. Now it has the correct class hierarchy
        and inherits from TCustomTabControl as it should.
  - New unit in the LCL:
      + lazdialogs.pas: adds non-native versions of various native dialogs,
        for example TLazOpenDialog, TLazSaveDialog, TLazSelectDirectoryDialog.
        It is used by widgetsets which either do not have a native dialog, or
        do not wish to use it because it is limited. These dialogs can also be
        used by user applications directly.
      + lazdeviceapis.pas: offers an interface to more hardware devices such
        as the accelerometer, GPS, etc. See LazDeviceAPIs
      + lazcanvas.pas: provides a TFPImageCanvas descendent implementing
        drawing in a LCL-compatible way, but 100% in Pascal.
      + lazregions.pas. LazRegions is a wholly Pascal implementation of
        regions for canvas clipping, event clipping, finding in which control
        of a region tree one an event should reach, for drawing polygons, etc.
      + customdrawncontrols.pas, customdrawndrawers.pas,
        customdrawn_common.pas, customdrawn_android.pas and
        customdrawn_winxp.pas: are the Lazarus Custom Drawn Controls -controls
        which imitate the standard LCL ones, but with the difference that they
        are non-native and support skinning.
  - New APIs added to the LCL to improve support of accessibility software
    such as screen readers.
* IDE changes:
  - Many improvments.
  - The detailed list of changes can be found here:
    http://wiki.lazarus.freepascal.org/New_IDE_features_since#v1.0_.282012-08-29.29
    http://wiki.lazarus.freepascal.org/Lazarus_1.0_release_notes#IDE_Changes
* Debugger / Editor changes:
  - Added pascal sources and breakpoints to the disassembler
  - Added threads dialog.
* Components changes:
  - TAChart: many fixes and new features
  - CodeTool: support Delphi style generics and new syntax extensions.
  - AggPas: removed to honor free licencing. (Closes: Bug#708695)
[Bart Martens]
* New debian/watch file fixing issues with upstream RC release.
[Abou Al Montacir]
* Avoid changing files in .pc hidden directory, these are used by quilt for
  internal purpose and could lead to surprises during build.
[Paul Gevers]
* Updated get-orig-source target and it compinion script orig-tar.sh so that they
  repack the source file, allowing bug 708695 to be fixed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
    procedure CopyFrom(ASource: TCustomChartSource);
56
56
    procedure Delete(AIndex: Integer);
57
57
    function IsSorted: Boolean; override;
 
58
 
 
59
    procedure SetColor(AIndex: Integer; AColor: TChartColor);
58
60
    function SetXValue(AIndex: Integer; AValue: Double): Integer;
59
61
    procedure SetYList(AIndex: Integer; const AYList: array of Double);
60
62
    procedure SetYValue(AIndex: Integer; AValue: Double);
 
63
 
61
64
    procedure Sort;
62
65
  published
63
66
    property DataPoints: TStrings read FDataPoints write SetDataPoints;
72
75
  // A generator is incapsulated in a class to allow using many simultaneous
73
76
  // random sequences, each determined by its own seed.
74
77
  TMWCRandomGenerator = class
75
 
  private
 
78
  strict private
76
79
    FHistory: array [0..4] of LongWord;
77
 
    procedure SetSeed(const AValue: Integer);
 
80
    procedure SetSeed(AValue: Integer);
78
81
  public
79
82
    function Get: LongWord;
80
83
    function GetInRange(AMin, AMax: Integer): Integer;
84
87
  { TRandomChartSource }
85
88
 
86
89
  TRandomChartSource = class(TCustomChartSource)
87
 
  private
 
90
  strict private
88
91
    FPointsNumber: Integer;
89
92
    FRandomX: Boolean;
90
93
    FRandSeed: Integer;
92
95
    FXMin: Double;
93
96
    FYMax: Double;
94
97
    FYMin: Double;
95
 
  private
 
98
  strict private
96
99
    FCurIndex: Integer;
97
100
    FCurItem: TChartDataItem;
98
101
    FRNG: TMWCRandomGenerator;
99
102
 
100
 
    procedure SetPointsNumber(const AValue: Integer);
101
 
    procedure SetRandomX(const AValue: Boolean);
102
 
    procedure SetRandSeed(const AValue: Integer);
103
 
    procedure SetXMax(const AValue: Double);
104
 
    procedure SetXMin(const AValue: Double);
105
 
    procedure SetYMax(const AValue: Double);
106
 
    procedure SetYMin(const AValue: Double);
 
103
    procedure SetPointsNumber(AValue: Integer);
 
104
    procedure SetRandomX(AValue: Boolean);
 
105
    procedure SetRandSeed(AValue: Integer);
 
106
    procedure SetXMax(AValue: Double);
 
107
    procedure SetXMin(AValue: Double);
 
108
    procedure SetYMax(AValue: Double);
 
109
    procedure SetYMin(AValue: Double);
107
110
  protected
108
111
    function GetCount: Integer; override;
109
112
    function GetItem(AIndex: Integer): PChartDataItem; override;
125
128
    property YMin: Double read FYMin write SetYMin;
126
129
  end;
127
130
 
128
 
  { TIntervalChartSource }
129
 
 
130
 
  TIntervalChartSource = class(TCustomChartSource)
131
 
  protected
132
 
    function GetCount: Integer; override;
133
 
    function GetItem(AIndex: Integer): PChartDataItem; override;
134
 
    procedure SetYCount(AValue: Cardinal); override;
135
 
  public
136
 
    procedure ValuesInRange(
137
 
      AMin, AMax: Double; const AFormat: String; AUseY: Boolean;
138
 
      var AValues: TDoubleDynArray; var ATexts: TStringDynArray); override;
139
 
  end;
140
 
 
141
 
  TDateTimeStep = (
142
 
    dtsCentury, dtsDecade, dtsYear, dtsQuarter, dtsMonth, dtsWeek, dtsDay,
143
 
    dtsHour, dtsTenMinutes, dtsMinute, dtsTenSeconds, dtsSecond, dtsMillisecond
144
 
  );
145
 
  TDateTimeSteps = set of TDateTimeStep;
146
 
 
147
 
const
148
 
  DATE_TIME_STEPS_ALL = [Low(TDateTimeStep) .. High(TDateTimeStep)];
149
 
 
150
 
type
151
 
 
152
 
  { TDateTimeIntervalChartSource }
153
 
 
154
 
  TDateTimeIntervalChartSource = class(TIntervalChartSource)
155
 
  private
156
 
    FDateTimeFormat: String;
157
 
    FSteps: TDateTimeSteps;
158
 
  public
159
 
    constructor Create(AOwner: TComponent); override;
160
 
    procedure ValuesInRange(
161
 
      AMin, AMax: Double; const AFormat: String; AUseY: Boolean;
162
 
      var AValues: TDoubleDynArray; var ATexts: TStringDynArray); override;
163
 
  published
164
 
    property DateTimeFormat: String read FDateTimeFormat write FDateTimeFormat;
165
 
    property Steps: TDateTimeSteps
166
 
      read FSteps write FSteps default DATE_TIME_STEPS_ALL;
167
 
  end;
168
 
 
169
131
  TUserDefinedChartSource = class;
170
132
 
171
133
  TGetChartDataItemEvent = procedure (
175
137
  { TUserDefinedChartSource }
176
138
 
177
139
  TUserDefinedChartSource = class(TCustomChartSource)
178
 
  private
 
140
  strict private
179
141
    FItem: TChartDataItem;
180
142
    FOnGetChartDataItem: TGetChartDataItemEvent;
181
143
    FPointsNumber: Integer;
182
144
    FSorted: Boolean;
183
 
    procedure SetOnGetChartDataItem(const AValue: TGetChartDataItemEvent);
184
 
    procedure SetPointsNumber(const AValue: Integer);
 
145
    procedure SetOnGetChartDataItem(AValue: TGetChartDataItemEvent);
 
146
    procedure SetPointsNumber(AValue: Integer);
185
147
  protected
186
148
    function GetCount: Integer; override;
187
149
    function GetItem(AIndex: Integer): PChartDataItem; override;
198
160
    property Sorted: Boolean read FSorted write FSorted default false;
199
161
  end;
200
162
 
201
 
  TChartAccumulationMethod = (camNone, camSum, camAverage);
 
163
  TChartAccumulationMethod = (
 
164
    camNone, camSum, camAverage, camDerivative, camSmoothDerivative);
 
165
  TChartAccumulationDirection = (cadBackward, cadForward, cadCenter);
202
166
 
203
167
  { TCalculatedChartSource }
204
168
 
205
169
  TCalculatedChartSource = class(TCustomChartSource)
206
 
  private
 
170
  strict private
 
171
    FAccumulationDirection: TChartAccumulationDirection;
207
172
    FAccumulationMethod: TChartAccumulationMethod;
208
 
    FAccumulationRange: Integer;
 
173
    FAccumulationRange: Cardinal;
209
174
    FHistory: TChartSourceBuffer;
210
175
    FIndex: Integer;
211
176
    FItem: TChartDataItem;
217
182
    FYOrder: array of Integer;
218
183
 
219
184
    procedure CalcAccumulation(AIndex: Integer);
 
185
    procedure CalcDerivative(AIndex: Integer);
220
186
    procedure CalcPercentage;
221
 
    procedure Changed(ASender: TObject); inline;
222
 
    procedure ExtractItem(out AItem: TChartDataItem; AIndex: Integer);
 
187
    procedure Changed(ASender: TObject);
 
188
    function EffectiveAccumulationRange: Cardinal;
 
189
    procedure ExtractItem(AIndex: Integer);
 
190
    function IsDerivative: Boolean; inline;
 
191
    procedure RangeAround(AIndex: Integer; out ALeft, ARight: Integer);
 
192
    procedure SetAccumulationDirection(AValue: TChartAccumulationDirection);
223
193
    procedure SetAccumulationMethod(AValue: TChartAccumulationMethod);
224
 
    procedure SetAccumulationRange(AValue: Integer);
 
194
    procedure SetAccumulationRange(AValue: Cardinal);
225
195
    procedure SetOrigin(AValue: TCustomChartSource);
226
196
    procedure SetPercentage(AValue: Boolean);
227
197
    procedure SetReorderYList(const AValue: String);
236
206
 
237
207
    function IsSorted: Boolean; override;
238
208
  published
 
209
    property AccumulationDirection: TChartAccumulationDirection
 
210
      read FAccumulationDirection write SetAccumulationDirection
 
211
      default cadBackward;
239
212
    property AccumulationMethod: TChartAccumulationMethod
240
213
      read FAccumulationMethod write SetAccumulationMethod default camNone;
241
 
    property AccumulationRange: Integer
242
 
      read FAccumulationRange write SetAccumulationRange default 1;
 
214
    property AccumulationRange: Cardinal
 
215
      read FAccumulationRange write SetAccumulationRange default 2;
243
216
 
244
217
    property Origin: TCustomChartSource read FOrigin write SetOrigin;
245
218
    property Percentage: Boolean
252
225
implementation
253
226
 
254
227
uses
255
 
  DateUtils, Math, StrUtils, SysUtils;
 
228
  Math, StrUtils, SysUtils, TAMath;
256
229
 
257
230
type
258
231
 
259
232
  { TListChartSourceStrings }
260
233
 
261
234
  TListChartSourceStrings = class(TStrings)
262
 
  private
 
235
  strict private
263
236
    FSource: TListChartSource;
264
 
    procedure Parse(const AString: String; ADataItem: PChartDataItem);
 
237
    procedure Parse(AString: String; ADataItem: PChartDataItem);
265
238
  protected
266
239
    function Get(Index: Integer): String; override;
267
240
    function GetCount: Integer; override;
268
241
    procedure Put(Index: Integer; const S: String); override;
 
242
    procedure SetUpdateState(AUpdating: Boolean); override;
269
243
  public
 
244
    constructor Create(ASource: TListChartSource);
270
245
    procedure Clear; override;
271
246
    procedure Delete(Index: Integer); override;
272
247
    procedure Insert(Index: Integer; const S: String); override;
276
251
begin
277
252
  RegisterComponents(
278
253
    CHART_COMPONENT_IDE_PAGE, [
279
 
      TListChartSource, TRandomChartSource, TDateTimeIntervalChartSource,
280
 
      TUserDefinedChartSource, TCalculatedChartSource
 
254
      TListChartSource, TRandomChartSource, TUserDefinedChartSource,
 
255
      TCalculatedChartSource
281
256
    ]);
282
257
end;
283
258
 
288
263
  FSource.Clear;
289
264
end;
290
265
 
 
266
constructor TListChartSourceStrings.Create(ASource: TListChartSource);
 
267
begin
 
268
  inherited Create;
 
269
  FSource := ASource;
 
270
end;
 
271
 
291
272
procedure TListChartSourceStrings.Delete(Index: Integer);
292
273
begin
293
274
  FSource.Delete(Index);
306
287
      Result += Format('|%g', [Y], fs);
307
288
    for i := 0 to High(YList) do
308
289
      Result += Format('|%g', [YList[i]], fs);
309
 
    Result += Format('|%s|%s',
310
 
      [IfThen(Color = clTAColor, '?', '$' + IntToHex(Color, 6)), Text]);
 
290
    Result += Format('|%s|%s', [IntToColorHex(Color), Text]);
311
291
  end;
312
292
end;
313
293
 
327
307
end;
328
308
 
329
309
procedure TListChartSourceStrings.Parse(
330
 
  const AString: String; ADataItem: PChartDataItem);
 
310
  AString: String; ADataItem: PChartDataItem);
331
311
var
332
312
  p: Integer = 0;
333
313
  parts: TStringList;
334
 
  fs: TFormatSettings;
335
314
 
336
315
  function NextPart: String;
337
316
  begin
342
321
    p += 1;
343
322
  end;
344
323
 
345
 
  function S2F(const AStr: String): Double;
346
 
  begin
347
 
    // Accept both locale-specific and default decimal separators.
348
 
    if
349
 
      not TryStrToFloat(AStr, Result, fs) and
350
 
      not TryStrToFloat(AStr, Result)
351
 
    then
352
 
      Result := 0.0;
353
 
  end;
354
 
 
355
324
var
356
325
  i: Integer;
357
326
begin
358
 
  fs := DefaultFormatSettings;
359
 
  fs.DecimalSeparator := '.';
360
327
  parts := TStringList.Create;
361
328
  try
362
329
    parts.Delimiter := '|';
365
332
    if FSource.YCount + 3 < Cardinal(parts.Count) then
366
333
      FSource.YCount := parts.Count - 3;
367
334
    with ADataItem^ do begin
368
 
      X := S2F(NextPart);
 
335
      X := StrToFloatDefSep(NextPart);
369
336
      if FSource.YCount > 0 then begin
370
 
        Y := S2F(NextPart);
 
337
        Y := StrToFloatDefSep(NextPart);
371
338
        for i := 0 to High(YList) do
372
 
          YList[i] := S2F(NextPart);
 
339
          YList[i] := StrToFloatDefSep(NextPart);
373
340
      end;
374
341
      Color := StrToIntDef(NextPart, clTAColor);
375
342
      Text := NextPart;
381
348
 
382
349
procedure TListChartSourceStrings.Put(Index: Integer; const S: String);
383
350
begin
 
351
  FSource.BeginUpdate;
 
352
  try
384
353
  Parse(S, FSource[Index]);
 
354
  finally
 
355
    FSource.EndUpdate;
 
356
  end;
 
357
end;
 
358
 
 
359
procedure TListChartSourceStrings.SetUpdateState(AUpdating: Boolean);
 
360
begin
 
361
  if AUpdating then
 
362
    FSource.BeginUpdate
 
363
  else
 
364
    FSource.EndUpdate;
385
365
end;
386
366
 
387
367
{ TListChartSource }
413
393
  pcd^.Text := ALabel;
414
394
  FData.Insert(APos, pcd);
415
395
  UpdateCachesAfterAdd(AX, AY);
416
 
  Notify;
417
396
end;
418
397
 
419
398
procedure TListChartSource.Clear; inline;
458
437
begin
459
438
  inherited Create(AOwner);
460
439
  FData := TFPList.Create;
461
 
  FDataPoints := TListChartSourceStrings.Create;
462
 
  TListChartSourceStrings(FDataPoints).FSource := Self;
 
440
  FDataPoints := TListChartSourceStrings.Create(Self);
463
441
  FYCount := 1;
464
442
  ClearCaches;
465
443
end;
471
449
      (FExtent.a.X < X) and (X < FExtent.b.X) and
472
450
      (FExtent.a.Y < Y) and (Y < FExtent.b.Y);
473
451
    if FValuesTotalIsValid then
474
 
      FValuesTotal -= Y;
 
452
      FValuesTotal -= NumberOr(Y);
475
453
  end;
476
454
  Dispose(Item[AIndex]);
477
455
  FData.Delete(AIndex);
507
485
  SetLength(Result^.YList, Max(YCount - 1, 0));
508
486
end;
509
487
 
 
488
procedure TListChartSource.SetColor(AIndex: Integer; AColor: TChartColor);
 
489
begin
 
490
  with Item[AIndex]^ do begin
 
491
    if Color = AColor then exit;
 
492
    Color := AColor;
 
493
  end;
 
494
  Notify;
 
495
end;
 
496
 
510
497
procedure TListChartSource.SetDataPoints(AValue: TStrings);
511
498
begin
512
499
  if FDataPoints = AValue then exit;
531
518
 
532
519
function TListChartSource.SetXValue(AIndex: Integer; AValue: Double): Integer;
533
520
var
534
 
  i: Integer;
535
521
  oldX: Double;
536
 
begin
537
 
  oldX := Item[AIndex]^.X;
538
 
  Item[AIndex]^.X := AValue;
539
 
 
540
 
  if FExtentIsValid then begin
541
 
    if AValue <= FExtent.a.X then FExtent.a.X := AValue
542
 
    else if AValue >= FExtent.b.X then FExtent.b.X := AValue;
 
522
 
 
523
  procedure UpdateExtent;
 
524
  var
 
525
    it: PChartDataItem;
 
526
  begin
 
527
    if not FExtentIsValid then exit;
 
528
 
 
529
    if not IsNan(AValue) then begin
 
530
      if AValue <= FExtent.a.X then
 
531
        FExtent.a.X := AValue
 
532
      else if AValue >= FExtent.b.X then
 
533
        FExtent.b.X := AValue;
 
534
    end;
 
535
 
 
536
    if IsNan(oldX) then exit;
543
537
    if oldX = FExtent.b.X then begin
544
538
      FExtent.b.X := NegInfinity;
545
 
      for i := 0 to Count - 1 do
546
 
        FExtent.b.X := Max(FExtent.b.X, Item[i]^.X);
 
539
      for it in Self do
 
540
        if not IsNan(it^.X) then
 
541
          FExtent.b.X := Max(FExtent.b.X, it^.X);
547
542
    end;
548
543
    if oldX = FExtent.a.X then begin
549
544
      FExtent.a.X := SafeInfinity;
550
 
      for i := 0 to Count - 1 do
551
 
        FExtent.a.X := Min(FExtent.a.X, Item[i]^.X);
 
545
      for it in Self do
 
546
        if not IsNan(it^.X) then
 
547
          FExtent.a.X := Min(FExtent.a.X, it^.X);
552
548
    end;
553
549
  end;
554
550
 
 
551
begin
 
552
  oldX := Item[AIndex]^.X;
555
553
  Result := AIndex;
 
554
  if IsEquivalent(oldX, AValue) then exit;
 
555
  Item[AIndex]^.X := AValue;
 
556
  UpdateExtent;
556
557
  if Sorted then begin
 
558
    if IsNan(AValue) then
 
559
      raise EChartError.Create('X = NaN in a sorted source');
557
560
    if AValue > oldX then
558
561
      while (Result < Count - 1) and (Item[Result + 1]^.X < AValue) do
559
562
        Inc(Result)
588
591
 
589
592
procedure TListChartSource.SetYValue(AIndex: Integer; AValue: Double);
590
593
var
591
 
  i: Integer;
592
594
  oldY: Double;
593
 
begin
594
 
  oldY := Item[AIndex]^.Y;
595
 
  Item[AIndex]^.Y := AValue;
596
 
  if FValuesTotalIsValid then
597
 
    FValuesTotal += AValue - oldY;
598
 
 
599
 
  if FExtentIsValid then begin
600
 
    if AValue <= FExtent.a.Y then FExtent.a.Y := AValue
601
 
    else if AValue >= FExtent.b.Y then FExtent.b.Y := AValue;
 
595
 
 
596
  procedure UpdateExtent;
 
597
  var
 
598
    it: PChartDataItem;
 
599
  begin
 
600
    if not FExtentIsValid then exit;
 
601
 
 
602
    if not IsNan(AValue) then begin
 
603
      if AValue <= FExtent.a.Y then
 
604
        FExtent.a.Y := AValue
 
605
      else if AValue >= FExtent.b.Y then
 
606
        FExtent.b.Y := AValue;
 
607
    end;
 
608
 
 
609
    if IsNan(oldY) then exit;
602
610
    if oldY = FExtent.b.Y then begin
603
611
      FExtent.b.Y := NegInfinity;
604
 
      for i := 0 to Count - 1 do
605
 
        FExtent.b.Y := Max(FExtent.b.Y, Item[i]^.Y);
 
612
      for it in Self do
 
613
        if not IsNan(it^.Y) then
 
614
          FExtent.b.Y := Max(FExtent.b.Y, it^.Y);
606
615
    end;
607
616
    if oldY = FExtent.a.Y then begin
608
617
      FExtent.a.Y := SafeInfinity;
609
 
      for i := 0 to Count - 1 do
610
 
        FExtent.a.Y := Min(FExtent.a.Y, Item[i]^.Y);
 
618
      for it in Self do
 
619
        if not IsNan(it^.Y) then
 
620
          FExtent.a.Y := Min(FExtent.a.Y, it^.Y);
611
621
    end;
612
622
  end;
 
623
 
 
624
begin
 
625
  oldY := Item[AIndex]^.Y;
 
626
  if IsEquivalent(oldY, AValue) then exit;
 
627
  Item[AIndex]^.Y := AValue;
 
628
  if FValuesTotalIsValid then
 
629
    FValuesTotal += NumberOr(AValue) - NumberOr(oldY);
 
630
  UpdateExtent;
613
631
  Notify;
614
632
end;
615
633
 
630
648
    UpdateMinMax(AY, FExtent.a.Y, FExtent.b.Y);
631
649
  end;
632
650
  if FValuesTotalIsValid then
633
 
    FValuesTotal += AY;
 
651
    FValuesTotal += NumberOr(AY);
 
652
  Notify;
634
653
end;
635
654
 
636
655
{ TMWCRandomGenerator }
664
683
  Result := Integer(Hi(m)) + AMin;
665
684
end;
666
685
 
667
 
procedure TMWCRandomGenerator.SetSeed(const AValue: Integer);
 
686
procedure TMWCRandomGenerator.SetSeed(AValue: Integer);
668
687
var
669
688
  i: Integer;
670
689
begin
742
761
  Result := not RandomX;
743
762
end;
744
763
 
745
 
procedure TRandomChartSource.SetPointsNumber(const AValue: Integer);
 
764
procedure TRandomChartSource.SetPointsNumber(AValue: Integer);
746
765
begin
747
766
  if FPointsNumber = AValue then exit;
748
767
  FPointsNumber := AValue;
750
769
  Notify;
751
770
end;
752
771
 
753
 
procedure TRandomChartSource.SetRandomX(const AValue: Boolean);
 
772
procedure TRandomChartSource.SetRandomX(AValue: Boolean);
754
773
begin
755
774
  if FRandomX = AValue then exit;
756
775
  FRandomX := AValue;
758
777
  Notify;
759
778
end;
760
779
 
761
 
procedure TRandomChartSource.SetRandSeed(const AValue: Integer);
 
780
procedure TRandomChartSource.SetRandSeed(AValue: Integer);
762
781
begin
763
782
  if FRandSeed = AValue then exit;
764
783
  FRandSeed := AValue;
768
787
  Notify;
769
788
end;
770
789
 
771
 
procedure TRandomChartSource.SetXMax(const AValue: Double);
 
790
procedure TRandomChartSource.SetXMax(AValue: Double);
772
791
begin
773
792
  if FXMax = AValue then exit;
774
793
  FXMax := AValue;
776
795
  Notify;
777
796
end;
778
797
 
779
 
procedure TRandomChartSource.SetXMin(const AValue: Double);
 
798
procedure TRandomChartSource.SetXMin(AValue: Double);
780
799
begin
781
800
  if FXMin = AValue then exit;
782
801
  FXMin := AValue;
792
811
  Notify;
793
812
end;
794
813
 
795
 
procedure TRandomChartSource.SetYMax(const AValue: Double);
 
814
procedure TRandomChartSource.SetYMax(AValue: Double);
796
815
begin
797
816
  if FYMax = AValue then exit;
798
817
  FYMax := AValue;
800
819
  Notify;
801
820
end;
802
821
 
803
 
procedure TRandomChartSource.SetYMin(const AValue: Double);
 
822
procedure TRandomChartSource.SetYMin(AValue: Double);
804
823
begin
805
824
  if FYMin = AValue then exit;
806
825
  FYMin := AValue;
808
827
  Notify;
809
828
end;
810
829
 
811
 
{ TIntervalChartSource }
812
 
 
813
 
function TIntervalChartSource.GetCount: Integer;
814
 
begin
815
 
  Result := 0;
816
 
end;
817
 
 
818
 
function TIntervalChartSource.GetItem(AIndex: Integer): PChartDataItem;
819
 
begin
820
 
  Unused(AIndex);
821
 
  Result := nil;
822
 
end;
823
 
 
824
 
procedure TIntervalChartSource.SetYCount(AValue: Cardinal);
825
 
begin
826
 
  Unused(AValue);
827
 
  raise EYCountError.Create('Can not set YCount');
828
 
end;
829
 
 
830
 
procedure TIntervalChartSource.ValuesInRange(
831
 
  AMin, AMax: Double; const AFormat: String; AUseY: Boolean;
832
 
  var AValues: TDoubleDynArray; var ATexts: TStringDynArray);
833
 
var
834
 
  i: Integer;
835
 
begin
836
 
  Unused(AUseY);
837
 
  if AMin > AMax then exit;
838
 
  AValues := GetIntervals(AMin, AMax, false);
839
 
  SetLength(ATexts, Length(AValues));
840
 
  for i := 0 to High(AValues) do
841
 
    // Extra format arguments for compatibility with FormatItem.
842
 
    ATexts[i] := Format(AFormat, [AValues[i], 0.0, '', 0.0, 0.0]);
843
 
end;
844
 
 
845
 
{ TDateTimeIntervalChartSource }
846
 
 
847
 
constructor TDateTimeIntervalChartSource.Create(AOwner: TComponent);
848
 
begin
849
 
  inherited Create(AOwner);
850
 
  FSteps := DATE_TIME_STEPS_ALL;
851
 
end;
852
 
 
853
 
procedure TDateTimeIntervalChartSource.ValuesInRange(AMin, AMax: Double;
854
 
  const AFormat: String; AUseY: Boolean; var AValues: TDoubleDynArray;
855
 
  var ATexts: TStringDynArray);
856
 
const
857
 
  YEAR = 365.25;
858
 
  STEP_INTERVALS: array [TDateTimeStep] of Double = (
859
 
    100 * YEAR, 10 * YEAR, YEAR, YEAR / 4, YEAR / 12, 7, 1,
860
 
    OneHour, 10 * OneMinute, OneMinute, 10 * OneSecond, OneSecond, OneMillisecond
861
 
  );
862
 
  MIN_STEPS = 4;
863
 
  MAX_STEPS = 20;
864
 
var
865
 
  s: TDateTimeStep;
866
 
  si, x, start: TDateTime;
867
 
  prevSt: TSystemTime;
868
 
 
869
 
  function FormatLabel: String;
870
 
  var
871
 
    st: TSystemTime;
872
 
  begin
873
 
    if DateTimeFormat <> '' then
874
 
      exit(FormatDateTime(DateTimeFormat, x));
875
 
    DateTimeToSystemTime(x, st);
876
 
    case s of
877
 
      dtsCentury, dtsDecade, dtsYear:
878
 
        Result := FormatDateTime('yyyy', x);
879
 
      dtsQuarter:
880
 
        Result := FormatDateTime('yyyy/', x) + IntToStr(Floor(x / si) mod 4 + 1);
881
 
      dtsMonth:
882
 
        Result := FormatDateTime(
883
 
          IfThen(st.Year = prevSt.Year, 'mm', 'mm/yyyy'), x);
884
 
      dtsWeek:
885
 
        Result := FormatDateTime('dd/mm', x);
886
 
      dtsDay:
887
 
        Result := FormatDateTime(
888
 
          IfThen(st.Month = prevSt.Month, 'dd', 'dd/mm'), x);
889
 
      dtsHour:
890
 
        Result := FormatDateTime(
891
 
          IfThen(st.Day = prevSt.Day, 'hh:00', 'dd hh:00'), x);
892
 
      dtsTenMinutes, dtsMinute:
893
 
        Result := FormatDateTime(
894
 
          IfThen(st.Hour = prevSt.Hour, 'nn', 'hh:nn'), x);
895
 
      dtsTenSeconds, dtsSecond:
896
 
        Result := FormatDateTime(
897
 
          IfThen(st.Minute = prevSt.Minute, 'ss', 'nn:ss'), x);
898
 
      dtsMillisecond:
899
 
        Result := IntToStr(st.Millisecond) + 'ms';
900
 
    end;
901
 
    prevSt := st;
902
 
  end;
903
 
 
904
 
var
905
 
  i, cnt: Integer;
906
 
begin
907
 
  if (AMax - AMin) / STEP_INTERVALS[dtsCentury] > MAX_STEPS then begin
908
 
    inherited ValuesInRange(
909
 
      AMin / STEP_INTERVALS[dtsYear], AMax / STEP_INTERVALS[dtsYear],
910
 
      AFormat, AUseY, AValues, ATexts);
911
 
    exit;
912
 
  end;
913
 
  s := Low(s);
914
 
  while s < High(s) do begin
915
 
    si := STEP_INTERVALS[s];
916
 
    if (s in Steps) and ((AMax - AMin) / si > MIN_STEPS) then
917
 
      break;
918
 
    Inc(s);
919
 
  end;
920
 
  start := Int(AMin / si - 1) * si;
921
 
  x := start;
922
 
  cnt := 0;
923
 
  while x <= AMax do begin
924
 
    if x >= AMin then
925
 
      cnt += 1;
926
 
    x += si;
927
 
  end;
928
 
  i := Length(AValues);
929
 
  SetLength(AValues, i + cnt);
930
 
  SetLength(ATexts, i + cnt);
931
 
 
932
 
  FillChar(prevSt, SizeOf(prevSt), $FF);
933
 
  x := start;
934
 
  while x <= AMax do begin
935
 
   if x >= AMin then begin
936
 
      AValues[i] := x;
937
 
      ATexts[i] := Format(AFormat, [x, 0.0, FormatLabel, 0.0, 0.0]);
938
 
      i += 1;
939
 
    end;
940
 
    case s of
941
 
      dtsCentury: x := IncYear(x, 100);
942
 
      dtsDecade: x := IncYear(x, 10);
943
 
      dtsYear: x := IncYear(x);
944
 
      dtsMonth: x := IncMonth(x);
945
 
      otherwise x += si;
946
 
    end;
947
 
  end;
948
 
end;
949
 
 
950
830
{ TUserDefinedChartSource }
951
831
 
952
832
procedure TUserDefinedChartSource.EndUpdate;
982
862
end;
983
863
 
984
864
procedure TUserDefinedChartSource.SetOnGetChartDataItem(
985
 
  const AValue: TGetChartDataItemEvent);
 
865
  AValue: TGetChartDataItemEvent);
986
866
begin
987
867
  if TMethod(FOnGetChartDataItem) = TMethod(AValue) then exit;
988
868
  FOnGetChartDataItem := AValue;
989
869
  Reset;
990
870
end;
991
871
 
992
 
procedure TUserDefinedChartSource.SetPointsNumber(const AValue: Integer);
 
872
procedure TUserDefinedChartSource.SetPointsNumber(AValue: Integer);
993
873
begin
994
874
  if FPointsNumber = AValue then exit;
995
875
  FPointsNumber := AValue;
1008
888
 
1009
889
procedure TCalculatedChartSource.CalcAccumulation(AIndex: Integer);
1010
890
var
1011
 
  i, ar: Integer;
 
891
  lastItemIndex: Integer = -1;
 
892
 
 
893
  function GetOriginItem(AItemIndex: Integer): PChartDataItem;
 
894
  begin
 
895
    Result := @FItem;
 
896
    if lastItemIndex = AItemIndex then exit;
 
897
    ExtractItem(AItemIndex);
 
898
    lastItemIndex := AItemIndex;
 
899
  end;
 
900
 
 
901
var
 
902
  i, oldLeft, oldRight, newLeft, newRight: Integer;
1012
903
begin
1013
 
  FHistory.Capacity := AccumulationRange;
1014
 
  ar := IfThen(AccumulationRange = 0, MaxInt, AccumulationRange);
1015
 
  if FIndex = AIndex - 1 then begin
1016
 
    ExtractItem(FItem, AIndex);
1017
 
    FHistory.AddLast(FItem);
 
904
  if AccumulationDirection = cadCenter then
 
905
    FHistory.Capacity := EffectiveAccumulationRange * 2
 
906
  else
 
907
    FHistory.Capacity := EffectiveAccumulationRange;
 
908
  RangeAround(FIndex, oldLeft, oldRight);
 
909
  RangeAround(AIndex, newLeft, newRight);
 
910
  if
 
911
    (FIndex < 0) or (Abs(oldLeft - newLeft) > 1) or
 
912
    (Abs(oldRight - newRight) > 1)
 
913
  then begin
 
914
    FHistory.Clear;
 
915
    for i := newLeft to newRight do
 
916
      FHistory.AddLast(GetOriginItem(i)^);
1018
917
  end
1019
 
  else if FIndex = AIndex + 1 then begin
1020
 
    if AccumulationRange = 0 then begin
1021
 
      ExtractItem(FItem, FIndex);
1022
 
      FHistory.RemoveValue(FItem);
1023
 
      ExtractItem(FItem, AIndex);
 
918
  else begin
 
919
    if FHistory.Capacity = 0 then
 
920
      for i := oldLeft to newLeft - 1 do
 
921
        FHistory.RemoveValue(GetOriginItem(i)^)
 
922
    else
 
923
      for i := oldLeft to newLeft - 1 do
 
924
        FHistory.RemoveFirst;
 
925
    if FHistory.Capacity = 0 then
 
926
      for i := oldRight downto newRight + 1 do
 
927
        FHistory.RemoveValue(GetOriginItem(i)^)
 
928
    else
 
929
      for i := oldRight downto newRight + 1 do
 
930
        FHistory.RemoveLast;
 
931
    for i := oldLeft - 1 downto newLeft do
 
932
      FHistory.AddFirst(GetOriginItem(i)^);
 
933
    for i := oldRight + 1 to newRight do
 
934
      FHistory.AddLast(GetOriginItem(i)^);
 
935
  end;
 
936
  GetOriginItem(AIndex);
 
937
  case AccumulationMethod of
 
938
    camSum:
 
939
      FHistory.GetSum(FItem);
 
940
    camAverage: begin
 
941
      FHistory.GetSum(FItem);
 
942
      FItem.MultiplyY(1 / (newRight - newLeft + 1));
 
943
    end;
 
944
    camDerivative, camSmoothDerivative:
 
945
      CalcDerivative(AIndex);
 
946
  end;
 
947
  FIndex := AIndex;
 
948
end;
 
949
 
 
950
procedure TCalculatedChartSource.CalcDerivative(AIndex: Integer);
 
951
 
 
952
  procedure WeightedSum(const ACoeffs: array of Double; ADir, ACount: Integer);
 
953
  var
 
954
    i, j: Integer;
 
955
    prevItem: PChartDataItem;
 
956
  begin
 
957
    for j := 0 to ACount - 1 do begin
 
958
      prevItem := FHistory.GetPtr(AIndex + ADir * j);
 
959
      FItem.Y += prevItem^.Y * ADir * ACoeffs[j];
 
960
      for i := 0 to High(FItem.YList) do
 
961
        FItem.YList[i] += prevItem^.YList[i] * ADir * ACoeffs[j];
 
962
    end;
 
963
  end;
 
964
 
 
965
// Derivative is approximated by finite differences
 
966
// with accuracy order of (AccumulationRange - 1).
 
967
// Smoothed derivative coefficients are based on work
 
968
// by Pavel Holoborodko (http://www.holoborodko.com/pavel/).
 
969
const
 
970
  COEFFS_BF: array [Boolean, 2..7, 0..6] of Double = (
 
971
    ( (     -1, 1,     0,    0,     0,   0,    0),
 
972
      (   -3/2, 2,  -1/2,    0,     0,   0,    0),
 
973
      (  -11/6, 3,  -3/2,  1/3,     0,   0,    0),
 
974
      ( -25/12, 4,    -3,  4/3,  -1/4,   0,    0),
 
975
      (-137/60, 5,    -5, 10/3,  -5/4, 1/5,    0),
 
976
      ( -49/20, 6, -15/2, 20/3, -15/4, 6/5, -1/6)
 
977
    ),
 
978
    ( (   -1,     1,     0,   0,    0,    0,    0),
 
979
      ( -1/2,     0,   1/2,   0,    0,    0,    0),
 
980
      ( -1/4,  -1/4,   1/4, 1/4,    0,    0,    0),
 
981
      ( -1/8,  -1/4,     0, 1/4,  1/8,    0,    0),
 
982
      (-1/16, -3/16,  -1/8, 1/8, 3/16, 1/16,    0),
 
983
      (-1/32,  -1/8, -5/32,   0, 5/32,  1/8, 1/32)
 
984
    ));
 
985
  COEFFS_C: array [Boolean, 2..5, 0..4] of Double = (
 
986
    ( (0,  1/2,     0,     0,      0),
 
987
      (0,  2/3, -1/12,     0,      0),
 
988
      (0,  3/4, -3/20,  1/60,      0),
 
989
      (0,  4/5,  -1/5, 4/105, -1/280)
 
990
    ),
 
991
    ( (0,  1/2,    0,    0,     0),
 
992
      (0,  1/4,  1/8,    0,     0),
 
993
      (0, 5/32,  1/8, 1/32,     0),
 
994
      (0, 7/64, 7/64, 3/64, 1/128)
 
995
    ));
 
996
var
 
997
  ar, iLeft, iRight, dir: Integer;
 
998
  isSmooth: Boolean;
 
999
  dx: Double;
 
1000
begin
 
1001
  RangeAround(AIndex, iLeft, iRight);
 
1002
  case CASE_OF_TWO[iLeft = AIndex, iRight = AIndex] of
 
1003
    cotNone: begin
 
1004
      dx := Max(
 
1005
        FItem.X - FHistory.GetPtr(AIndex - iLeft - 1)^.X,
 
1006
        FHistory.GetPtr(AIndex - iLeft + 1)^.X - FItem.X);
 
1007
      ar := Min(Min(AIndex - iLeft, iRight - AIndex) + 1, High(COEFFS_C[false]));
 
1008
      dir := 0;
 
1009
    end;
 
1010
    cotFirst: begin
 
1011
      dx := FHistory.GetPtr(1)^.X - FItem.X;
 
1012
      ar := Min(iRight - AIndex + 1, High(COEFFS_BF[false]));
 
1013
      dir := 1;
 
1014
    end;
 
1015
    cotSecond: begin
 
1016
      dx := FItem.X - FHistory.GetPtr(AIndex - iLeft - 1)^.X;
 
1017
      ar := Min(AIndex - iLeft + 1, High(COEFFS_BF[false]));
 
1018
      dir := -1;
 
1019
    end;
 
1020
    cotBoth: begin
 
1021
      FItem.SetY(SafeNan);
 
1022
      exit;
1024
1023
    end
1025
 
    else begin
1026
 
      i := AIndex - AccumulationRange + 1;
1027
 
      if i < 0 then
1028
 
        FHistory.RemoveLast
1029
 
      else begin
1030
 
        ExtractItem(FItem, i);
1031
 
        FHistory.AddFirst(FItem);
1032
 
      end;
1033
 
      FItem := FHistory.GetPLast^;
1034
 
    end;
 
1024
  end;
 
1025
  if dx = 0 then begin
 
1026
    FItem.SetY(SafeNan);
 
1027
    exit;
 
1028
  end;
 
1029
  FItem.SetY(0.0);
 
1030
  AIndex -= iLeft;
 
1031
  isSmooth := AccumulationMethod = camSmoothDerivative;
 
1032
  if dir = 0 then begin
 
1033
    WeightedSum(COEFFS_C[isSmooth][ar], -1, ar);
 
1034
    WeightedSum(COEFFS_C[isSmooth][ar], +1, ar);
1035
1035
  end
1036
 
  else begin
1037
 
    FHistory.Clear;
1038
 
    for i := Max(AIndex - ar + 1, 0) to AIndex do begin
1039
 
      ExtractItem(FItem, i);
1040
 
      FHistory.AddLast(FItem);
1041
 
    end;
1042
 
  end;
1043
 
  FHistory.GetSum(FItem);
1044
 
  if AccumulationMethod = camAverage then begin
1045
 
    FItem.Y /= Min(ar, AIndex + 1);
1046
 
    for i := 0 to High(FItem.YList) do
1047
 
      FItem.YList[i] /= Min(ar, AIndex + 1);
1048
 
  end;
 
1036
  else
 
1037
    WeightedSum(COEFFS_BF[isSmooth][ar], dir, ar);
 
1038
  FItem.MultiplyY(1 / dx);
1049
1039
end;
1050
1040
 
1051
1041
procedure TCalculatedChartSource.CalcPercentage;
1052
1042
var
1053
1043
  s: Double;
1054
 
  i: Integer;
1055
1044
begin
1056
1045
  if not Percentage then exit;
1057
1046
  s := (FItem.Y + Sum(FItem.YList)) * PERCENT;
1058
 
  FItem.Y /= s;
1059
 
  for i := 0 to High(FItem.YList) do
1060
 
    FItem.YList[i] /= s;
 
1047
  if s = 0 then exit;
 
1048
  FItem.MultiplyY(1 / s);
1061
1049
end;
1062
1050
 
1063
1051
procedure TCalculatedChartSource.Changed(ASender: TObject);
1077
1065
constructor TCalculatedChartSource.Create(AOwner: TComponent);
1078
1066
begin
1079
1067
  inherited Create(AOwner);
1080
 
  FAccumulationRange := 1;
 
1068
  FAccumulationRange := 2;
1081
1069
  FIndex := -1;
1082
1070
  FHistory := TChartSourceBuffer.Create;
1083
1071
  FListener := TListener.Create(@FOrigin, @Changed);
1090
1078
  inherited Destroy;
1091
1079
end;
1092
1080
 
1093
 
procedure TCalculatedChartSource.ExtractItem(
1094
 
  out AItem: TChartDataItem; AIndex: Integer);
 
1081
function TCalculatedChartSource.EffectiveAccumulationRange: Cardinal;
 
1082
const
 
1083
  MAX_DERIVATIVE_RANGE = 10;
 
1084
begin
 
1085
  if IsDerivative and (AccumulationRange = 0) then
 
1086
    Result := MAX_DERIVATIVE_RANGE
 
1087
  else
 
1088
    Result := AccumulationRange;
 
1089
end;
 
1090
 
 
1091
procedure TCalculatedChartSource.ExtractItem(AIndex: Integer);
 
1092
 
 
1093
  function YByOrder(AOrderIndex: Integer): Double;
 
1094
  begin
 
1095
    if AOrderIndex = 0 then
 
1096
      Result := FItem.Y
 
1097
    else
 
1098
      Result := FItem.YList[AOrderIndex - 1];
 
1099
  end;
 
1100
 
1095
1101
var
1096
1102
  t: TDoubleDynArray;
1097
1103
  i: Integer;
1098
1104
begin
1099
 
  AItem := Origin[AIndex]^;
1100
 
  SetLength(t, Length(FYOrder));
1101
 
  for i := 0 to High(FYOrder) do
1102
 
    t[i] := AItem.YList[FYOrder[i]];
1103
 
  AItem.YList := t;
 
1105
  FItem := Origin[AIndex]^;
 
1106
  SetLength(t, High(FYOrder));
 
1107
  for i := 1 to High(FYOrder) do
 
1108
    t[i - 1] := YByOrder(FYOrder[i]);
 
1109
  FItem.Y := YByOrder(FYOrder[0]);
 
1110
  FItem.YList := t;
1104
1111
end;
1105
1112
 
1106
1113
function TCalculatedChartSource.GetCount: Integer;
1117
1124
  Result := @FItem;
1118
1125
  if FIndex = AIndex then exit;
1119
1126
  if (AccumulationMethod = camNone) or (AccumulationRange = 1) then
1120
 
    ExtractItem(FItem, AIndex)
 
1127
    ExtractItem(AIndex)
1121
1128
  else
1122
1129
    CalcAccumulation(AIndex);
1123
1130
  CalcPercentage;
1124
 
  FIndex := AIndex;
 
1131
end;
 
1132
 
 
1133
function TCalculatedChartSource.IsDerivative: Boolean;
 
1134
begin
 
1135
  Result := AccumulationMethod in [camDerivative, camSmoothDerivative];
1125
1136
end;
1126
1137
 
1127
1138
function TCalculatedChartSource.IsSorted: Boolean;
1132
1143
    Result := false;
1133
1144
end;
1134
1145
 
 
1146
procedure TCalculatedChartSource.RangeAround(
 
1147
  AIndex: Integer; out ALeft, ARight: Integer);
 
1148
var
 
1149
  ar: Integer;
 
1150
begin
 
1151
  ar := EffectiveAccumulationRange;
 
1152
  ar := IfThen(ar = 0, MaxInt div 2, ar - 1);
 
1153
  ALeft := AIndex - IfThen(AccumulationDirection = cadForward, 0, ar);
 
1154
  ARight := AIndex + IfThen(AccumulationDirection = cadBackward, 0, ar);
 
1155
  ALeft := EnsureRange(ALeft, 0, Count - 1);
 
1156
  ARight := EnsureRange(ARight, 0, Count - 1);
 
1157
end;
 
1158
 
 
1159
procedure TCalculatedChartSource.SetAccumulationDirection(
 
1160
  AValue: TChartAccumulationDirection);
 
1161
begin
 
1162
  if FAccumulationDirection = AValue then exit;
 
1163
  FAccumulationDirection := AValue;
 
1164
  Changed(nil);
 
1165
end;
 
1166
 
1135
1167
procedure TCalculatedChartSource.SetAccumulationMethod(
1136
1168
  AValue: TChartAccumulationMethod);
1137
1169
begin
1140
1172
  Changed(nil);
1141
1173
end;
1142
1174
 
1143
 
procedure TCalculatedChartSource.SetAccumulationRange(AValue: Integer);
 
1175
procedure TCalculatedChartSource.SetAccumulationRange(AValue: Cardinal);
1144
1176
begin
1145
1177
  if FAccumulationRange = AValue then exit;
1146
1178
  FAccumulationRange := AValue;
1196
1228
 
1197
1229
  FOriginYCount := FOrigin.YCount;
1198
1230
  if ReorderYList = '' then begin
1199
 
    FYCount := FOrigin.YCount;
1200
 
    SetLength(FYOrder,  Max(FYCount - 1, 0));
 
1231
    SetLength(FYOrder,  FOriginYCount);
1201
1232
    for i := 0 to High(FYOrder) do
1202
1233
      FYOrder[i] := i;
1203
1234
  end
1207
1238
      order.CommaText := ReorderYList;
1208
1239
      SetLength(FYOrder, order.Count);
1209
1240
      for i := 0 to High(FYOrder) do
1210
 
        FYOrder[i] :=
1211
 
          EnsureRange(StrToIntDef(order[i], 0), 0, FOrigin.YCount - 2);
1212
 
      FYCount := Length(FYOrder) + 1;
 
1241
        FYOrder[i] := EnsureRange(StrToIntDef(order[i], 0), 0, FOriginYCount - 1);
1213
1242
    finally
1214
1243
      order.Free;
1215
1244
    end;
1216
1245
  end;
 
1246
  FYCount := Length(FYOrder);
1217
1247
 
1218
 
  SetLength(FItem.YList, Length(FYOrder));
 
1248
  SetLength(FItem.YList, Max(High(FYOrder), 0));
1219
1249
  Changed(nil);
1220
1250
end;
1221
1251