BaseCont.pas
上传用户:yj_qiu
上传日期:2022-08-08
资源大小:23636k
文件大小:30k
源码类别:

游戏引擎

开发平台:

Delphi

  1. (*
  2.  @Abstract(Basic containers unit)
  3.  (C) 2006 George "Mirage" Bakhtadze. <a href="http://www.casteng.com">www.casteng.com</a> <br>
  4.  The source code may be used under either MPL 1.1 or LGPL 2.1 license. See included license.txt file <br>
  5.  Created May 30, 2006 <br>
  6.  Unit contains basic container classes
  7. *)
  8. {$Include GDefines.inc}
  9. unit basecont;
  10. interface
  11. uses BaseTypes, Basics,
  12.      Props;
  13. const
  14.   // Default capacity for hash map containers
  15.   DefaultHashmapCapacity = 16;
  16. type
  17.   // Class of items which can be contained only in a one container without duplicates in other containers
  18.   TBaseUniqueItem = class
  19.     constructor Create; virtual;
  20.   private
  21.     // Index in a containing collection
  22.     Index: Integer;
  23.   end;
  24.   // Container for @Link(TBaseUniqueItem)
  25.   TUniqueItemCollection = class
  26.   protected
  27.     FTotalItems: Integer;
  28.   public
  29.     GrowStep: Integer;                             // Memory usage grow step
  30.     Ordered: Boolean;                              // Set to True to preserve item's order
  31.     Items: array of TBaseUniqueItem;
  32.     constructor Create;
  33.     destructor Destroy; override;
  34.     function Add(AItem: TBaseUniqueItem): TBaseUniqueItem;
  35.     function Exists(AItem: TBaseUniqueItem): Boolean;
  36.     function Remove(AItem: TBaseUniqueItem): Boolean;
  37.     procedure Clear;
  38.     property TotalItems: Integer read FTotalItems;
  39.   end;
  40.   // Class of items with reference counting and universal equivalence checking
  41.   TReferencedItem = class
  42.     constructor Create;
  43.     // Increase and return reference counter
  44.     function IncRef: Integer;
  45.     // Decrease and return reference counter. If it becomes zero destructor is called
  46.     function DecRef: Integer;
  47.     // Returns @True if the item has the same class and parameters as <b>AItem</b>
  48.     function IsSameItem(AItem: TReferencedItem): Boolean; virtual;
  49.     { Fills <b>Parameters</b> with a pointer to public or internal (depending on value of <b>Internal</b>) parameters and
  50.       returns size of the parameters in 32-bit dwords.
  51.       Descendant classes should override this method to introduce their own parameters }
  52.     function RetrieveParameters(out Parameters: Pointer; Internal: Boolean): Integer; virtual;
  53.   private
  54.     FRefCount: Integer;
  55. //    NextItem: TReferencedItem;                   // For grouping by class
  56.   public
  57.     // Reference counter
  58.     property RefCount: Integer read FRefCount;
  59.   end;
  60.   CReferencedItem = class of TReferencedItem;
  61.   // Class which manages reference-counted items
  62.   TReferencedItemManager = class
  63.     // Memory usage grow step
  64.     GrowStep: Integer;
  65.     // Items
  66.     Items: array of TReferencedItem;
  67.     constructor Create;
  68.     destructor Destroy; override;
  69.     // Returns an item with the same class and parameter set as the specified one. If not found returns nil.
  70.     function FindSameItem(AItem: TReferencedItem): TReferencedItem;
  71.     // If the same as the given item is present in manager returns it, otherwise adds the given item and returns it
  72.     function AddItem(Item: TReferencedItem): TReferencedItem;
  73.     // Clears and release all contained items
  74.     procedure Clear;
  75.   private
  76.     // Total items in manager
  77.     FTotalItems: Integer;
  78.   public
  79.     // Total items in manager
  80.     property TotalItems: Integer read FTotalItems;
  81.   end;
  82.   // Hash map key location data structure
  83.   TKeyLocation  = packed record Index1, Index2: Integer; end;
  84.   // Pointer-to-pointer map key type
  85.   KeyType = Pointer;
  86.   // Pointer-to-pointer map value type
  87.   ValueType = Pointer;
  88.   // Hash function delegate
  89.   TPointerHashFunction = function(Key: KeyType): Integer of object;
  90.   // Hash map action delegate
  91.   TPointerPointerDoFunction = function(Key: KeyType; Value: ValueType): Boolean of object;
  92.   // Hash map key-value pair
  93.   TKeyValuePair = packed record Key: KeyType; Value: ValueType; end;
  94.   // Data structure to store values of hash map
  95.   TValueStore   = packed record Count: Integer; Data: array of TKeyValuePair; end;
  96.   { @Abstract(Pointer to pointer hash map)
  97.     A data structure which maps a pointer to another pointer in constant time (O(1)) }
  98.   TPointerPointerMap = class
  99.   private
  100.     FValues: array of TValueStore;
  101.     FCapacity, GrowStep: Integer;
  102.     function LocateKey(const Key: KeyType; out KeyLocation: TKeyLocation; Add: Boolean): Boolean;
  103.     function GetValue(const Key: KeyType): ValueType;
  104.     procedure SetValue(const Key: KeyType; const Value: ValueType);
  105.     procedure SetCapacity(ACapacity: Integer);
  106.     function DefaultHash(Key: KeyType): Integer; virtual;
  107.   public
  108.     // Current hash function
  109.     HashFunction: TPointerHashFunction;
  110.     constructor Create; overload;
  111.     constructor Create(Capacity: Integer); overload;
  112.     // Calls a delegate for each value stored in the map
  113.     procedure DoForEach(DoFunction: TPointerPointerDoFunction);
  114.     // Values retrieved by pointer key
  115.     property Values[const Key: KeyType]: ValueType read GetValue write SetValue; default;
  116.     // Determines hash function values range which is currently used.
  117.     property Capacity: Integer read FCapacity;
  118.   end;
  119.   // Container for untyped temporary data
  120.   TTempContainer = class
  121.     TotalDataChains, MaxDataChains: Integer;
  122.     function AddData(Src: Pointer; Size: Integer): Integer; virtual;
  123.     procedure RemoveData(ID: Integer); virtual;
  124.     function GetData(ID: Integer): Pointer; virtual;
  125.     function GetDataSize(ID: Integer): Integer; virtual;
  126.     function ExtractData(ID: Integer): Pointer; virtual;
  127.     destructor Destroy; override;
  128.   protected
  129.     Data: Pointer;
  130.     DataSize: Integer;
  131.     DataChains: array of Pointer;
  132.     DataSizes: array of Integer;
  133.   end;
  134.   TQueue = class
  135.     TotalElements: Integer;
  136.     ElementSize, Capacity, CapacityStep: Cardinal;
  137.     constructor Create;
  138.     procedure Allocate(NewCapacity: Cardinal); virtual;
  139.     function Copy: TQueue; virtual;
  140.     procedure Delete(Index: Cardinal); virtual;
  141.     procedure Remove(Index, Count: Cardinal); virtual;
  142.     procedure MakeEmpty; virtual;
  143.     procedure Clear; virtual;
  144.     function Save(const Stream: Basics.TStream): Boolean; virtual;
  145.     function Load(const Stream: Basics.TStream): Boolean; virtual;
  146.     destructor Destroy; override;
  147.   protected
  148.     FData: Pointer;
  149.   end;
  150.   // Data structure represented with samples. Values between samples are calculated with some interpolation algorithm.
  151.   TSampledData = class
  152.   private
  153.     // [1..TotalSamples]
  154.     function GetIndex(AX: Single): Integer;
  155.     // Finds and places into NewIndex an index for the given X value and returns True if the index is found or False if it is a new value
  156.     function FindIndex(AX: Single): Integer;
  157.     function GetSampleX(Index: Integer): Single;
  158.     procedure SetSamplesX(Index: Integer; const Value: Single);
  159.     procedure SetTotalSamples(const Value: Integer); virtual;
  160.     procedure SetMaxX(const Value: Single);
  161.     procedure SetMinX(const Value: Single);
  162.   protected
  163.     FSampleX: array of Single;
  164.     FThreshold: Single;
  165.     FMinX, FMaxX: Single;
  166.     FTotalSamples: Integer;
  167.     PropertyValueType: TPropertyValueType;
  168.     function GetDataSize: Integer; virtual; abstract;
  169.     procedure DataExport(Dest: Pointer); virtual; abstract;
  170.     procedure DataImport(Src: Pointer); virtual; abstract;
  171.     // Should be implemented in descendants and move a sample value from SrcIndex to DestIndex to maintain sorted order
  172.     procedure MoveSample(SrcIndex, DestIndex: Integer); virtual; abstract;
  173.   public
  174.     Enabled: Boolean;
  175.     constructor Create; virtual;
  176.     procedure Reset; virtual;
  177.     // Adds a property which represents all samples
  178.     procedure AddAsProperty(Properties: Props.TProperties; const AName: string); virtual;
  179.     // Reads samples from properties
  180.     procedure SetFromProperty(Properties: Props.TProperties; const AName: string); virtual;
  181.     // Deletes the specified sample
  182.     procedure Delete(Index: Integer);
  183.     property TotalSamples: Integer read FTotalSamples write SetTotalSamples;
  184.     property MinX: Single read FMinX write SetMinX;
  185.     property MaxX: Single read FMaxX write SetMaxX;
  186.     property SampleX[Index: Integer]: Single read GetSampleX write SetSamplesX;
  187.   end;
  188.   // Sampled single precision floats
  189.   TSampledFloats = class(TSampledData)
  190.   private
  191.     FSamples: array of Single;
  192.     FMinY, FMaxY: Single;
  193.     FRange, FRangeInv: Single;
  194.     function GetSampleValue(Index: Integer): Single;
  195.     procedure SetSampleValue(Index: Integer; const Value: Single);
  196.     function GetValue(X: Single): Single;
  197.     procedure SetTotalSamples(const Value: Integer); override;
  198.     procedure SetMaxY(const Value: Single);
  199.     procedure SetMinY(const Value: Single);
  200.   protected
  201.     function GetDataSize: Integer; override;
  202.     procedure DataExport(Dest: Pointer); override;
  203.     procedure DataImport(Src: Pointer); override;
  204.     procedure MoveSample(SrcIndex, DestIndex: Integer); override;
  205.   public
  206.     // Value used as default while resetting
  207.     DefaultValue: Single;
  208.     
  209.     constructor Create; override;
  210.     // Reset all to default value
  211.     procedure Reset; override;
  212.     // Creates a property hierarchy with the given name in the specified property collection
  213.     procedure AddAsProperty(Properties: Props.TProperties; const AName: string); override;
  214.     // Applies property
  215.     procedure SetFromProperty(Properties: Props.TProperties; const AName: string); override;
  216.     // Insert sample
  217.     procedure Insert(AX, AY: Single);
  218.     // Value range (MaxY - MinY)
  219.     property Range: Single read FRange;
  220.     // 1/Range
  221.     property RangeInv: Single read FRangeInv;
  222.     // Minimal sample value
  223.     property MinY: Single read FMinY write SetMinY;
  224.     // Maximal sample value
  225.     property MaxY: Single read FMaxY write SetMaxY;
  226.     // Value of sample specified by index
  227.     property SampleValue[Index: Integer]: Single read GetSampleValue write SetSampleValue;
  228.     // Interpolated value
  229.     property Value[X: Single]: Single read GetValue;
  230.     
  231.   end;
  232.   // Color gradient represented with color samples and interpolation between the samples
  233.   TSampledGradient = class(TSampledData)
  234.   private
  235.     FSamples: array of TColor;
  236.     function GetSampleValue(Index: Integer): TColor;
  237.     procedure SetSampleValue(Index: Integer; const Value: TColor);
  238.     function GetValue(X: Single): TColor;
  239.     procedure SetTotalSamples(const Value: Integer); override;
  240.   protected
  241.     function GetDataSize: Integer; override;
  242.     procedure DataExport(Dest: Pointer); override;
  243.     procedure DataImport(Src: Pointer); override;
  244.     procedure MoveSample(SrcIndex, DestIndex: Integer); override;
  245.   public
  246.     constructor Create; override;
  247.     procedure Reset; override;
  248.     procedure Insert(AX: Single; AColor: TColor);
  249.     property SampleValue[Index: Integer]: TColor read GetSampleValue write SetSampleValue;
  250.     property Value[X: Single]: TColor read GetValue;
  251.   end;
  252.   function CreateSampledFloats(MinValue, MaxValue, DefValue: Single): TSampledFloats;
  253. implementation
  254. { TBaseUniqueItem }
  255. constructor TBaseUniqueItem.Create;
  256. begin
  257.   Index := -1;
  258. end;
  259. { TUniqueItemCollection }
  260. constructor TUniqueItemCollection.Create;
  261. begin
  262.   GrowStep := 1;
  263. end;
  264. destructor TUniqueItemCollection.Destroy;
  265. begin
  266.   Items := nil;
  267.   inherited;
  268. end;
  269. function TUniqueItemCollection.Add(AItem: TBaseUniqueItem): TBaseUniqueItem;
  270. begin
  271.   Result := nil;
  272.   if Exists(AItem) then Exit;
  273.   Inc(FTotalItems);
  274.   if Length(Items) < FTotalItems then SetLength(Items, Length(Items) + GrowStep);
  275.   Items[FTotalItems-1] := AItem;
  276.   Items[FTotalItems-1].Index := FTotalItems-1;
  277. end;
  278. function TUniqueItemCollection.Exists(AItem: TBaseUniqueItem): Boolean;
  279. begin
  280.   Result := False;
  281.   if AItem = nil then Exit;
  282.   Result := (AItem.Index >= 0) and (AItem.Index < FTotalItems) and (Items[AItem.Index] = AItem);
  283. //  for i := 0 to FTotalItems-1 do if Items[i] = AItem then Exit;
  284. end;
  285. function TUniqueItemCollection.Remove(AItem: TBaseUniqueItem): Boolean;
  286. var Index: Integer;
  287. begin
  288.   Result := False;
  289.   if AItem = nil then Exit;
  290.   if Exists(AItem) then begin
  291.     Index := AItem.Index;
  292.     AItem.Index := -1;
  293.     if Ordered then begin
  294.       while Index < FTotalItems-1 do begin
  295.         Items[Index] := Items[Index+1];
  296.         Items[Index].Index := Index;
  297.         Inc(Index);
  298.       end;
  299.     end else begin
  300.       Items[Index] := Items[FTotalItems-1];
  301.       Items[Index].Index := Index;
  302.     end;
  303.     Dec(FTotalItems);
  304.     Result := True;
  305.   end;// else Assert(False, 'TUniqueItemCollection.Remove: Item not found');
  306. end;
  307. procedure TUniqueItemCollection.Clear;
  308. begin
  309.   FTotalItems := 0; SetLength(Items, 0);
  310. end;
  311. { TReferencedItem }
  312. constructor TReferencedItem.Create;
  313. begin
  314.   FRefCount := 1;
  315. //  NextItem  := nil;
  316. end;
  317. function TReferencedItem.IncRef: Integer;
  318. begin
  319.   Inc(FRefCount); Result := FRefCount;
  320. end;
  321. function TReferencedItem.DecRef: Integer;
  322. begin
  323.   Dec(FRefCount);
  324.   Result := FRefCount;
  325.   if FRefCount <= 0 then Free;
  326. end;
  327. function TReferencedItem.IsSameItem(AItem: TReferencedItem): Boolean;
  328. var Par1Num, Par2Num: Integer; Par1, Par2: Pointer;
  329. begin
  330.   Result := False;
  331.   if ClassType <> AItem.ClassType then Exit;
  332.   Par1Num := RetrieveParameters(Par1, True);
  333.   Par2Num := AItem.RetrieveParameters(Par2, True);
  334.   if Par1Num <> Par2Num then Exit;                                 // Unlikely case
  335.   if not CmpMem(Par1, Par2, Par1Num*4) then Exit;                  // Exit if some parameters do not match
  336.   Result := True;
  337. end;
  338. function TReferencedItem.RetrieveParameters(out Parameters: Pointer; Internal: Boolean): Integer;
  339. begin
  340.   Parameters := nil; Result := 0;
  341. end;
  342. { TReferencedItemManager }
  343. constructor TReferencedItemManager.Create;
  344. begin
  345.   GrowStep := 1;
  346. end;
  347. function TReferencedItemManager.FindSameItem(AItem: TReferencedItem): TReferencedItem;
  348. var i: Integer;
  349. begin
  350.   Result := nil; exit;
  351.   i := FTotalItems-1;
  352.   while (i >= 0) and (not Items[i].IsSameItem(AItem)) do Dec(i);
  353.   if i >= 0 then Result := Items[i] else Result := nil;
  354. end;
  355. function TReferencedItemManager.AddItem(Item: TReferencedItem): TReferencedItem;
  356. begin
  357.   Result := FindSameItem(Item);
  358.   if Result <> nil then Exit;
  359.   Result := Item;
  360. // Add an item
  361.   if Length(Items) <= FTotalItems then SetLength(Items, Length(Items) + GrowStep);
  362.   Items[FTotalItems] := Item;
  363.   Inc(FTotalItems);
  364. end;
  365. procedure TReferencedItemManager.Clear;
  366. var i: Integer;
  367. begin
  368.   for i := 0 to TotalItems-1 do Items[i].Free;
  369.   Items := nil;
  370.   FTotalItems := 0;
  371. end;
  372. destructor TReferencedItemManager.Destroy;
  373. begin
  374.   Clear;
  375.   inherited;
  376. end;
  377. { TQueue }
  378. constructor TQueue.Create;
  379. begin
  380.   CapacityStep := 256;
  381.   Clear;
  382. end;
  383. procedure TQueue.Allocate(NewCapacity: Cardinal);
  384. begin
  385.   Capacity := NewCapacity;
  386. end;
  387. function TQueue.Copy: TQueue;
  388. begin
  389.   Result := ClassType.Create as TQueue;
  390.   Result.ElementSize := ElementSize;
  391.   Result.CapacityStep := CapacityStep;
  392.   Result.Allocate(Capacity);
  393.   Result.TotalElements := TotalElements;
  394.   if TotalElements > 0 then Move(FData^, Result.FData^, Cardinal(TotalElements) * ElementSize);
  395. end;
  396. function TQueue.Save(const Stream: Basics.TStream): Boolean;
  397. begin
  398.   Result := False;
  399.   if not Stream.WriteCheck(TotalElements, SizeOf(TotalElements)) then Exit;
  400.   if TotalElements > 0 then if not Stream.WriteCheck(FData^, ElementSize * Cardinal(TotalElements)) then Exit;
  401.   Result := True;
  402. end;
  403. function TQueue.Load(const Stream: Basics.TStream): Boolean;
  404. begin
  405.   Result := False;
  406.   if not Stream.ReadCheck(TotalElements, SizeOf(TotalElements)) then Exit;
  407.   Allocate(TotalElements);
  408.   if TotalElements > 0 then if not Stream.ReadCheck(FData^, ElementSize * Cardinal(TotalElements)) then Exit;
  409.   Result := True;
  410. end;
  411. procedure TQueue.Delete(Index: Cardinal);
  412. begin
  413.   Dec(TotalElements);
  414.   if Index < Cardinal(TotalElements) then
  415.    Move(Pointer(Cardinal(FData) + Cardinal(TotalElements) * ElementSize)^, Pointer(Cardinal(FData) + Index * ElementSize)^, ElementSize);
  416. end;
  417. procedure TQueue.Clear;
  418. begin
  419.   TotalElements := 0; Capacity := 0; FData := nil;
  420. end;
  421. procedure TQueue.MakeEmpty;
  422. begin
  423.   TotalElements := 0;
  424. end;
  425. procedure TQueue.Remove(Index, Count: Cardinal);
  426. begin
  427.   if Count = 0 then Exit;
  428.   Assert((TotalElements >= 0) and (Index+Count-1 < Cardinal(TotalElements)), 'CommandQueue.Remove: Index out of bounds');
  429.   Move(Pointer(Cardinal(FData) + (Index + Count) * ElementSize)^, Pointer(Cardinal(FData) + Index * ElementSize)^, Cardinal(MaxI(0, Cardinal(TotalElements) - Index - Count)) * ElementSize);
  430.   Dec(TotalElements, Count);
  431. end;
  432. destructor TQueue.Destroy;
  433. begin
  434.   Clear;
  435.   inherited;
  436. end;
  437. { TTempContainer }
  438. function TTempContainer.AddData(Src: Pointer; Size: Integer): Integer;
  439. var i: Integer;
  440. begin
  441.   Result := -1;
  442.   for i := 0 to Length(DataChains)-1 do if DataChains[i] = nil then begin    // Try to find an unused ID
  443.     Result := i; Break;
  444.   end;
  445.   if Result = -1 then begin
  446.     Result := MaxDataChains;
  447.     Inc(MaxDataChains);
  448.     SetLength(DataChains, MaxDataChains);
  449.     SetLength(DataSizes, MaxDataChains);
  450.   end;
  451.   GetMem(DataChains[Result], Size);
  452.   Move(Src^, DataChains[Result]^, Size);
  453.   DataSizes[Result] := Size;
  454.   Inc(TotalDataChains);
  455. end;
  456. function TTempContainer.GetData(ID: Integer): Pointer;
  457. begin
  458.   Result := nil;
  459.   if (ID < 0) or (ID >= MaxDataChains) then Exit;
  460.   Result := DataChains[ID];
  461. end;
  462. function TTempContainer.GetDataSize(ID: Integer): Integer;
  463. begin
  464.   Result := 0;
  465.   if (ID < 0) or (ID >= MaxDataChains) then Exit;
  466.   Result := DataSizes[ID];
  467. end;
  468. procedure TTempContainer.RemoveData(ID: Integer);
  469. begin
  470.   if (ID < 0) or (ID >= MaxDataChains) or (DataChains[ID] = nil) then Exit;
  471.   FreeMem(DataChains[ID], DataSizes[ID]);
  472.   DataChains[ID] := nil;
  473.   Dec(TotalDataChains);
  474.   Assert(TotalDataChains >= 0, 'TempData: TotalDataChains < 0');
  475. end;
  476. function TTempContainer.ExtractData(ID: Integer): Pointer;
  477. begin
  478.   Result := GetData(ID);
  479.   RemoveData(ID);
  480. end;
  481. destructor TTempContainer.Destroy;
  482. var i: Integer;
  483. begin
  484.   for i := 0 to MaxDataChains-1 do RemoveData(i);
  485.   SetLength(DataChains, 0);
  486.   TotalDataChains := 0; MaxDataChains := 0;
  487.   inherited;
  488. end;
  489. { TPointerPointerMap }
  490. constructor TPointerPointerMap.Create;
  491. begin
  492.   Create(DefaultHashmapCapacity);
  493. end;
  494. constructor TPointerPointerMap.Create(Capacity: Integer);
  495. begin
  496.   inherited Create;
  497.   HashFunction := {$IFDEF OBJFPCEnable}@{$ENDIF}DefaultHash;
  498.   GrowStep := Capacity;
  499.   SetCapacity(Capacity);
  500. end;
  501. procedure TPointerPointerMap.DoForEach(DoFunction: TPointerPointerDoFunction);
  502. var i, j: Integer;
  503. begin
  504.   if @DoFunction = nil then Exit;
  505.   for i := 0 to Capacity-1 do for j := 0 to FValues[i].Count-1 do if DoFunction(FValues[i].Data[j].Key, FValues[i].Data[j].Value) then Exit;
  506. end;
  507. procedure TPointerPointerMap.SetCapacity(ACapacity: Integer);
  508. begin
  509.   FCapacity := ACapacity;
  510.   SetLength(FValues, FCapacity);
  511. end;
  512. function TPointerPointerMap.LocateKey(const Key: KeyType; out KeyLocation: TKeyLocation; Add: Boolean): Boolean;
  513. var i: Integer;
  514. begin
  515.   Result := True;
  516.   KeyLocation.Index1 := HashFunction(Key);
  517.   for i := 0 to FValues[KeyLocation.Index1].Count-1 do
  518.     if FValues[KeyLocation.Index1].Data[i].Key = Key then begin
  519.       KeyLocation.Index2 := i;
  520.       Exit;
  521.     end;
  522.   Result := Add;
  523.   if Add then begin
  524.     KeyLocation.Index2 := FValues[KeyLocation.Index1].Count;
  525.     if Length(FValues[KeyLocation.Index1].Data) <= FValues[KeyLocation.Index1].Count then
  526.      SetLength(FValues[KeyLocation.Index1].Data, Length(FValues[KeyLocation.Index1].Data) + GrowStep);
  527.     FValues[KeyLocation.Index1].Data[KeyLocation.Index2].Key := Key;
  528.     Inc(FValues[KeyLocation.Index1].Count);
  529.   end;
  530. end;
  531. function TPointerPointerMap.GetValue(const Key: KeyType): ValueType;
  532. var  KeyLoc: TKeyLocation;
  533. begin
  534.   Result := nil;
  535.   if not LocateKey(Key, KeyLoc, False) then Exit;
  536.   Result := FValues[KeyLoc.Index1].Data[KeyLoc.Index2].Value;
  537. end;
  538. procedure TPointerPointerMap.SetValue(const Key: KeyType; const Value: ValueType);
  539. var  KeyLoc: TKeyLocation;
  540. begin
  541.   if not LocateKey(Key, KeyLoc, True) then Exit;
  542.   FValues[KeyLoc.Index1].Data[KeyLoc.Index2].Value := Value;
  543. end;
  544. function TPointerPointerMap.DefaultHash(Key: KeyType): Integer;
  545. const K = 0.6180339887; // (Sqrt(5) - 1) / 2
  546. begin
  547.   Result := Trunc(FCapacity * (Frac(Integer(Key) * K)));
  548. end;
  549. { TSampledData }
  550. function TSampledData.GetIndex(AX: Single): Integer;
  551. begin
  552.   Result := FTotalSamples;
  553.   while (Result > 0) and (AX < FSampleX[Result-1]) do Dec(Result);
  554. end;
  555. function TSampledData.FindIndex(AX: Single): Integer;
  556. var i: Integer; Found: Boolean;
  557. begin
  558.   AX := ClampS(AX, MinX, MaxX);
  559.   Result := GetIndex(AX);
  560.   Found := (Result > 0) and (Abs(AX - FSampleX[Result-1]) < FThreshold);
  561.   if Found then
  562.     Result := Result-1
  563.   else
  564.     Found := (Result < FTotalSamples) and (Abs(AX - FSampleX[Result]) < FThreshold);
  565.   if not Found then begin
  566.     // Grow points array if needed
  567.     TotalSamples := TotalSamples + 1;
  568.     // Shift Samples array
  569.     for i := FTotalSamples - 1 downto Result+1 do begin
  570.       FSampleX[i] := FSampleX[i-1];
  571.       MoveSample(i-1, i);           // Do the same move for values
  572.     end;
  573.     if (FTotalSamples <= 2) then FSampleX[Result] := AX;
  574.   end;
  575.   if (Result > 0) and (Result < FTotalSamples-1) then FSampleX[Result] := AX;
  576. end;
  577. function TSampledData.GetSampleX(Index: Integer): Single;
  578. begin
  579.   Result := FSampleX[ClampI(Index, 0, FTotalSamples-1)];
  580. end;
  581. procedure TSampledData.SetSamplesX(Index: Integer; const Value: Single);
  582. begin
  583.   if (Index > 0) and (Index < TotalSamples-1) then
  584.     FSampleX[Index] := ClampS(Value, FSampleX[Index-1] + FThreshold, FSampleX[Index+1] - FThreshold);
  585. end;
  586. procedure TSampledData.SetTotalSamples(const Value: Integer);
  587. const ArrayGrowStep = 1;                  // Grow points array by ArrayGrowStep elements at once for better performance
  588. begin
  589.   FTotalSamples := Value;
  590.   if Length(FSampleX) < FTotalSamples then
  591.     SetLength(FSampleX, FTotalSamples + ArrayGrowStep);
  592. end;
  593. procedure TSampledData.SetMinX(const Value: Single);
  594. begin
  595.   FMinX := Value;
  596.   FSampleX[0] := FMinX;
  597. end;
  598. procedure TSampledData.SetMaxX(const Value: Single);
  599. begin
  600.   FMaxX := Value;
  601.   FSampleX[FTotalSamples-1] := FMaxX;
  602. end;
  603. procedure TSampledData.Reset;
  604. begin
  605.   TotalSamples := 2;
  606.   MinX := MinX;
  607.   MaxX := MaxX;
  608. end;
  609. constructor TSampledData.Create;
  610. begin
  611.   PropertyValueType := vtSingleSample;
  612.   FThreshold := 0.02;
  613.   TotalSamples := 2;
  614.   MinX := 0;
  615.   MaxX := 1;
  616.   Enabled := False;
  617.   Reset;
  618. end;
  619. procedure TSampledData.AddAsProperty(Properties: Props.TProperties; const AName: string);
  620. var Data: Pointer;
  621. begin
  622.   if not Assigned(Properties) then Exit;
  623.   if TotalSamples > 0 then begin
  624.     GetMem(Data, TotalSamples * (SizeOf(Single) + GetDataSize()));
  625.     Move(FSampleX[0], Data^, TotalSamples * SizeOf(Single));
  626.     DataExport(PtrOffs(Data, TotalSamples * SizeOf(Single)));
  627.   end else Data := nil;
  628.   Properties.Add(AName, PropertyValueType, [], IntToStrA(Cardinal(Data)), IntToStrA(TotalSamples * (SizeOf(Single) + GetDataSize())), '');
  629.   Properties.Add(AName + '$Reset',   vtBoolean, [], OnOffStr[False], '');
  630.   Properties.Add(AName + '$Enabled', vtBoolean, [], OnOffStr[Enabled], '');
  631.   Properties.Add(AName + '$Min X', vtSingle, [], FloatToStrA(FMinX), '');
  632.   Properties.Add(AName + '$Max X', vtSingle, [], FloatToStrA(FMaxX), '');
  633. end;
  634. procedure TSampledData.SetFromProperty(Properties: Props.TProperties; const AName: string);
  635. var Buf: Pointer;
  636. begin
  637.   if not Assigned(Properties) then Exit;
  638.   if Properties.Valid(AName) then begin
  639.     TotalSamples := Properties.GetBinPropertySize(AName, SizeOf(Single) + GetDataSize());
  640. //    SetLength(FSampleX, FTotalSamples);
  641.     if Assigned(FSampleX) then begin
  642.       GetMem(Buf, TotalSamples * (SizeOf(Single) + GetDataSize()));
  643.       Properties.RetrieveBinPropertyData(AName, Buf);
  644.       Move(Buf^, FSampleX[0], TotalSamples * SizeOf(Single));
  645.       DataImport(PtrOffs(Buf, TotalSamples * SizeOf(Single)));
  646.       FreeMem(Buf);
  647.     end;
  648.   end;
  649.   if (TotalSamples < 2) or
  650.      Properties.Valid(AName + '$Reset') and (Properties.GetAsInteger(AName + '$Reset') > 0) then Reset();
  651.   if Properties.Valid(AName + '$Enabled') then Enabled := Properties.GetAsInteger(AName + '$Enabled') > 0;
  652.   if Properties.Valid(AName + '$Min X') then MinX := StrToFloatDefA(Properties[AName + '$Min X'], 0);
  653.   if Properties.Valid(AName + '$Max X') then MaxX := StrToFloatDefA(Properties[AName + '$Max X'], 0);
  654. end;
  655. procedure TSampledData.Delete(Index: Integer);
  656. var i: Integer;
  657. begin
  658.   if (Index > 0) and (Index < TotalSamples-1) then begin
  659.     for i := Index to TotalSamples - 2 do begin
  660.       MoveSample(i+1, i);
  661.       FSampleX[i] := FSampleX[i+1];
  662.     end;
  663.     TotalSamples := TotalSamples - 1;
  664.   end;  
  665. end;
  666. { TSampledFloats }
  667. function CreateSampledFloats(MinValue, MaxValue, DefValue: Single): TSampledFloats;
  668. begin
  669.   Result := TSampledFloats.Create;
  670.   Result.MaxY := MaxValue;
  671.   Result.MinY := MinValue;
  672.   Result.DefaultValue := DefValue;
  673.   Result.Reset();
  674. end;
  675. function TSampledFloats.GetSampleValue(Index: Integer): Single;
  676. begin
  677.   Result := FSamples[ClampI(Index, 0, FTotalSamples-1)];
  678. end;
  679. procedure TSampledFloats.SetSampleValue(Index: Integer; const Value: Single);
  680. begin
  681.   if (Index >= 0) and (Index < TotalSamples) then
  682.     FSamples[Index] := ClampS(Value, MinY, MaxY);
  683. end;
  684. function TSampledFloats.GetValue(X: Single): Single;
  685. var Ind1, Ind2: Integer; K: Single;
  686. begin
  687.   Result := DefaultValue;
  688.   Ind1 := GetIndex(X)-1;
  689.   if (Ind1 < 0) or (Ind1 >= FTotalSamples) then Exit;
  690.   Ind2 := MinI(Ind1+1, FTotalSamples-1);
  691.   if abs(FSampleX[Ind1] - FSampleX[Ind2]) < epsilon then
  692.     K := 1
  693.   else
  694.     K := (FSampleX[Ind2] - X) / (FSampleX[Ind2] - FSampleX[Ind1]);
  695.   Result := FSamples[Ind1] * K + FSamples[Ind2] * (1-K);
  696. //  Result := FMinY + Result * (FMaxY - FMinY);
  697. end;
  698. procedure TSampledFloats.SetMaxY(const Value: Single);
  699. begin
  700.   FMaxY := Value;
  701.   FRange := FMaxY - FMinY;
  702.   if FRange > epsilon then FRangeInv := 1/FRange else FRangeInv := 0;
  703. end;
  704. procedure TSampledFloats.SetMinY(const Value: Single);
  705. begin
  706.   FMinY := Value;
  707.   FRange := FMaxY - FMinY;
  708.   if FRange > epsilon then FRangeInv := 1/FRange else FRangeInv := 0;
  709. end;
  710. procedure TSampledFloats.SetTotalSamples(const Value: Integer);
  711. begin
  712.   inherited;
  713.   SetLength(FSamples, Length(FSampleX));
  714. end;
  715. function TSampledFloats.GetDataSize: Integer;
  716. begin
  717.   Result := SizeOf(Single);
  718. end;
  719. procedure TSampledFloats.DataExport(Dest: Pointer);
  720. begin
  721.   if TotalSamples > 0 then Move(FSamples[0], Dest^, TotalSamples*GetDataSize());
  722. end;
  723. procedure TSampledFloats.DataImport(Src: Pointer);
  724. begin
  725.   Assert(Length(FSamples) = Length(FSampleX));
  726.   if TotalSamples > 0 then Move(Src^, FSamples[0], TotalSamples*GetDataSize());
  727. end;
  728. procedure TSampledFloats.MoveSample(SrcIndex, DestIndex: Integer);
  729. begin
  730.   FSamples[DestIndex] := FSamples[SrcIndex];
  731. end;
  732. constructor TSampledFloats.Create;
  733. begin
  734.   DefaultValue := 0.5;
  735.   inherited;
  736.   MinY := 0;
  737.   MaxY := 1;
  738. end;
  739. procedure TSampledFloats.Reset;
  740. begin
  741.   inherited;
  742.   FSamples[0] := DefaultValue;
  743.   FSamples[FTotalSamples-1] := DefaultValue;
  744. end;
  745. procedure TSampledFloats.AddAsProperty(Properties: Props.TProperties; const AName: string);
  746. begin
  747.   inherited;
  748.   Properties.Add(AName + '$Min Y', vtSingle, [], FloatToStrA(FMinY), '');
  749.   Properties.Add(AName + '$Max Y', vtSingle, [], FloatToStrA(FMaxY), '');
  750.   Properties.Add(AName + '$DefaultValue', vtSingle, [], FloatToStrA(DefaultValue), '');
  751. end;
  752. procedure TSampledFloats.SetFromProperty(Properties: Props.TProperties; const AName: string);
  753. begin
  754.   inherited;
  755.   if Properties.Valid(AName + '$Min Y') then MinY := StrToFloatDefA(Properties[AName + '$Min Y'], 0);
  756.   if Properties.Valid(AName + '$Max Y') then MaxY := StrToFloatDefA(Properties[AName + '$Max Y'], 0);
  757.   if Properties.Valid(AName + '$DefaultValue') then DefaultValue := StrToFloatDefA(Properties[AName + '$DefaultValue'], 0);
  758. end;
  759. procedure TSampledFloats.Insert(AX, AY: Single);
  760. begin
  761.   FSamples[FindIndex(AX)] := ClampS(AY, MinY, MaxY);
  762. end;
  763. { TSampledGradient }
  764. function TSampledGradient.GetSampleValue(Index: Integer): TColor;
  765. begin
  766.   Result := FSamples[ClampI(Index, 0, FTotalSamples-1)];
  767. end;
  768. procedure TSampledGradient.SetSampleValue(Index: Integer; const Value: TColor);
  769. begin
  770.   if (Index >= 0) and (Index < TotalSamples) then
  771.     FSamples[Index] := Value;
  772. end;
  773. function TSampledGradient.GetValue(X: Single): TColor;
  774. var Ind1, Ind2: Integer; K: Single;
  775. begin
  776.   Result.C := $808080FF;
  777.   Ind1 := GetIndex(X)-1;
  778.   if (Ind1 < 0) or (Ind1 >= FTotalSamples) then Exit;
  779.   Ind2 := MinI(Ind1+1, FTotalSamples-1);
  780.   if abs(FSampleX[Ind1] - FSampleX[Ind2]) < epsilon then
  781.     K := 1
  782.   else
  783.     K := (X - FSampleX[Ind1]) / (FSampleX[Ind2] - FSampleX[Ind1]);
  784.   Result := BlendColor(FSamples[Ind1], FSamples[Ind2], K)
  785. end;
  786. procedure TSampledGradient.SetTotalSamples(const Value: Integer);
  787. begin
  788.   inherited;
  789.   SetLength(FSamples, Length(FSampleX));
  790. end;
  791. function TSampledGradient.GetDataSize: Integer;
  792. begin
  793.   Result := SizeOf(TColor);
  794. end;
  795. constructor TSampledGradient.Create;
  796. begin
  797.   inherited;
  798.   PropertyValueType := vtGradientSample;
  799. end;
  800. procedure TSampledGradient.DataExport(Dest: Pointer);
  801. begin
  802.   if TotalSamples > 0 then Move(FSamples[0], Dest^, TotalSamples*GetDataSize());
  803. end;
  804. procedure TSampledGradient.DataImport(Src: Pointer);
  805. begin
  806.   if TotalSamples > 0 then Move(Src^, FSamples[0], TotalSamples*GetDataSize());
  807. end;
  808. procedure TSampledGradient.MoveSample(SrcIndex, DestIndex: Integer);
  809. begin
  810.   FSamples[DestIndex] := FSamples[SrcIndex];
  811. end;
  812. procedure TSampledGradient.Reset;
  813. begin
  814.   inherited;
  815.   FSamples[0].C := $FF000000;
  816.   FSamples[FTotalSamples-1].C := $FFFFFFFF;
  817. end;
  818. procedure TSampledGradient.Insert(AX: Single; AColor: TColor);
  819. begin
  820.   FSamples[FindIndex(AX)] := AColor;
  821. end;
  822. end.