sync: restore source from Google Drive
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s

This commit is contained in:
admin_d committed 2026-09-12 16:50:30 +05:00
1 parent e2d8247e91
commit 7063d1b1c9
993 files changed
+949976

No files matched your search

+527
View File
@@ -0,0 +1,527 @@
// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit BGRAArrow;
{$mode objfpc}{$H+}
interface
uses
SysUtils, BGRABitmapTypes, BGRAGraphics;
type
{ TBGRAArrow }
TBGRAArrow = class(TBGRACustomArrow)
private
FLineCap: TPenEndCap;
FWidth : single;
FStart : ArrayOfTPointF;
FStartComputed: boolean;
FStartStyle: TBGRAArrowStyle;
FStartSizeFactor: TPointF;
FStartTipStyle: TPenJoinStyle;
FStartOffsetX: single;
FStartRepeatCount: integer;
FStartRelativePenWidth: single;
FStartTriangleBackOffset: single;
FEnd : ArrayOfTPointF;
FEndComputed: boolean;
FEndStyle: TBGRAArrowStyle;
FEndSizeFactor: TPointF;
FEndTipStyle: TPenJoinStyle;
FEndOffsetX: single;
FEndRepeatCount: integer;
FEndRelativePenWidth: single;
FEndTriangleBackOffset: single;
function ComputeAnyAt(const AData: ArrayOfTPointF; const APosition, ADirection: TPointF): ArrayOfTPointF;
function ComputeData(AStyle: TBGRAArrowStyle; const ASizeFactor: TPointF;
ATipStyle: TPenJoinStyle; ALineCap: TPenEndCap; const AWidth: single; AOffsetX: single;
ARepeatCount: integer; ARelativePenWidth: single; ATriangleBackOffset: single): ArrayOfTPointF;
procedure SetWidth(AValue: single);
protected
function GetEndRepeatCount: integer; override;
function GetEndSizeFactor: TPointF; override;
function GetIsEndDefined: boolean; override;
function GetIsStartDefined: boolean; override;
function GetEndOffsetX: single; override;
function GetStartOffsetX: single; override;
function GetStartRepeatCount: integer; override;
function GetStartSizeFactor: TPointF; override;
procedure SetEndOffsetX(AValue: single); override;
procedure SetEndRepeatCount(AValue: integer); override;
procedure SetEndSizeFactor(AValue: TPointF); override;
procedure SetStartOffsetX(AValue: single); override;
procedure SetStartRepeatCount(AValue: integer); override;
procedure SetStartSizeFactor(AValue: TPointF); override;
function GetLineCap: TPenEndCap; override;
procedure SetLineCap(AValue: TPenEndCap); override;
procedure SetStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0);
procedure SetEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0);
public
constructor Create;
procedure StartAsNone; override;
procedure StartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override;
procedure StartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override;
procedure StartAsTail; override;
procedure EndAsNone; override;
procedure EndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override;
procedure EndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override;
procedure EndAsTail; override;
function ComputeStartAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; override;
function ComputeEndAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; override;
end;
implementation
uses BGRAClasses, BGRATransform, BGRAPen, BGRAPath;
{ TBGRAArrow }
function TBGRAArrow.ComputeAnyAt(const AData: ArrayOfTPointF; const APosition,
ADirection: TPointF): ArrayOfTPointF;
var m: TAffineMatrix;
i: integer;
begin
if (AData = nil) or isEmptyPointF(APosition) or isEmptyPointF(ADirection) or ((ADirection.x = 0) and (ADirection.y = 0)) then
begin
result := nil;
exit;
end;
m := AffineMatrixTranslation(APosition.x,APosition.y)*AffineMatrixLinear(ADirection, PointF(-ADirection.y, ADirection.x));
setlength(result, length(AData));
for i := 0 to high(result) do
if not isEmptyPointF(AData[i]) then
result[i] := m*AData[i]
else
result[i] := EmptyPointF;
end;
function TBGRAArrow.ComputeData(AStyle: TBGRAArrowStyle;
const ASizeFactor: TPointF; ATipStyle: TPenJoinStyle; ALineCap: TPenEndCap;
const AWidth: single; AOffsetX: single; ARepeatCount: integer;
ARelativePenWidth: single; ATriangleBackOffset: single): ArrayOfTPointF;
var sizeFactorX,sizeFactorY,ofsX: single;
prefix,suffix,middle: ArrayOfTPointF;
arc: TArcDef;
i,j,n : integer;
withCut: ArrayOfTPointF;
subResult: ArrayOfTPointF;
w: single;
backOfs: single;
tailSizeX, tailAdditionalWidth: single;
begin
sizeFactorX := abs(ASizeFactor.X)*AWidth;
sizeFactorY := abs(ASizeFactor.Y)*AWidth;
if (sizeFactorX = 0) or (sizeFactorY = 0) then
begin
result := nil;
exit;
end;
w := AWidth*ARelativePenWidth;
if AStyle in [asTail,asTailRepeat] then
begin
tailSizeX := sizeFactorX/SizeFactorY*AWidth*0.5;
if AStyle = asTailRepeat then
begin
tailAdditionalWidth:= AWidth-tailSizeX;
if tailAdditionalWidth < 0 then tailAdditionalWidth := 0;
end else
tailAdditionalWidth:=0;
end
else
begin
tailSizeX := 0;
tailAdditionalWidth:=0;
end;
case AStyle of
asTriangle,asHollowTriangle: begin
backOfs := ATriangleBackOffset*sizeFactorX;
if AStyle = asHollowTriangle then
begin
result := ComputeWidePolylinePoints(PointsF([PointF(0.5*w,-AWidth*0.5),
PointF(0.5*w-backOfs,-sizeFactorY+w*0.5),
PointF(sizeFactorX-w*0.5,0),
PointF(w*0.5-backOfs,sizeFactorY-w*0.5),
PointF(0.5*w,AWidth*0.5)]),
w,BGRABlack,ALineCap,ATipStyle,nil,[plCycle]);
end else
begin
prefix := PointsF([PointF(0,-AWidth*0.5),PointF(-backOfs,-sizeFactorY)]);
suffix := PointsF([PointF(-backOfs,sizeFactorY),PointF(0,AWidth*0.5)]);
if (ATipStyle in[pjsRound,pjsBevel]) then
begin
arc := Html5ArcTo(prefix[1],PointF(sizeFactorX,0),suffix[0],AWidth*0.5);
if ATipStyle = pjsRound then
middle := ComputeArc(arc)
else
middle := PointsF([ArcStartPoint(arc),ArcEndPoint(arc)]);
end
else middle := PointsF([PointF(sizeFactorX,0)]);
result := ConcatPointsF([prefix,middle,suffix]);
end;
end;
asNormal,asCut:
begin
if AStyle = asCut then ALineCap:= pecSquare;
result := ComputeWidePolylinePoints([PointF(-sizeFactorX,-sizeFactorY),
PointF(0,0),PointF(-sizeFactorX,+sizeFactorY)],w,BGRABlack,ALineCap,ATipStyle,nil,[]);
end;
asFlipped,asFlippedCut:
begin
if AStyle = asFlippedCut then ALineCap:= pecSquare;
result := ComputeWidePolylinePoints([PointF(+sizeFactorX,-sizeFactorY),
PointF(0,0),PointF(+sizeFactorX,+sizeFactorY)],w,BGRABlack,ALineCap,ATipStyle,nil,[]);
end;
asTail: result := PointsF([PointF(0,-0.5*AWidth),PointF(tailSizeX,-0.5*AWidth),PointF(0,0),PointF(tailSizeX,0.5*AWidth),PointF(0,0.5*AWidth)]);
asTailRepeat: result := PointsF([PointF(0,-0.5*AWidth),PointF(tailSizeX+tailAdditionalWidth,-0.5*AWidth),PointF(tailAdditionalWidth,0),PointF(tailSizeX+tailAdditionalWidth,0.5*AWidth),PointF(0,0.5*AWidth),PointF(-tailSizeX,0)]);
else
result := nil;
end;
if (AStyle in [asCut,asFlippedCut,asHollowTriangle]) then
begin
n := 0;
setlength(withCut,length(result)*2);
for i := 0 to high(result) do
if isEmptyPointF(result[i]) then
begin
if (n > 0) and not isEmptyPointF(withCut[n-1]) then
begin
withCut[n] := EmptyPointF;
inc(n);
end;
end else
if abs(result[i].y)<=sizeFactorY then
begin
withCut[n] := result[i];
inc(n);
end else
if result[i].y>sizeFactorY then
begin
j := (i+length(result)-1) mod length(result);
if result[j].y<=sizeFactorY then
begin
withCut[n].x := result[j].x + (result[i].x-result[j].x)/(result[i].y-result[j].y)*(sizeFactorY-result[j].y);
withCut[n].y := sizeFactorY;
inc(n);
end;
j := (i+1) mod length(result);
if result[j].y<=sizeFactorY then
begin
withCut[n].x := result[j].x + (result[i].x-result[j].x)/(result[i].y-result[j].y)*(sizeFactorY-result[j].y);
withCut[n].y := sizeFactorY;
inc(n);
end;
end else
if result[i].y<-sizeFactorY then
begin
j := (i+length(result)-1) mod length(result);
if result[j].y>=-sizeFactorY then
begin
withCut[n].x := result[j].x + (result[i].x-result[j].x)/(result[i].y-result[j].y)*(-sizeFactorY-result[j].y);
withCut[n].y := -sizeFactorY;
inc(n);
end;
j := (i+1) mod length(result);
if result[j].y>=-sizeFactorY then
begin
withCut[n].x := result[j].x + (result[i].x-result[j].x)/(result[i].y-result[j].y)*(-sizeFactorY-result[j].y);
withCut[n].y := -sizeFactorY;
inc(n);
end;
end;
if (n > 0) and isEmptyPointF(withCut[n-1]) then dec(n);
setlength(withCut,n);
result := withCut;
end;
if AOffsetX <> 0 then
begin
ofsX := AOffsetX*AWidth;
for i := 0 to high(result) do
if not isEmptyPointF(result[i]) then
IncF(result[i].x, ofsX);
end;
if ARepeatCount > 1 then
begin
if ARepeatCount > 10 then ARepeatCount:= 10;
if AStyle in[asTriangle,asHollowTriangle] then IncF(AOffsetX, sizeFactorX/AWidth)
else if AStyle in[asTail,asTailRepeat] then IncF(AOffsetX, (tailSizeX+tailAdditionalWidth)/AWidth+1)
else IncF(AOffsetX, 2*ARelativePenWidth);
if AStyle = asTail then AStyle := asTailRepeat;
subResult := ComputeData(AStyle,ASizeFactor,ATipStyle,ALineCap,AWidth,AOffsetX,ARepeatCount-1,ARelativePenWidth,ATriangleBackOffset);
result := ConcatPointsF([result,PointsF([EmptyPointF]),subResult]);
end;
end;
function TBGRAArrow.GetIsEndDefined: boolean;
begin
result := FEndStyle <> asNone;
end;
function TBGRAArrow.GetIsStartDefined: boolean;
begin
result := FStartStyle <> asNone;
end;
function TBGRAArrow.GetEndOffsetX: single;
begin
result := FEndOffsetX;
end;
function TBGRAArrow.GetStartOffsetX: single;
begin
result := FStartOffsetX;
end;
function TBGRAArrow.GetStartRepeatCount: integer;
begin
result := FStartRepeatCount;
end;
function TBGRAArrow.GetStartSizeFactor: TPointF;
begin
result := FStartSizeFactor;
end;
procedure TBGRAArrow.SetEndOffsetX(AValue: single);
begin
if FEndOffsetX=AValue then Exit;
FEndOffsetX:=AValue;
FEndComputed:= false;
FEnd := nil;
end;
function TBGRAArrow.GetLineCap: TPenEndCap;
begin
result := FLineCap;
end;
procedure TBGRAArrow.SetEndRepeatCount(AValue: integer);
begin
if FEndRepeatCount=AValue then Exit;
FEndRepeatCount:=AValue;
FEndComputed:= false;
FEnd := nil;
end;
procedure TBGRAArrow.SetEndSizeFactor(AValue: TPointF);
begin
if FEndSizeFactor=AValue then Exit;
FEndSizeFactor:=AValue;
FEndComputed:= false;
FEnd := nil;
end;
procedure TBGRAArrow.SetLineCap(AValue: TPenEndCap);
begin
if FLineCap=AValue then Exit;
FLineCap:=AValue;
FStartComputed:= false;
FEndComputed:= false;
FStart:= nil;
FEnd := nil;
end;
procedure TBGRAArrow.SetStartOffsetX(AValue: single);
begin
if FStartOffsetX=AValue then Exit;
FStartOffsetX:=AValue;
FStartComputed:= false;
FStart := nil;
end;
procedure TBGRAArrow.SetStartRepeatCount(AValue: integer);
begin
if FStartRepeatCount=AValue then Exit;
FStartRepeatCount:=AValue;
FStartComputed:= false;
FStart := nil;
end;
procedure TBGRAArrow.SetStartSizeFactor(AValue: TPointF);
begin
if FStartSizeFactor=AValue then Exit;
FStartSizeFactor:=AValue;
FStartComputed:= false;
FStart := nil;
end;
procedure TBGRAArrow.SetWidth(AValue: single);
begin
if FWidth=AValue then Exit;
FWidth:=AValue;
FStartComputed := false;
FEndComputed:= false;
end;
function TBGRAArrow.GetEndRepeatCount: integer;
begin
Result:= FEndRepeatCount;
end;
function TBGRAArrow.GetEndSizeFactor: TPointF;
begin
Result:= FEndSizeFactor;
end;
constructor TBGRAArrow.Create;
begin
FWidth := 1;
FStartSizeFactor := PointF(2,2);
FEndSizeFactor := PointF(2,2);
end;
procedure TBGRAArrow.StartAsNone;
begin
SetStart(asNone);
end;
procedure TBGRAArrow.StartAsClassic(AFlipped: boolean; ACut: boolean;
ARelativePenWidth: single);
var join: TPenJoinStyle;
begin
if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
if ACut then
begin
if AFlipped then
SetStart(asFlippedCut,join,ARelativePenWidth)
else
SetStart(asCut,join,ARelativePenWidth)
end
else
begin
if AFlipped then
SetStart(asFlipped,join,ARelativePenWidth)
else
SetStart(asNormal,join,ARelativePenWidth)
end;
end;
procedure TBGRAArrow.StartAsTriangle(ABackOffset: single; ARounded: boolean;
AHollow: boolean; AHollowPenWidth: single);
var join: TPenJoinStyle;
begin
if ARounded then join := pjsRound else join := pjsMiter;
if AHollow then
SetStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
else
SetStart(asTriangle, join,1,ABackOffset);
end;
procedure TBGRAArrow.StartAsTail;
begin
SetStart(asTail);
end;
procedure TBGRAArrow.EndAsNone;
begin
SetEnd(asNone);
end;
procedure TBGRAArrow.EndAsClassic(AFlipped: boolean; ACut: boolean;
ARelativePenWidth: single);
var join: TPenJoinStyle;
begin
if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
if ACut then
begin
if AFlipped then
SetEnd(asFlippedCut,join,ARelativePenWidth)
else
SetEnd(asCut,join,ARelativePenWidth)
end
else
begin
if AFlipped then
SetEnd(asFlipped,join,ARelativePenWidth)
else
SetEnd(asNormal,join,ARelativePenWidth)
end;
end;
procedure TBGRAArrow.EndAsTriangle(ABackOffset: single; ARounded: boolean;
AHollow: boolean; AHollowPenWidth: single);
var join: TPenJoinStyle;
begin
if ARounded then join := pjsRound else join := pjsMiter;
if AHollow then
SetEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
else
SetEnd(asTriangle, join,1, ABackOffset);
end;
procedure TBGRAArrow.EndAsTail;
begin
SetEnd(asTail);
end;
procedure TBGRAArrow.SetStart(AStyle: TBGRAArrowStyle;
ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single);
begin
FStartStyle := AStyle;
FStartTipStyle := ATipStyle;
FStartComputed := false;
FStartRelativePenWidth:= ARelativePenWidth;
FStartTriangleBackOffset := ATriangleBackOffset;
FStart := nil;
end;
procedure TBGRAArrow.SetEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle;
ARelativePenWidth: single; ATriangleBackOffset: single);
begin
FEndStyle := AStyle;
FEndTipStyle := ATipStyle;
FEndComputed := false;
FEndRelativePenWidth:= ARelativePenWidth;
FEndTriangleBackOffset := ATriangleBackOffset;
FEnd := nil;
end;
function TBGRAArrow.ComputeStartAt(const APosition: TPointF;
const ADirection: TPointF; const AWidth: single; const ACurrentPos: single
): ArrayOfTPointF;
begin
if not IsStartDefined then
begin
result := nil;
exit;
end;
if AWidth <> FWidth then
begin
FWidth := AWidth;
FStartComputed:= false;
end;
if not FStartComputed then
begin
FStart := ComputeData(FStartStyle,FStartSizeFactor,FStartTipStyle,FLineCap,FWidth,
FStartOffsetX-ACurrentPos,FStartRepeatCount,FStartRelativePenWidth,FStartTriangleBackOffset);
FStartComputed:= true;
end;
result := ComputeAnyAt(FStart,APosition,ADirection);
end;
function TBGRAArrow.ComputeEndAt(const APosition: TPointF;
const ADirection: TPointF; const AWidth: single; const ACurrentPos: single
): ArrayOfTPointF;
begin
if not IsEndDefined then
begin
result := nil;
exit;
end;
if AWidth <> FWidth then
begin
FWidth := AWidth;
FEndComputed:= false;
end;
if not FEndComputed then
begin
FEnd := ComputeData(FEndStyle,FEndSizeFactor,FEndTipStyle,FLineCap,FWidth,
FEndOffsetX-ACurrentPos,FEndRepeatCount,FEndRelativePenWidth,FEndTriangleBackOffset);
FEndComputed:= true;
end;
result := ComputeAnyAt(FEnd,APosition,ADirection);
end;
end.