lazarus-ccr/components/fpspreadsheet/source/common/fpspatterns.pas

968 lines
25 KiB
ObjectPascal

unit fpsPatterns;
{$mode objfpc}{$H+}
interface
uses
SysUtils, Classes, Contnrs, Math, Types, FPImage, FPCanvas,
fpsTypes, fpsChart;
const
FPS_GRAY06 = 'GRAY06';
FPS_GRAY12 = 'GRAY12';
FPS_GRAY25 = 'GRAY25';
FPS_GRAY50 = 'GRAY50';
FPS_GRAY75 = 'GRAY75';
FPS_HOR_THICK = 'HOR_THICK';
FPS_VERT_THICK = 'VERT_THICK';
FPS_DIAG_UP_THICK = 'DIAG_UP_THICK';
FPS_DIAG_DOWN_THICK = 'DIAG_DOWN_THICK';
FPS_HATCH_THICK = 'HATCH_THICK';
FPS_CROSS_THICK = 'CROSS_THICK';
FPS_HOR_THIN = 'HOR_THIN';
FPS_VERT_THIN = 'VERT_THIN';
FPS_DIAG_UP_THIN = 'DIAG_UP_THIN';
FPS_DIAG_DOWN_THIN = 'DIAG_DOWN_THIN';
FPS_HATCH_THIN = 'HATCH_THIN';
FPS_CROSS_THIN = 'CROSS_THIN';
FPS_HOR_NARROW = 'HOR_NARROW';
FPS_VERT_NARROW = 'VERT_NARROW';
FPS_DIAG_UP_NARROW = 'DIAG_UP_NARROW';
FPS_DIAG_DOWN_NARROW = 'DIAG_DOWN_NARROW';
FPS_HATCH_NARROW = 'HATCH_NARROW';
FPS_CROSS_NARROW = 'CROSS_NARROW';
FPS_CROSS_DOT = 'CROSS_DOT';
FPS_HATCH_DOT = 'HATCH_DOT';
FPS_BRICK_HOR = 'BRICK_HOR';
FPS_BRICK_DIAG = 'BRICK_DIAG';
FPS_CHECKERBOARD_LARGE = 'CHECKERBOARD_LARGE';
FPS_CHECKERBOARD_SMALL = 'CHECKERBOARD_SMALL';
FPS_DIAMOND = 'DIAMOND';
FPS_SHINGLE = 'SHINGLE';
FPS_WAVE = 'WAVE';
FPS_ZIGZAG = 'ZIGZAG';
type
TsLineFillPatternMultiplier = (lfpmSingle, lfpmDouble, lfpmTriple);
TsLineFillPattern = class
Distance: Single; // Distance of lines, in mm if > 0, in px if < 0
Angle: Single; // Rotation angle of pattern, in degrees
LineWidth: Single; // Line width of pattern, in mm if > 0, in px if < 0
Multiplier: TsLineFillPatternMultiplier;
end;
TsDotFillPattern = array[0..7] of Byte;
const
SOLID_DOT_FILL_PATTERN: TsDotFillPattern = ($FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF);
type
{ TsFillPattern
Combines the same visual pattern as a dot matrix and as vector strokes.
Vector strokes are supported only by ODS }
TsFillPattern = class
private
FName: String;
FDotPattern: TsDotFillPattern;
FLinePattern: TsLineFillPattern;
procedure SetLinePattern(AValue: TsLineFillPattern);
public
constructor Create(AName: String; ADotPattern: TsDotFillPattern;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier);
constructor Create(AName: String; ADotPattern: TsDotFillPattern);
constructor Create(AName: String;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier);
destructor Destroy; override;
procedure CopyFrom(ASource: TsFillPattern); virtual;
property Name: String read FName;
property DotPattern: TsDotFillPattern read FDotPattern write FDotPattern;
property LinePattern: TsLineFillPattern read FLinePattern write SetLinePattern;
end;
TsFillPatternList = class(TFPObjectlist)
private
function GetItem(AIndex: Integer): TsFillPattern;
procedure SetItem(AIndex: Integer; AValue: TsFillPattern);
protected
function AddOrReplace(APattern: TsFillPattern): Integer;
function FindSimilarDotPattern(ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): TsDotFillPattern;
public
function AddFillPattern(AName: String; ADotPattern: TsDotFillPattern;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier): Integer;
function AddDotFillPattern(AName: String; APattern: TsDotFillPattern): Integer; overload;
function AddLineFillPattern(AName: String; ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): Integer;
function FindByName(AName: String): TsFillPattern;
function IndexOfName(AName: String): Integer;
property Items[AIndex: Integer]: TsFillPattern read GetItem write SetItem; default;
end;
function StringToDotPattern(APattern: String): TsDotFillPattern;
procedure CreateFillPatterns(WithDefaultPatterns: Boolean);
function RegisterFillPattern(AName: String; ADotPattern: TsDotFillPattern): Integer;
function RegisterFillPattern(AName: String;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier): Integer;
function RegisterFillPattern(AName: String; ADotPattern: TsDotFillPattern;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier): Integer;
function ClonePattern(AIndex: Integer; AName: String): Integer;
function FindLineFillPatternIndex(ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): Integer;
function GetFillPattern(AIndex: Integer): TsFillPattern;
function IndexOfPattern(AName: String): Integer;
function NumPatterns: Integer;
implementation
var
FillPatternList: TsFillPatternList = nil;
const
WIDE_DISTANCE = 3.0;
NARROW_DISTANCE = 1.5;
THIN_LINE = 0.1;
THICK_LINE = 0.3;
function StringToDotPattern(APattern: String): TsDotFillPattern;
const
w = 8;
h = 8;
var
// L: TStrings;
i, j, n, x, y: Integer;
begin
n := Length(APattern);
if n > 64 then
n := 64;
FillChar(Result, Sizeof(Result), 0);
x := 0;
y := 0;
i := 0;
for j := 1 to n do
begin
if APattern[j] in ['x', 'X'] then
Result[i] := Result[i] or (1 shl x);
inc(x);
if x = 8 then
begin
inc(i);
x := 0;
end;
end;
(*
y := 0;
i := 0;
j := 1;
while y < h do
begin
x := 0;
while x < w do
begin
if (APattern[j] in ['x', 'X']) then
Result[i] := Result[i] or (1 shl x);
inc(x);
inc(j);
end;
inc(y);
inc(i);
end;
finally
L.Free;
end;
*)
end;
{ TsFillPattern }
constructor TsFillPattern.Create(AName: String; ADotPattern: TsDotFillPattern);
begin
inherited Create;
FName := AName;
FDotPattern := ADotPattern;
FLinePattern := nil; // no line pattern
end;
constructor TsFillPattern.Create(AName: String;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier);
begin
inherited Create;
FName := AName;
FDotPattern := SOLID_DOT_FILL_PATTERN;
FLinePattern := TsLineFillPattern.Create;
FLinePattern.Distance := ALineDistance;
FLinePattern.Angle := ALineAngle;
FLinePattern.LineWidth := ALineWidth;
FLinePattern.Multiplier := AMultiplier;
end;
constructor TsFillPattern.Create(AName: String; ADotPattern: TsDotFillPattern;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier);
begin
inherited Create;
FName := AName;
FDotPattern := ADotPattern;
FLinePattern := TsLineFillPattern.Create;
FLinePattern.Distance := ALineDistance;
FLinePattern.Angle := ALineAngle;
FLinePattern.LineWidth := ALineWidth;
FLinePattern.Multiplier := AMultiplier;
end;
destructor TsFillPattern.Destroy;
begin
FName := '';
FLinePattern.Free;
inherited;
end;
procedure TsFillPattern.CopyFrom(ASource: TsFillPattern);
begin
FName := ASource.Name;
FDotPattern := ASource.DotPattern;
SetLinePattern(ASource.LinePattern);
end;
procedure TsFillPattern.SetLinePattern(AValue: TsLineFillPattern);
begin
if AValue = nil then
begin
FLinePattern.Free;
FLinePattern := nil;
end else
begin
if FLinePattern = nil then
FLinePattern := TsLineFillPattern.Create;
FLinePattern.Distance := AValue.Distance;
FLinePattern.Angle := AValue.Angle;
FLinePattern.LineWidth := AValue.LineWidth;
FLinePattern.Multiplier := AValue.Multiplier;
end;
end;
(*
{ TsLineFillPatternStyle }
constructor TsLineFillPatternStyle.Create(AName: String;
ALineDistance, ALineAngle, ALineWidth: Single);
begin
inherited Create(AName);
FLineDistance := ALineDistance;
FLineAngle := ALineAngle;
FLineWidth := ALineWidth;
end;
procedure TsLineFillPatternStyle.CopyFrom(ASource: TsFillPatternStyle);
begin
if (ASource is TsLineFillPatternStyle) then
begin
FLineDistance := TsLineFillPatternStyle(ASource).LineDistance;
FLineAngle := TsLineFillPatternStyle(ASource).LineAngle;
FLineWidth := TsLineFillPatternStyle(ASource).LineWidth;
end;
inherited CopyFrom(ASource);
end;
// The pattern itself goes to infinity, limited by size of area to be filled.
function TsLineFillPatternStyle.GetHeight: Integer;
begin
Result := -1;
end;
function TsLineFillPatternStyle.GetWidth: Integer;
begin
Result := -1;
end;
{ TsDotFillPatternStyle }
constructor TsDotFillPatternStyle.Create(AName: String; APattern: String);
begin
inherited Create(AName);
StringToPattern(APattern);
end;
constructor TsDotFillPatternStyle.Create(AName: String; APattern: TBrushPattern);
begin
inherited Create(AName);
FPattern := APattern;
end;
procedure TsDotFillPatternStyle.CopyFrom(ASource: TsFillPatternStyle);
begin
if ASource is TsDotFillPatternStyle then
FPattern := TsDotFillPatternStyle(ASource).Pattern;
inherited CopyFrom(ASource);
end;
function TsDotFillPatternStyle.GetWidth: Integer;
begin
Result := SizeOf(TPenPattern);
end;
function TsDotFillPatternStyle.GetHeight: Integer;
begin
Result := SizeOf(TBrushPattern) div SizeOf(TPenPattern);
end;
procedure TsDotFillPatternStyle.StringToPattern(APattern: String);
var
L: TStrings;
w, h, i, j, x, y: Integer;
patt: String;
begin
L := TStringList.Create;
try
L.Text := APattern;
h := L.Count;
w := Length(L[0]);
for i := 1 to L.Count-1 do
if Length(L[i]) <> w then
raise Exception.Create('The lines in the pattern string must have the same lengths.');
FillChar(FPattern, Sizeof(FPattern), 0);
y := 0;
i := 0;
while y < SizeOf(TPenPattern) do
begin
if i >= L.Count then
i := 0;
patt := L[i];
x := 0;
j := 1;
while x < SizeOf(TPenPattern) do
begin
if j > w then
j := 1;
if (patt[j] in ['x', 'X']) then
FPattern[i] := FPattern[i] or (1 shl x);
inc(x);
inc(j);
end;
inc(y);
inc(i);
end;
finally
L.Free;
end;
end;
*)
{ TsFillPatternList }
function TsFillPatternList.AddDotFillPattern(AName: String;
APattern: TsDotFillPattern): Integer;
var
patt: TsFillPattern;
begin
patt := TsFillPattern.Create(AName, APattern);
Result := AddOrReplace(patt);
end;
function TsFillPatternList.AddFillPattern(AName: String; ADotPattern: TsDotFillPattern;
ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): Integer;
var
patt: TsFillPattern;
begin
patt := TsFillPattern.Create(AName,
ADotPattern,
ALineDistance, ALineAngle, ALineWidth, AMultiplier
);
Result := AddOrReplace(patt);
end;
function TsFillPatternList.AddLineFillPattern(AName: String;
ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): Integer;
var
patt: TsFillPattern;
begin
patt := TsFillPattern.Create(AName, ALineDistance, ALineAngle, ALineWidth, AMultiplier);
patt.DotPattern := FindSimilarDotPattern(ALineDistance, ALineAngle, ALineWidth, AMultiplier);
Result := AddOrReplace(patt);
end;
function TsFillPatternList.AddOrReplace(APattern: TsFillPattern): Integer;
var
idx: Integer;
begin
idx := IndexOfName(APattern.Name);
if idx = -1 then
Result := Add(APattern)
else
begin
Items[idx].CopyFrom(APattern);
// Items[idx].Free;
// Delete(idx);
Insert(idx, APattern);
Result := idx;
end;
end;
function TsFillPatternList.FindByName(AName: String): TsFillPattern;
var
i: Integer;
begin
for i := 0 to Count-1 do
if Items[i].Name = AName then
begin
Result := Items[i];
exit;
end;
Result := nil;
end;
function TsFillPatternList.FindSimilarDotPattern(ALineDistance, ALineAngle,
ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier): TsDotFillPattern;
var
sinAngle, cosAngle: Single;
similarIdx: Integer;
singleLine: Boolean;
begin
similarIdx := -1;
SinCos(DegToRad(ALineAngle), sinAngle, cosAngle);
singleLine := (AMultiplier = lfpmSingle);
if SameValue(sinAngle, 0.0, 0.2) then // about +/- 12°
begin
if (ALineDistance >= WIDE_DISTANCE) or (ALineDistance <= -8) then
similarIdx := IfThen(singleLine, fpsHorThin, fpsCrossThin)
else
similarIdx := IfThen(singleLine, fpsHorNarrow, fpsCrossNarrow);
end else
if SameValue(cosAngle, 0.0, 0.2) then // about 90 +/- 12°
begin
if (ALineDistance >= WIDE_DISTANCE) or (ALineDistance <= -8) then
similarIdx := IfThen(singleLine, fpsVertThin, fpsCrossThin)
else
similarIdx := IfThen(singleLine, fpsVertNarrow, fpsCrossNarrow);
end else
if ALineAngle > 0 then
begin
if (ALineDistance >= WIDE_DISTANCE) or (ALineDistance <= -8) then
similarIdx := IfThen(singleLine, fpsDiagUpThin, fpsHatchThin)
else
similarIdx := IfThen(singleLine, fpsDiagUpNarrow, fpsHatchNarrow);
end else
begin
if (ALineDistance >= WIDE_DISTANCE) or (ALineDistance <= -8) then
similarIdx := IfThen(singleLine, fpsDiagDownThin, fpsHatchThin)
else
similarIdx := IfThen(singleLine, fpsDiagDownNarrow, fpsHatchNarrow);
end;
if similarIdx > -1 then
Result := Items[similarIdx].DotPattern
else
Result := SOLID_DOT_FILL_PATTERN;
end;
function TsFillPatternList.IndexOfName(AName: String): Integer;
begin
for Result := 0 to Count-1 do
if Items[Result].Name = AName then
exit;
Result := -1;
end;
function TsFillPatternList.GetItem(AIndex: Integer): TsFillPattern;
begin
Result := TsFillPattern(inherited Items[AIndex]);
end;
procedure TsFillPatternList.SetItem(AIndex: Integer; AValue: TsFillPattern);
begin
TsFillPattern(inherited Items[AIndex]).CopyFrom(AValue);
end;
{ Globals }
const
GRAY75_PATTERN =
' xxx xxx'+
'xxxxxxxx'+
'xx xxx x'+
'xxxxxxxx'+
' xxx xxx'+
'xxxxxxxx'+
'xx xxx x'+
'xxxxxxxx';
GRAY50_PATTERN =
'x x x x '+
' x x x x'+
'x x x x '+
' x x x x'+
'x x x x '+
' x x x x'+
'x x x x '+
' x x x x';
GRAY25_PATTERN =
'x x '+
' x x '+
'x x '+
' x x '+
'x x '+
' x x '+
'x x '+
' x x ';
GRAY12_PATTERN =
'x x '+
' '+
' x x '+
' '+
'x x '+
' '+
' x x '+
' ';
GRAY06_PATTERN =
'x '+
' '+
' x '+
' '+
'x '+
' '+
' x '+
' ';
HOR_PATTERN_THIN =
'xxxxxxxx'+
' '+
' '+
' '+
' '+
' '+
' '+
' ';
HOR_PATTERN_THICK =
'xxxxxxxx'+
'xxxxxxxx'+
' '+
' '+
' '+
' '+
' '+
' ';
HOR_PATTERN_NARROW =
'xxxxxxxx'+
' '+
' '+
' '+
'xxxxxxxx'+
' '+
' '+
' ';
VERT_PATTERN_THIN =
'x '+
'x '+
'x '+
'x '+
'x '+
'x '+
'x '+
'x ';
VERT_PATTERN_THICK =
'xx '+
'xx '+
'xx '+
'xx '+
'xx '+
'xx '+
'xx '+
'xx ';
VERT_PATTERN_NARROW =
'x x '+
'x x '+
'x x '+
'x x '+
'x x '+
'x x '+
'x x '+
'x x ';
DIAG_DOWN_PATTERN_THIN =
'x '+
' x '+
' x '+
' x '+
' x '+
' x '+
' x '+
' x';
DIAG_DOWN_PATTERN_THICK =
'xx '+
' xx '+
' xx '+
' xx '+
' xx '+
' xx '+
' xx'+
'x x';
DIAG_DOWN_PATTERN_NARROW =
'x x '+
' x x '+
' x x '+
' x x'+
'x x '+
' x x '+
' x x '+
' x x';
DIAG_UP_PATTERN_THIN =
' x'+
' x '+
' x '+
' x '+
' x '+
' x '+
' x '+
'x ';
DIAG_UP_PATTERN_THICK =
'x x'+
' xx'+
' xx '+
' xx '+
' xx '+
' xx '+
' xx '+
'xx ';
DIAG_UP_PATTERN_NARROW =
' x x'+
' x x '+
' x x '+
'x x '+
' x x'+
' x x '+
' x x '+
'x x ';
HATCH_PATTERN_THIN =
' x'+
'x x '+
' x x '+
' x x '+
' x '+
' x x '+
' x x '+
'x x ';
HATCH_PATTERN_THICK =
'x x'+
'xx xx'+
' xx xx '+
' xxxx '+
' xx '+
' xxxx '+
' xx xx '+
'xx xx';
HATCH_PATTERN_NARROW =
' x x'+
'x x x x '+
' x x '+
'x x x x '+
' x x'+
'x x x x'+
' x x '+
'x x x x ';
HATCH_PATTERN_DOT =
'x '+
' '+
' x x '+
' '+
' x '+
' '+
' x x '+
' ';
CROSS_PATTERN_THIN =
' x '+
' x '+
' x '+
'xxxxxxxx'+
' x '+
' x '+
' x '+
' x ';
CROSS_PATTERN_THICK =
' xx '+
' xx '+
' xx '+
'xxxxxxxx'+
'xxxxxxxx'+
' xx '+
' xx '+
' xx ';
CROSS_PATTERN_NARROW =
' x x '+
'xxxxxxxx'+
' x x '+
' x x '+
' x x '+
'xxxxxxxx'+
' x x '+
' x x ';
CROSS_PATTERN_DOT =
'x x x x '+
' '+
'x '+
' '+
'x '+
' '+
'x '+
' ';
CHECKERBOARD_LARGE_PATTERN =
'xxxx '+
'xxxx '+
'xxxx '+
'xxxx '+
' xxxx'+
' xxxx'+
' xxxx'+
' xxxx';
CHECKERBOARD_SMALL_PATTERN =
'xx xx '+
'xx xx '+
' xx xx'+
' xx xx'+
'xx xx '+
'xx xx '+
' xx xx'+
' xx xx';
DIAMOND_PATTERN =
' x '+
' xxx '+
' xxxxx '+
'xxxxxxx '+
' xxxxx '+
' xxx '+
' x '+
' ';
BRICK_HOR_PATTERN =
'xxxxxxxx'+
'x '+
'x '+
'x '+
'xxxxxxxx'+
' x '+
' x '+
' x ';
BRICK_DIAG_PATTERN =
' x'+
' x '+
' x '+
' x '+
' xx '+
' x x '+
' x x '+
'x x';
SHINGLE_PATTERN =
' xx'+
'x x '+
' x x '+
' xx '+
' xx '+
' x '+
' x'+
' x';
WAVE_PATTERN =
' '+
' xx '+
' x x x'+
'xx '+
' '+
' xx '+
' x x x'+
'xx ';
ZIGZAG_PATTERN =
'x x'+
' x x '+
' x x '+
' xx '+
'x x'+
' x x '+
' x x '+
' xx ';
procedure CreateFillPatterns(WithDefaultPatterns: Boolean);
begin
if FillPatternList = nil then
FillPatternList := TsFillPatternList.Create;
if WithDefaultPatterns then
begin
fpsGray75 := RegisterFillPattern(FPS_GRAY75, StringToDotPattern(GRAY75_PATTERN));
fpsGray50 := RegisterFillPattern(FPS_GRAY50, StringToDotPattern(GRAY50_PATTERN));
fpsGray25 := RegisterFillPattern(FPS_GRAY25, StringToDotPattern(GRAY25_PATTERN));
fpsGray12 := RegisterFillPattern(FPS_GRAY12, StringToDotPattern(GRAY12_PATTERN));
fpsGray06 := RegisterFillPattern(FPS_GRAY06, StringToDotPattern(GRAY06_PATTERN));
fpsHorThick := RegisterFillPattern(FPS_HOR_THICK,
StringToDotPattern(HOR_PATTERN_THICK),
WIDE_DISTANCE, 0.0, THICK_LINE, lfpmSingle);
fpsVertThick := RegisterFillPattern(FPS_VERT_THICK,
StringToDotPattern(VERT_PATTERN_THICK),
WIDE_DISTANCE, 90.0, THICK_LINE, lfpmSingle);
fpsDiagUpThick := RegisterFillPattern(FPS_DIAG_UP_THICK,
StringToDotPattern(DIAG_UP_PATTERN_THICK),
WIDE_DISTANCE, 45.0, THICK_LINE, lfpmSingle);
fpsDiagDownThick := RegisterFillPattern(FPS_DIAG_DOWN_THICK,
StringToDotPattern(DIAG_DOWN_PATTERN_THICK),
WIDE_DISTANCE, -45.0, THICK_LINE, lfpmSingle);
fpsHatchThick := RegisterFillPattern(FPS_HATCH_THICK,
StringToDotPattern(HATCH_PATTERN_THICK),
WIDE_DISTANCE, 45.0, THICK_LINE, lfpmDouble);
fpsCrossThick := RegisterFillPattern(FPS_CROSS_THICK,
StringToDotPattern(CROSS_PATTERN_THICK),
WIDE_DISTANCE, 0.0, THICK_LINE, lfpmDouble);
fpsHorThin := RegisterFillPattern(FPS_HOR_THIN,
StringToDotPattern(HOR_PATTERN_THIN),
WIDE_DISTANCE, 0.0, THIN_LINE, lfpmSingle);
fpsVertThin := RegisterFillPattern(FPS_VERT_THIN,
StringToDotPattern(VERT_PATTERN_THIN),
WIDE_DISTANCE, 90.0, THIN_LINE, lfpmSingle);
fpsDiagUpThin := RegisterFillPattern(FPS_DIAG_UP_THIN,
StringToDotPattern(DIAG_UP_PATTERN_THIN),
WIDE_DISTANCE, 45.0, THIN_LINE, lfpmSingle);
fpsDiagDownThin := RegisterFillPattern(FPS_DIAG_DOWN_THIN,
StringToDotPattern(DIAG_DOWN_PATTERN_THIN),
WIDE_DISTANCE, -45.0, THIN_LINE, lfpmSingle);
fpsHatchThin := RegisterFillPattern(FPS_HATCH_THIN,
StringToDotPattern(HATCH_PATTERN_THIN),
WIDE_DISTANCE, 45.0, THIN_LINE, lfpmDouble);
fpsCrossThin := RegisterFillPattern(FPS_CROSS_THIN,
StringToDotPattern(CROSS_PATTERN_THIN),
WIDE_DISTANCE, 0.0, THIN_LINE, lfpmDouble);
fpsHorNarrow := RegisterFillPattern(FPS_HOR_NARROW,
StringToDotPattern(HOR_PATTERN_NARROW),
NARROW_DISTANCE, 0.0, THIN_LINE, lfpmSingle);
fpsVertNarrow := RegisterFillPattern(FPS_VERT_NARROW,
StringToDotPattern(VERT_PATTERN_NARROW),
NARROW_DISTANCE, 90.0, THIN_LINE, lfpmSingle);
fpsDiagUpNarrow := RegisterFillPattern(FPS_DIAG_UP_NARROW,
StringToDotPattern(DIAG_UP_PATTERN_NARROW),
NARROW_DISTANCE, 45.0, THIN_LINE, lfpmSingle);
fpsDiagDownNarrow := RegisterFillPattern(FPS_DIAG_DOWN_NARROW,
StringToDotPattern(DIAG_DOWN_PATTERN_NARROW),
NARROW_DISTANCE, -45.0, THIN_LINE, lfpmSingle);
fpsHatchNarrow := RegisterFillPattern(FPS_HATCH_NARROW,
StringToDotPattern(HATCH_PATTERN_NARROW),
NARROW_DISTANCE, 45.0, THIN_LINE, lfpmDouble);
fpsCrossNarrow := RegisterFillPattern(FPS_CROSS_NARROW,
StringToDotPattern(CROSS_PATTERN_NARROW),
NARROW_DISTANCE, 0.0, THIN_LINE, lfpmDouble);
fpsCrossDot := RegisterFillPattern(FPS_CROSS_DOT, StringToDotPattern(CROSS_PATTERN_DOT));
fpsHatchDot := RegisterFillPattern(FPS_HATCH_DOT, StringToDotPattern(HATCH_PATTERN_DOT));
fpsBrickHor := RegisterFillPattern(FPS_BRICK_HOR, StringToDotPattern(BRICK_HOR_PATTERN));
fpsBrickHor := RegisterFillPattern(FPS_BRICK_DIAG, StringToDotPattern(BRICK_DIAG_PATTERN));
fpsCheckerBoardLarge := RegisterFillPattern(FPS_CHECKERBOARD_LARGE, StringToDotPattern(CHECKERBOARD_LARGE_PATTERN));
fpsCheckerBoardSmall := RegisterFillPattern(FPS_CHECKERBOARD_SMALL, StringToDotPattern(CHECKERBOARD_SMALL_PATTERN));
fpsDiamond := RegisterFillPattern(FPS_DIAMOND, StringToDotPattern(DIAMOND_PATTERN));
fpsShingle := RegisterFillPattern(FPS_SHINGLE, StringToDotPattern(SHINGLE_PATTERN));
fpsWave := RegisterFillPattern(FPS_WAVE, StringToDotPattern(WAVE_PATTERN));
fpsZigzag := RegisterFillPattern(FPS_ZIGZAG, StringToDotPattern(ZIGZAG_PATTERN));
end;
end;
procedure EnsureFillPatternList;
begin
CreateFillPatterns(false);
end;
function RegisterFillPattern(AName: String; ADotPattern: TsDotFillPattern;
ALineDistance, ALineAngle, ALineWidth: Single; AMultiplier: TsLineFillPatternMultiplier): Integer;
begin
EnsureFillPatternList;
Result := FillPatternList.AddFillPattern(AName, ADotPattern,
ALineDistance, ALineAngle, ALineWidth, AMultiplier);
end;
function RegisterFillPattern(AName: String; ADotPattern: TsDotFillPattern): Integer;
begin
EnsureFillPatternList;
Result := FillPatternList.AddDotFillPattern(AName, ADotPattern);
end;
function RegisterFillPattern(AName: String; ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): Integer;
begin
EnsureFillPatternList;
Result := FillPatternList.AddLineFillPattern(AName, ALineDistance, ALineAngle, ALineWidth, AMultiplier);
end;
function ClonePattern(AIndex: Integer; AName: String): Integer;
var
patt: TsFillPattern;
begin
if Assigned(FillPatternList) then
begin
patt := GetFillPattern(AIndex);
Result := RegisterFillPattern(AName,
patt.DotPattern,
patt.LinePattern.Distance, patt.LinePattern.Angle, patt.LinePattern.LineWidth, patt.LinePattern.Multiplier
);
end else
Result := -1;
end;
function FindLineFillPatternIndex(ALineDistance, ALineAngle, ALineWidth: Single;
AMultiplier: TsLineFillPatternMultiplier): Integer;
var
i: Integer;
patt: TsFillPattern;
begin
if Assigned(FillPatternList) then
for i := 0 to FillPatternList.Count-1 do
begin
patt := FillPatternList[i];
if patt.LinePattern = nil then
Continue;
if SameValue(ALineDistance, patt.LinePattern.Distance, 0.1) and
SameValue(ALineAngle, patt.LinePattern.Angle, 0.1) and
SameValue(ALineWidth, patt.LinePattern.LineWidth, 0.1) and
(AMultiplier = patt.LinePattern.Multiplier) then
begin
Result := i;
exit;
end;
end;
Result := -1;
end;
function GetFillPattern(AIndex: Integer): TsFillPattern;
begin
if Assigned(FillPatternList) then
Result := FillPatternList.Items[AIndex]
else
Result := nil;
end;
function IndexOfPattern(AName: String): Integer;
begin
if Assigned(FillPatternList) then
Result := FillPatternList.IndexOfName(AName)
else
Result := -1;
end;
function NumPatterns: Integer;
begin
if Assigned(FillPatternList) then
Result := FillPatternList.Count
else
Result := 0;
end;
finalization
FreeAndNil(FillPatternList);
end.