
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9728 8e941d3f-bd1b-0410-a28a-d453659cc2b4
968 lines
25 KiB
ObjectPascal
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.
|
|
|