lcvectorpolyshapes.pas 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit LCVectorPolyShapes;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, Types, LCVectorOriginal, BGRABitmapTypes, BGRALayerOriginal,
  7. BGRABitmap, BGRATransform, BGRAGradients, BGRAGraphics,
  8. BGRASVGShapes, BGRASVGType, BGRAUnits, BGRAPath;
  9. type
  10. TArrowKind = (akNone, akTail, akTip, akNormal, akCut, akFlipped, akFlippedCut,
  11. akTriangle, akTriangleBack1, akTriangleBack2,
  12. akHollowTriangle, akHollowTriangleBack1, akHollowTriangleBack2);
  13. const
  14. errShapeNotHandled = 'Shape not handled';
  15. ArrowKindToStr: array[TArrowKind] of string =
  16. ('none', 'tail', 'tip', 'normal', 'cut', 'flipped', 'flipped-cut',
  17. 'triangle', 'triangle-back1', 'triangle-back2',
  18. 'hollow-triangle', 'hollow-triangle-back1', 'hollow-triangle-back2');
  19. LineCapToStr: array[TPenEndCap] of string =
  20. ('round','square','flat');
  21. function StrToArrowKind(AStr: string): TArrowKind;
  22. function StrToLineCap(AStr: string): TPenEndCap;
  23. type
  24. TCustomPolypointShape = class;
  25. TCustomPolypointPoint = record
  26. coord: TPointF;
  27. editorIndex: integer;
  28. data: cardinal;
  29. end;
  30. { TCustomPolypointShapeDiff }
  31. TCustomPolypointShapeDiff = class(TVectorShapeDiff)
  32. protected
  33. FStartPoints: array of TCustomPolypointPoint;
  34. FStartClosed: boolean;
  35. FStartArrowStartKind,FStartArrowEndKind: TArrowKind;
  36. FStartArrowSize: TPointF;
  37. FStartLineCap: TPenEndCap;
  38. FEndPoints: array of TCustomPolypointPoint;
  39. FEndClosed: boolean;
  40. FEndArrowStartKind,FEndArrowEndKind: TArrowKind;
  41. FEndArrowSize: TPointF;
  42. FEndLineCap: TPenEndCap;
  43. public
  44. constructor Create(AStartShape: TVectorShape); override;
  45. procedure ComputeDiff(AEndShape: TVectorShape); override;
  46. procedure Apply(AStartShape: TVectorShape); override;
  47. procedure Unapply(AEndShape: TVectorShape); override;
  48. procedure Append(ADiff: TVectorShapeDiff); override;
  49. function IsIdentity: boolean; override;
  50. end;
  51. { TCustomPolypointShape }
  52. TCustomPolypointShape = class(TVectorShape)
  53. private
  54. FClosed: boolean;
  55. function GetHoverPoint: integer;
  56. function GetLineCap: TPenEndCap;
  57. function GetPoint(AIndex: integer): TPointF;
  58. function GetPointCount: integer;
  59. function GetValidatedPointCount: integer;
  60. procedure SetArrowEndKind(AValue: TArrowKind);
  61. procedure SetArrowSize(AValue: TPointF);
  62. procedure SetArrowStartKind(AValue: TArrowKind);
  63. procedure SetCenterPoint(AValue: TPointF);
  64. procedure SetHoverCenter(AValue: boolean);
  65. procedure SetHoverPoint(AValue: integer);
  66. procedure SetLineCap(AValue: TPenEndCap);
  67. procedure SetPoint(AIndex: integer; AValue: TPointF);
  68. protected
  69. FPoints: array of TCustomPolypointPoint;
  70. FCenterPoint: TPointF;
  71. FCenterPointEditorIndex: integer;
  72. FCurPoint: integer;
  73. FAddingPoint, FAltPressed: boolean;
  74. FMousePos: TPointF;
  75. FHoverPoint: integer;
  76. FHoverCenter: boolean;
  77. FArrowStartKind,FArrowEndKind: TArrowKind;
  78. FArrowSize: TPointF;
  79. FViewMatrix, FViewMatrixInverse, FGridMatrix: TAffineMatrix;
  80. procedure OnMovePoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
  81. procedure OnMoveCenterPoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
  82. procedure OnStartMove({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState);
  83. function GetCurve(AMatrix: TAffineMatrix): ArrayOfTPointF; virtual;
  84. function GetPath(AMatrix: TAffineMatrix): TBGRAPath; virtual; overload;
  85. function GetPath(const APoints: array of TPointF): TBGRAPath; overload;
  86. procedure SetUsermode(AValue: TVectorShapeUsermode); override;
  87. function GetClosed: boolean; virtual;
  88. procedure SetClosed(AValue: boolean); virtual;
  89. function PointsEqual(const APoint1, APoint2: TPointF): boolean;
  90. procedure OnHoverPoint({%H-}ASender: TObject; APointIndex: integer); virtual;
  91. procedure OnClickPoint({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState); virtual;
  92. procedure DoClickPoint({%H-}APointIndex: integer; {%H-}AShift: TShiftState); virtual;
  93. function CanMovePoints: boolean; virtual;
  94. procedure InsertPointAuto(AShift: TShiftState);
  95. function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean;
  96. AStrokeMatrix: TAffineMatrix): ArrayOfTPointF; override;
  97. function GetLoopStartIndex: integer;
  98. function GetLoopPointCount: integer;
  99. function GetIsFollowingMouse: boolean; override;
  100. public
  101. constructor Create(AContainer: TVectorOriginal); override;
  102. procedure Clear;
  103. function AddPoint(const APoint: TPointF): integer; virtual;
  104. function RemovePoint(AIndex: integer): boolean;
  105. procedure RemovePointRange(AFromIndex, AToIndexPlus1: integer);
  106. procedure InsertPoint(AIndex: integer; APoint: TPointF);
  107. function GetPointBounds(AMatrix: TAffineMatrix): TRectF;
  108. procedure MouseMove({%H-}Shift: TShiftState; X, Y: single; var {%H-}ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
  109. procedure MouseDown(RightButton: boolean; {%H-}ClickCount: integer; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
  110. procedure KeyDown({%H-}Shift: TShiftState; Key: TSpecialKey; var AHandled: boolean); override;
  111. procedure KeyUp(Shift: TShiftState; Key: TSpecialKey; var AHandled: boolean); override;
  112. procedure QuickDefine(constref APoint1,APoint2: TPointF); override;
  113. procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
  114. procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
  115. procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
  116. procedure TransformFrame(const AMatrix: TAffineMatrix); override;
  117. class function Usermodes: TVectorShapeUsermodes; override;
  118. class function DefaultArrowSize: TPointF;
  119. property Points[AIndex:integer]: TPointF read GetPoint write SetPoint;
  120. property PointCount: integer read GetPointCount;
  121. property ValidatedPointCount: integer read GetValidatedPointCount;
  122. property Closed: boolean read GetClosed write SetClosed;
  123. property HoverPoint: integer read GetHoverPoint write SetHoverPoint;
  124. property HoverCenter: boolean read FHoverCenter write SetHoverCenter;
  125. property ArrowStartKind: TArrowKind read FArrowStartKind write SetArrowStartKind;
  126. property ArrowEndKind: TArrowKind read FArrowEndKind write SetArrowEndKind;
  127. property ArrowSize: TPointF read FArrowSize write SetArrowSize;
  128. property LineCap: TPenEndCap read GetLineCap write SetLineCap;
  129. property Center: TPointF read FCenterPoint write SetCenterPoint;
  130. end;
  131. { TPolylineShape }
  132. TPolylineShape = class(TCustomPolypointShape)
  133. public
  134. class function Fields: TVectorShapeFields; override;
  135. procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
  136. function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
  137. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
  138. function PointInShape(APoint: TPointF): boolean; overload; override;
  139. function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
  140. function PointInBack(APoint: TPointF): boolean; overload; override;
  141. function PointInPen(APoint: TPointF): boolean; overload; override;
  142. function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
  143. class function StorageClassName: RawByteString; override;
  144. end;
  145. TCurveShape = class;
  146. { TCurveShapeDiff }
  147. TCurveShapeDiff = class(TVectorShapeDiff)
  148. protected
  149. FStartCosineAngle: single;
  150. FStartSplineStyle: TSplineStyle;
  151. FEndCosineAngle: single;
  152. FEndSplineStyle: TSplineStyle;
  153. public
  154. constructor Create(AStartShape: TVectorShape); override;
  155. procedure ComputeDiff(AEndShape: TVectorShape); override;
  156. procedure Apply(AStartShape: TVectorShape); override;
  157. procedure Unapply(AEndShape: TVectorShape); override;
  158. procedure Append(ADiff: TVectorShapeDiff); override;
  159. function IsIdentity: boolean; override;
  160. end;
  161. { TCurveShape }
  162. TCurveShape = class(TPolylineShape)
  163. private
  164. FCosineAngle: single;
  165. FSplineStyle: TSplineStyle;
  166. function GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
  167. procedure SetCosineAngle(AValue: single);
  168. procedure SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
  169. procedure SetSplineStyle(AValue: TSplineStyle);
  170. protected
  171. function GetCurve(AMatrix: TAffineMatrix): ArrayOfTPointF; override;
  172. function GetPath(AMatrix: TAffineMatrix): TBGRAPath; override;
  173. function CanMovePoints: boolean; override;
  174. procedure DoClickPoint(APointIndex: integer; {%H-}AShift: TShiftState); override;
  175. public
  176. class function Usermodes: TVectorShapeUsermodes; override;
  177. constructor Create(AContainer: TVectorOriginal); override;
  178. constructor CreateFrom(AContainer: TVectorOriginal; AShape: TVectorShape);
  179. class function CanCreateFrom(AShape: TVectorShape): boolean;
  180. function AddPoint(const APoint: TPointF): integer; overload; override;
  181. function AddPoint(const APoint: TPointF; AMode: TEasyBezierCurveMode): integer; overload;
  182. procedure KeyPress(UTF8Key: string; var AHandled: boolean); override;
  183. procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
  184. procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
  185. class function StorageClassName: RawByteString; override;
  186. property SplineStyle: TSplineStyle read FSplineStyle write SetSplineStyle;
  187. property CurveMode[AIndex: integer]: TEasyBezierCurveMode read GetCurveMode write SetCurveMode;
  188. property CosineAngle: single read FCosineAngle write SetCosineAngle;
  189. end;
  190. procedure ApplyArrowStyle(AArrow: TBGRACustomArrow; AStart: boolean; AKind: TArrowKind; ASize: TPointF);
  191. implementation
  192. uses BGRAPen, BGRAFillInfo, math, LCVectorialFill,
  193. BGRAArrow, LCVectorRectShapes, LCResourceString;
  194. function StrToArrowKind(AStr: string): TArrowKind;
  195. var
  196. ak: TArrowKind;
  197. begin
  198. for ak := low(TArrowKind) to high(TArrowKind) do
  199. if CompareText(AStr, ArrowKindToStr[ak])=0 then exit(ak);
  200. result := akNone;
  201. end;
  202. function StrToLineCap(AStr: string): TPenEndCap;
  203. var
  204. ec: TPenEndCap;
  205. begin
  206. for ec := low(TPenEndCap) to high(TPenEndCap) do
  207. if CompareText(AStr, LineCapToStr[ec])=0 then exit(ec);
  208. result := pecRound;
  209. end;
  210. procedure ApplyArrowStyle(AArrow: TBGRACustomArrow; AStart: boolean; AKind: TArrowKind; ASize: TPointF);
  211. var backOfs: single;
  212. begin
  213. backOfs := 0;
  214. if (ASize.x = 0) or (ASize.y = 0) then AKind := akNone;
  215. if AKind in[akTriangleBack1,akHollowTriangleBack1] then backOfs := 0.25;
  216. if AKind in[akTriangleBack2,akHollowTriangleBack2] then backOfs := 0.50;
  217. case AKind of
  218. akTail: if AStart then AArrow.StartAsTail else AArrow.EndAsTail;
  219. akTip: if AStart then AArrow.StartAsTriangle else AArrow.EndAsTriangle;
  220. akNormal,akCut,akFlipped,akFlippedCut:
  221. if AStart then AArrow.StartAsClassic(AKind in[akFlipped,akFlippedCut], AKind in[akCut,akFlippedCut])
  222. else AArrow.EndAsClassic(AKind in[akFlipped,akFlippedCut], AKind in[akCut,akFlippedCut]);
  223. akTriangle,akTriangleBack1,akTriangleBack2:
  224. if AStart then AArrow.StartAsTriangle(backOfs) else AArrow.EndAsTriangle(backOfs);
  225. akHollowTriangle,akHollowTriangleBack1,akHollowTriangleBack2:
  226. if AStart then AArrow.StartAsTriangle(backOfs,False,True) else AArrow.EndAsTriangle(backOfs,False,True);
  227. else if AStart then AArrow.StartAsNone else AArrow.EndAsNone;
  228. end;
  229. if (AKind = akTip) and not ((ASize.x = 0) or (ASize.y = 0)) then
  230. ASize := ASize*(0.5/ASize.y);
  231. if AStart then AArrow.StartSize := ASize else AArrow.EndSize := ASize;
  232. end;
  233. procedure IncludePointF(var ARectF: TRectF; APointF: TPointF);
  234. begin
  235. if APointF.x < ARectF.Left then ARectF.Left := APointF.x;
  236. if APointF.x > ARectF.Right then ARectF.Right := APointF.x;
  237. if APointF.y < ARectF.Top then ARectF.Top := APointF.y;
  238. if APointF.y > ARectF.Bottom then ARectF.Bottom := APointF.y;
  239. end;
  240. function GetPointsBoundsF(const APoints: array of TPointF): TRectF;
  241. var
  242. i: Integer;
  243. firstPoint: Boolean;
  244. begin
  245. result:= EmptyRectF;
  246. firstPoint := true;
  247. for i:= 0 to high(APoints) do
  248. if not isEmptyPointF(APoints[i]) then
  249. begin
  250. if firstPoint then
  251. begin
  252. result.TopLeft := APoints[i];
  253. result.BottomRight := APoints[i];
  254. firstPoint := false;
  255. end else
  256. IncludePointF(result, APoints[i]);
  257. end;
  258. end;
  259. { TCurveShapeDiff }
  260. constructor TCurveShapeDiff.Create(AStartShape: TVectorShape);
  261. begin
  262. with (AStartShape as TCurveShape) do
  263. begin
  264. FStartCosineAngle:= FCosineAngle;
  265. FStartSplineStyle:= FSplineStyle;
  266. end;
  267. end;
  268. procedure TCurveShapeDiff.ComputeDiff(AEndShape: TVectorShape);
  269. begin
  270. with (AEndShape as TCurveShape) do
  271. begin
  272. FEndCosineAngle:= FCosineAngle;
  273. FEndSplineStyle:= FSplineStyle;
  274. end;
  275. end;
  276. procedure TCurveShapeDiff.Apply(AStartShape: TVectorShape);
  277. begin
  278. with (AStartShape as TCurveShape) do
  279. begin
  280. BeginUpdate;
  281. FCosineAngle := FEndCosineAngle;
  282. FSplineStyle := FEndSplineStyle;
  283. EndUpdate;
  284. end;
  285. end;
  286. procedure TCurveShapeDiff.Unapply(AEndShape: TVectorShape);
  287. begin
  288. with (AEndShape as TCurveShape) do
  289. begin
  290. BeginUpdate;
  291. FCosineAngle := FStartCosineAngle;
  292. FSplineStyle := FStartSplineStyle;
  293. EndUpdate;
  294. end;
  295. end;
  296. procedure TCurveShapeDiff.Append(ADiff: TVectorShapeDiff);
  297. var
  298. next: TCurveShapeDiff;
  299. begin
  300. next := ADiff as TCurveShapeDiff;
  301. FEndCosineAngle:= next.FEndCosineAngle;
  302. FEndSplineStyle:= next.FEndSplineStyle;
  303. end;
  304. function TCurveShapeDiff.IsIdentity: boolean;
  305. begin
  306. result := (FStartCosineAngle = FEndCosineAngle) and
  307. (FStartSplineStyle = FEndSplineStyle);
  308. end;
  309. { TCustomPolypointShapeDiff }
  310. constructor TCustomPolypointShapeDiff.Create(AStartShape: TVectorShape);
  311. var
  312. i: Integer;
  313. begin
  314. with (AStartShape as TCustomPolypointShape) do
  315. begin
  316. setlength(FStartPoints, length(FPoints));
  317. for i := 0 to high(FPoints) do FStartPoints[i] := FPoints[i];
  318. FStartClosed:= FClosed;
  319. FStartArrowStartKind := FArrowStartKind;
  320. FStartArrowEndKind:= FArrowEndKind;
  321. FStartArrowSize:= FArrowSize;
  322. FStartLineCap:= Stroker.LineCap;
  323. end;
  324. end;
  325. procedure TCustomPolypointShapeDiff.ComputeDiff(AEndShape: TVectorShape);
  326. var
  327. i: Integer;
  328. begin
  329. with (AEndShape as TCustomPolypointShape) do
  330. begin
  331. setlength(FEndPoints, length(FPoints));
  332. for i := 0 to high(FPoints) do FEndPoints[i] := FPoints[i];
  333. FEndClosed:= FClosed;
  334. FEndArrowStartKind := FArrowStartKind;
  335. FEndArrowEndKind:= FArrowEndKind;
  336. FEndArrowSize:= FArrowSize;
  337. FEndLineCap:= Stroker.LineCap;
  338. end;
  339. end;
  340. procedure TCustomPolypointShapeDiff.Apply(AStartShape: TVectorShape);
  341. var
  342. i: Integer;
  343. begin
  344. with (AStartShape as TCustomPolypointShape) do
  345. begin
  346. BeginUpdate;
  347. setlength(FPoints, length(FEndPoints));
  348. for i := 0 to high(FPoints) do FPoints[i] := FEndPoints[i];
  349. FClosed := FEndClosed;
  350. FArrowStartKind := FEndArrowStartKind;
  351. FArrowEndKind := FEndArrowEndKind;
  352. FArrowSize := FEndArrowSize;
  353. Stroker.LineCap:= FEndLineCap;
  354. EndUpdate;
  355. end;
  356. end;
  357. procedure TCustomPolypointShapeDiff.Unapply(AEndShape: TVectorShape);
  358. var
  359. i: Integer;
  360. begin
  361. with (AEndShape as TCustomPolypointShape) do
  362. begin
  363. BeginUpdate;
  364. setlength(FPoints, length(FStartPoints));
  365. for i := 0 to high(FPoints) do FPoints[i] := FStartPoints[i];
  366. FClosed := FStartClosed;
  367. FArrowStartKind := FStartArrowStartKind;
  368. FArrowEndKind := FStartArrowEndKind;
  369. FArrowSize := FStartArrowSize;
  370. Stroker.LineCap:= FStartLineCap;
  371. EndUpdate;
  372. end;
  373. end;
  374. procedure TCustomPolypointShapeDiff.Append(ADiff: TVectorShapeDiff);
  375. var
  376. next: TCustomPolypointShapeDiff;
  377. i: Integer;
  378. begin
  379. next := ADiff as TCustomPolypointShapeDiff;
  380. setlength(FEndPoints, length(next.FEndPoints));
  381. for i := 0 to high(FEndPoints) do FEndPoints[i] := next.FEndPoints[i];
  382. FEndClosed := next.FEndClosed;
  383. FEndArrowStartKind := next.FEndArrowStartKind;
  384. FEndArrowEndKind := next.FEndArrowEndKind;
  385. FEndArrowSize := next.FEndArrowSize;
  386. FEndLineCap:= next.FEndLineCap;
  387. end;
  388. function TCustomPolypointShapeDiff.IsIdentity: boolean;
  389. var
  390. i: Integer;
  391. begin
  392. result := (length(FStartPoints) = length(FEndPoints)) and
  393. (FStartClosed = FEndClosed) and
  394. (FStartArrowStartKind = FEndArrowStartKind) and
  395. (FStartArrowEndKind = FEndArrowEndKind) and
  396. (FStartArrowSize = FEndArrowSize) and
  397. (FStartLineCap = FEndLineCap);
  398. if result then
  399. begin
  400. for i := 0 to high(FStartPoints) do
  401. if (FStartPoints[i].coord<>FEndPoints[i].coord) or
  402. (FStartPoints[i].data<>FEndPoints[i].data) then
  403. begin
  404. result := false;
  405. break;
  406. end;
  407. end;
  408. end;
  409. { TCustomPolypointShape }
  410. function TCustomPolypointShape.GetClosed: boolean;
  411. begin
  412. result := FClosed;
  413. end;
  414. function TCustomPolypointShape.GetPoint(AIndex: integer): TPointF;
  415. begin
  416. if (AIndex < 0) or (AIndex >= length(FPoints)) then
  417. raise ERangeError.Create(rsIndexOutOfBounds);
  418. result := FPoints[AIndex].coord;
  419. end;
  420. function TCustomPolypointShape.GetLineCap: TPenEndCap;
  421. begin
  422. result := Stroker.LineCap;
  423. end;
  424. function TCustomPolypointShape.GetHoverPoint: integer;
  425. begin
  426. if (FHoverPoint >= 0) and (FHoverPoint < PointCount) and
  427. not Points[FHoverPoint].IsEmpty then
  428. result := FHoverPoint else result := -1;
  429. end;
  430. function TCustomPolypointShape.GetPointCount: integer;
  431. begin
  432. result:= length(FPoints);
  433. end;
  434. function TCustomPolypointShape.GetValidatedPointCount: integer;
  435. begin
  436. if (PointCount > 1) and FAddingPoint then
  437. result := PointCount - 1
  438. else
  439. result := PointCount;
  440. end;
  441. procedure TCustomPolypointShape.SetArrowEndKind(AValue: TArrowKind);
  442. begin
  443. if FArrowEndKind=AValue then Exit;
  444. BeginUpdate(TCustomPolypointShapeDiff);
  445. FArrowEndKind:=AValue;
  446. EndUpdate;
  447. end;
  448. procedure TCustomPolypointShape.SetArrowSize(AValue: TPointF);
  449. begin
  450. if FArrowSize=AValue then Exit;
  451. BeginUpdate(TCustomPolypointShapeDiff);
  452. FArrowSize:=AValue;
  453. EndUpdate;
  454. end;
  455. procedure TCustomPolypointShape.SetArrowStartKind(AValue: TArrowKind);
  456. begin
  457. if FArrowStartKind=AValue then Exit;
  458. BeginUpdate(TCustomPolypointShapeDiff);
  459. FArrowStartKind:=AValue;
  460. EndUpdate;
  461. end;
  462. procedure TCustomPolypointShape.SetCenterPoint(AValue: TPointF);
  463. var
  464. i: Integer;
  465. delta: TPointF;
  466. begin
  467. if FCenterPoint=AValue then Exit;
  468. BeginUpdate(TCustomPolypointShapeDiff);
  469. delta := AValue - FCenterPoint;
  470. for i := 0 to PointCount-1 do
  471. Points[i] := Points[i]+delta;
  472. if vsfBackFill in Fields then
  473. BackFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
  474. if vsfPenFill in Fields then
  475. PenFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
  476. FCenterPoint:=AValue;
  477. EndUpdate;
  478. end;
  479. procedure TCustomPolypointShape.SetHoverCenter(AValue: boolean);
  480. begin
  481. if FHoverCenter=AValue then Exit;
  482. BeginEditingUpdate;
  483. if AValue then FHoverPoint := -1;
  484. FHoverCenter:=AValue;
  485. EndEditingUpdate;
  486. end;
  487. procedure TCustomPolypointShape.SetHoverPoint(AValue: integer);
  488. begin
  489. if (AValue < 0) or (AValue >= PointCount) or
  490. Points[AValue].IsEmpty then AValue := -1;
  491. if AValue <> FHoverPoint then
  492. begin
  493. BeginEditingUpdate;
  494. FHoverPoint := AValue;
  495. if AValue <> -1 then FHoverCenter:= false;
  496. EndEditingUpdate;
  497. end;
  498. end;
  499. procedure TCustomPolypointShape.SetLineCap(AValue: TPenEndCap);
  500. begin
  501. if Stroker.LineCap=AValue then Exit;
  502. BeginUpdate(TCustomPolypointShapeDiff);
  503. Stroker.LineCap:=AValue;
  504. EndUpdate;
  505. end;
  506. procedure TCustomPolypointShape.SetClosed(AValue: boolean);
  507. begin
  508. if AValue = FClosed then exit;
  509. BeginUpdate(TCustomPolypointShapeDiff);
  510. FClosed := AValue;
  511. EndUpdate;
  512. end;
  513. procedure TCustomPolypointShape.SetPoint(AIndex: integer; AValue: TPointF);
  514. begin
  515. if (AIndex < 0) or (AIndex > length(FPoints)) then
  516. raise ERangeError.Create(rsIndexOutOfBounds);
  517. BeginUpdate(TCustomPolypointShapeDiff);
  518. if AIndex = length(FPoints) then
  519. begin
  520. setlength(FPoints, length(FPoints)+1);
  521. FPoints[AIndex].coord := AValue;
  522. FPoints[AIndex].editorIndex := -1;
  523. FPoints[AIndex].data := 0;
  524. end
  525. else
  526. FPoints[AIndex].coord := AValue;
  527. EndUpdate;
  528. end;
  529. procedure TCustomPolypointShape.OnMovePoint(ASender: TObject; APrevCoord,
  530. ANewCoord: TPointF; AShift: TShiftState);
  531. begin
  532. if FCurPoint = -1 then exit;
  533. Points[FCurPoint] := ANewCoord;
  534. end;
  535. procedure TCustomPolypointShape.OnMoveCenterPoint(ASender: TObject; APrevCoord,
  536. ANewCoord: TPointF; AShift: TShiftState);
  537. begin
  538. Center := ANewCoord;
  539. end;
  540. procedure TCustomPolypointShape.OnStartMove(ASender: TObject; APointIndex: integer;
  541. AShift: TShiftState);
  542. var
  543. i: Integer;
  544. begin
  545. FCurPoint:= -1;
  546. for i:= 0 to PointCount-1 do
  547. if FPoints[i].editorIndex = APointIndex then
  548. begin
  549. FCurPoint:= i;
  550. break;
  551. end;
  552. end;
  553. function TCustomPolypointShape.GetCurve(AMatrix: TAffineMatrix): ArrayOfTPointF;
  554. var
  555. i: Integer;
  556. m: TAffineMatrix;
  557. begin
  558. setlength(result, PointCount);
  559. m:= MatrixForPixelCentered(AMatrix);
  560. for i := 0 to PointCount-1 do
  561. result[i] := m*Points[i];
  562. end;
  563. function TCustomPolypointShape.GetPath(const APoints: array of TPointF): TBGRAPath;
  564. var p: TPointF;
  565. subPoly: boolean;
  566. begin
  567. result := TBGRAPath.Create;
  568. subPoly := true;
  569. for p in APoints do
  570. begin
  571. if isEmptyPointF(p) then
  572. begin
  573. if not result.IsEmpty and Closed then result.closePath;
  574. subPoly := true;
  575. end else
  576. begin
  577. if subPoly then
  578. begin
  579. result.moveTo(p);
  580. subPoly := false;
  581. end
  582. else
  583. result.lineTo(p);
  584. end;
  585. end;
  586. if not result.IsEmpty and Closed then result.closePath;
  587. end;
  588. function TCustomPolypointShape.GetPath(AMatrix: TAffineMatrix): TBGRAPath;
  589. var
  590. pts: array of TPointF;
  591. begin
  592. pts := GetCurve(AMatrix);
  593. result := GetPath(pts);
  594. end;
  595. class function TCustomPolypointShape.Usermodes: TVectorShapeUsermodes;
  596. begin
  597. Result:= inherited Usermodes + [vsuCreate];
  598. end;
  599. class function TCustomPolypointShape.DefaultArrowSize: TPointF;
  600. begin
  601. result := PointF(2,2);
  602. end;
  603. procedure TCustomPolypointShape.SetUsermode(AValue: TVectorShapeUsermode);
  604. var
  605. add: Boolean;
  606. begin
  607. add := AValue = vsuCreate;
  608. if add and (PointCount = 0) then exit;
  609. if FAddingPoint and not add then
  610. begin
  611. if (PointCount>1) and PointsEqual(Points[PointCount-1],Points[PointCount-2]) then
  612. RemovePoint(PointCount-1);
  613. FAddingPoint:= add;
  614. end else
  615. if not FAddingPoint and add then
  616. begin
  617. if not isEmptyPointF(FMousePos) then
  618. AddPoint(FMousePos)
  619. else
  620. AddPoint(Points[PointCount-1]);
  621. FAddingPoint:= add;
  622. end;
  623. inherited SetUsermode(AValue);
  624. end;
  625. function TCustomPolypointShape.PointsEqual(const APoint1, APoint2: TPointF
  626. ): boolean;
  627. begin
  628. if isEmptyPointF(APoint1) then
  629. exit(isEmptyPointF(APoint2))
  630. else
  631. if isEmptyPointF(APoint2) then exit(false)
  632. else
  633. exit((APoint1.x = APoint2.x) and (APoint1.y = APoint2.y));
  634. end;
  635. procedure TCustomPolypointShape.OnHoverPoint(ASender: TObject;
  636. APointIndex: integer);
  637. var
  638. i, newHoverPoint: Integer;
  639. begin
  640. if APointIndex = FCenterPointEditorIndex then
  641. begin
  642. HoverCenter := true;
  643. exit;
  644. end;
  645. newHoverPoint:= -1;
  646. if APointIndex <> -1 then
  647. begin
  648. for i:= 0 to PointCount-1 do
  649. if FPoints[i].editorIndex = APointIndex then
  650. begin
  651. newHoverPoint:= i;
  652. break;
  653. end;
  654. end;
  655. HoverPoint := newHoverPoint;
  656. HoverCenter:= false;
  657. end;
  658. procedure TCustomPolypointShape.OnClickPoint(ASender: TObject;
  659. APointIndex: integer; AShift: TShiftState);
  660. var
  661. i: Integer;
  662. begin
  663. if APointIndex <> -1 then
  664. begin
  665. for i:= 0 to PointCount-1 do
  666. if FPoints[i].editorIndex = APointIndex then
  667. begin
  668. DoClickPoint(i, AShift);
  669. break;
  670. end;
  671. end;
  672. end;
  673. procedure TCustomPolypointShape.DoClickPoint(APointIndex: integer;
  674. AShift: TShiftState);
  675. var
  676. nb: Integer;
  677. begin
  678. if FAddingPoint and ((APointIndex = GetLoopStartIndex) or
  679. ((APointIndex = PointCount-2) and (ssRight in AShift))) then
  680. begin
  681. nb := GetLoopPointCount;
  682. if nb > 2 then
  683. begin
  684. BeginUpdate;
  685. RemovePoint(PointCount-1);
  686. if APointIndex < PointCount-2 then Closed := true;
  687. EndUpdate;
  688. UserMode := vsuEdit;
  689. end else
  690. begin
  691. if GetLoopStartIndex = 0 then
  692. Remove
  693. else
  694. begin
  695. BeginUpdate;
  696. while nb > 0 do
  697. begin
  698. RemovePoint(PointCount-1);
  699. dec(nb);
  700. end;
  701. RemovePoint(PointCount-1); //remove separator
  702. end;
  703. end;
  704. end;
  705. end;
  706. function TCustomPolypointShape.CanMovePoints: boolean;
  707. begin
  708. result := true;
  709. end;
  710. procedure TCustomPolypointShape.InsertPointAuto(AShift: TShiftState);
  711. var
  712. i,j, loopStart: Integer;
  713. bestSegmentIndex,bestPointIndex: integer;
  714. bestSegmentDist,bestPointDist, segmentLen, segmentPos: single;
  715. u, n, bestProjection: TPointF;
  716. segmentDist: single;
  717. isLooping: Boolean;
  718. begin
  719. if isEmptyPointF(FMousePos) then exit;
  720. for i := 0 to PointCount-1 do
  721. if (Points[i] = FMousePos) and not (FAddingPoint and (i = PointCount-1)) then exit;
  722. bestSegmentIndex := -1;
  723. bestSegmentDist := MaxSingle;
  724. bestProjection := EmptyPointF;
  725. loopStart := 0;
  726. for i := 0 to PointCount-1 do
  727. if FAddingPoint and (i >= PointCount-2) then break else
  728. begin
  729. if IsEmptyPointF(Points[i]) then
  730. begin
  731. loopStart := i+1;
  732. continue;
  733. end;
  734. isLooping := (i = PointCount-1) or IsEmptyPointF(Points[i+1]);
  735. if isLooping and not Closed then break;
  736. if isLooping then
  737. j := loopStart
  738. else j := i+1;
  739. u := Points[j] - Points[i];
  740. segmentLen := VectLen(u);
  741. if segmentLen > 0 then
  742. begin
  743. u *= 1/segmentLen;
  744. segmentPos := (FMousePos-Points[i])*u;
  745. if (segmentPos > 0) and (segmentPos< segmentLen) then
  746. begin
  747. n := PointF(u.y,-u.x);
  748. segmentDist := abs((FMousePos-Points[i])*n);
  749. if segmentDist <= bestSegmentDist then
  750. begin
  751. bestSegmentDist := segmentDist;
  752. bestSegmentIndex := i;
  753. bestProjection := Points[i]+segmentPos*u;
  754. end;
  755. end;
  756. end;
  757. end;
  758. bestPointIndex := -1;
  759. bestPointDist := MaxSingle;
  760. if not FAddingPoint then
  761. for i := 0 to PointCount-1 do
  762. if ((i = 0) or isEmptyPointF(Points[i-1])) and
  763. ((i = PointCount-1) or isEmptyPointF(Points[i+1])) then
  764. begin
  765. segmentDist := VectLen(FMousePos-Points[i]);
  766. if segmentDist < bestPointDist then
  767. begin
  768. bestPointDist := segmentDist;
  769. bestPointIndex := i;
  770. end;
  771. end;
  772. if (bestPointIndex <> -1) and ((bestSegmentIndex = -1) or (bestPointDist < bestSegmentDist)) then
  773. begin
  774. InsertPoint(bestPointIndex+1, FMousePos);
  775. HoverPoint := bestPointIndex+1;
  776. end else
  777. if bestSegmentIndex <> -1 then
  778. begin
  779. if ssShift in AShift then
  780. InsertPoint(bestSegmentIndex+1, bestProjection)
  781. else
  782. InsertPoint(bestSegmentIndex+1, FMousePos);
  783. HoverPoint:= bestSegmentIndex+1;
  784. end;
  785. end;
  786. function TCustomPolypointShape.ComputeStroke(APoints: ArrayOfTPointF;
  787. AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF;
  788. begin
  789. if Stroker.Arrow = nil then
  790. begin
  791. Stroker.Arrow := TBGRAArrow.Create;
  792. Stroker.ArrowOwned:= true;
  793. end;
  794. Stroker.Arrow.LineCap:= LineCap;
  795. ApplyArrowStyle(Stroker.Arrow, true, ArrowStartKind, ArrowSize);
  796. ApplyArrowStyle(Stroker.Arrow, false, ArrowEndKind, ArrowSize);
  797. Result:=inherited ComputeStroke(APoints, AClosed, AStrokeMatrix);
  798. Stroker.Arrow.StartAsNone;
  799. Stroker.Arrow.EndAsNone;
  800. end;
  801. function TCustomPolypointShape.GetLoopStartIndex: integer;
  802. var
  803. i: Integer;
  804. begin
  805. for i := PointCount-1 downto 0 do
  806. if isEmptyPointF(Points[i]) then exit(i+1);
  807. exit(0);
  808. end;
  809. function TCustomPolypointShape.GetLoopPointCount: integer;
  810. begin
  811. result := PointCount-GetLoopStartIndex;
  812. end;
  813. function TCustomPolypointShape.GetIsFollowingMouse: boolean;
  814. begin
  815. Result:= Usermode = vsuCreate;
  816. end;
  817. constructor TCustomPolypointShape.Create(AContainer: TVectorOriginal);
  818. begin
  819. inherited Create(AContainer);
  820. FMousePos := EmptyPointF;
  821. FClosed:= false;
  822. FHoverPoint:= -1;
  823. FCenterPoint := EmptyPointF;
  824. end;
  825. procedure TCustomPolypointShape.Clear;
  826. begin
  827. RemovePointRange(0, PointCount);
  828. end;
  829. function TCustomPolypointShape.AddPoint(const APoint: TPointF): integer;
  830. begin
  831. result := PointCount;
  832. Points[result] := APoint;
  833. end;
  834. function TCustomPolypointShape.RemovePoint(AIndex: integer): boolean;
  835. begin
  836. if (AIndex < 0) or (AIndex >= PointCount) then exit(false);
  837. RemovePointRange(AIndex,AIndex+1);
  838. result := true;
  839. end;
  840. procedure TCustomPolypointShape.RemovePointRange(AFromIndex, AToIndexPlus1: integer);
  841. var
  842. i, delCount: Integer;
  843. begin
  844. if AFromIndex < 0 then AFromIndex:= 0;
  845. if AToIndexPlus1 > PointCount then AToIndexPlus1:= PointCount;
  846. if AFromIndex >= AToIndexPlus1 then exit;
  847. BeginUpdate(TCustomPolypointShapeDiff);
  848. delCount := AToIndexPlus1-AFromIndex;
  849. for i := AFromIndex to PointCount-DelCount-1 do
  850. FPoints[i] := FPoints[i+delCount];
  851. setlength(FPoints, PointCount-delCount);
  852. if (HoverPoint >= AFromIndex) and (HoverPoint < AToIndexPlus1) then HoverPoint := -1
  853. else if (HoverPoint <> -1) and (HoverPoint >= AToIndexPlus1) then HoverPoint := HoverPoint - delCount;
  854. EndUpdate;
  855. end;
  856. procedure TCustomPolypointShape.InsertPoint(AIndex: integer; APoint: TPointF);
  857. var
  858. i: Integer;
  859. begin
  860. if (AIndex < 0) or (AIndex > PointCount) then raise exception.Create(rsIndexOutOfBounds);
  861. BeginUpdate(TCustomPolypointShapeDiff);
  862. setlength(FPoints, PointCount+1);
  863. for i := PointCount-1 downto AIndex+1 do
  864. FPoints[i] := FPoints[i-1];
  865. FPoints[AIndex].coord := APoint;
  866. FPoints[AIndex].editorIndex:= -1;
  867. FPoints[AIndex].data := 0;
  868. if (HoverPoint <> -1) and (HoverPoint >= AIndex) then HoverPoint := HoverPoint + 1;
  869. EndUpdate;
  870. end;
  871. function TCustomPolypointShape.GetPointBounds(AMatrix: TAffineMatrix): TRectF;
  872. begin
  873. result := GetPointsBoundsF(GetCurve(AMatrix));
  874. end;
  875. procedure TCustomPolypointShape.MouseMove(Shift: TShiftState; X, Y: single; var
  876. ACursor: TOriginalEditorCursor; var AHandled: boolean);
  877. begin
  878. FMousePos := PointF(X,Y);
  879. if FAddingPoint then
  880. begin
  881. BeginUpdate;
  882. if (PointCount = 1) and (FMousePos <> Points[PointCount-1]) then
  883. Points[PointCount] := FMousePos
  884. else
  885. Points[PointCount-1] := FMousePos;
  886. FillFit;
  887. EndUpdate;
  888. AHandled:= true;
  889. end;
  890. end;
  891. procedure TCustomPolypointShape.MouseDown(RightButton: boolean;
  892. ClickCount: integer; Shift: TShiftState; X, Y: single; var ACursor: TOriginalEditorCursor; var
  893. AHandled: boolean);
  894. begin
  895. FMousePos := PointF(X,Y);
  896. if FAddingPoint then
  897. begin
  898. if not RightButton then
  899. begin
  900. if (PointCount>1) and not PointsEqual(FMousePos,Points[PointCount-2]) then
  901. begin
  902. BeginUpdate;
  903. Points[PointCount-1] := FMousePos;
  904. AddPoint(FMousePos);
  905. EndUpdate;
  906. end;
  907. end else
  908. Usermode := vsuEdit;
  909. AHandled:= true;
  910. end else
  911. begin
  912. if (ssShift in Shift) and (Usermode = vsuEdit) then
  913. begin
  914. BeginUpdate;
  915. AddPoint(EmptyPointF);
  916. AddPoint(FMousePos);
  917. FillFit;
  918. EndUpdate;
  919. UserMode := vsuCreate;
  920. AHandled:= true;
  921. end;
  922. end;
  923. end;
  924. procedure TCustomPolypointShape.KeyDown(Shift: TShiftState; Key: TSpecialKey;
  925. var AHandled: boolean);
  926. var
  927. nb, idx: Integer;
  928. dx, dy, d: TPointF;
  929. begin
  930. if (Key = skDelete) and (FAddingPoint or (HoverPoint <> -1)) then
  931. begin
  932. if (HoverPoint <> -1) then
  933. begin
  934. BeginUpdate(TCustomPolypointShapeDiff);
  935. idx := HoverPoint;
  936. RemovePoint(idx);
  937. if ((idx = PointCount) or IsEmptyPointF(Points[idx])) and
  938. ((idx = 0) or IsEmptyPointF(Points[idx-1])) then
  939. begin
  940. if idx < PointCount then
  941. RemovePoint(idx)
  942. else if idx > 0 then
  943. RemovePoint(idx-1);
  944. end;
  945. EndUpdate;
  946. if PointCount = 0 then self.Remove;
  947. end;
  948. AHandled:= true;
  949. end else
  950. if (Key = skBackspace) and FAddingPoint then
  951. begin
  952. nb := GetLoopPointCount;
  953. if nb > 2 then
  954. RemovePoint(PointCount-2)
  955. else
  956. begin
  957. if GetLoopStartIndex = 0 then self.Remove
  958. else
  959. begin
  960. RemovePointRange(PointCount-3, PointCount);
  961. Usermode:= vsuEdit;
  962. end;
  963. end;
  964. AHandled:= true;
  965. end else
  966. if (Key = skInsert) then
  967. begin
  968. InsertPointAuto(Shift);
  969. AHandled := true;
  970. end else
  971. if (Key in [skLeft,skUp,skRight,skDown]) and ((HoverPoint <> -1) or HoverCenter) then
  972. begin
  973. if ssCtrl in Shift then
  974. begin
  975. dx := PointF(FGridMatrix[1,1], FGridMatrix[2,1]);
  976. dy := PointF(FGridMatrix[1,2], FGridMatrix[2,2]);
  977. end else
  978. begin
  979. dx := PointF(FViewMatrixInverse[1,1], FViewMatrixInverse[2,1]);
  980. dy := PointF(FViewMatrixInverse[1,2], FViewMatrixInverse[2,2]);
  981. end;
  982. case Key of
  983. skLeft: d := -dx;
  984. skRight: d := dx;
  985. skUp: d := -dy;
  986. skDown: d := dy;
  987. else d := PointF(0,0);
  988. end;
  989. if HoverCenter then
  990. Center := Center + d
  991. else
  992. Points[HoverPoint] := Points[HoverPoint] + d;
  993. AHandled := true;
  994. end else
  995. if Key = skAlt then
  996. begin
  997. BeginUpdate;
  998. FAltPressed := true;
  999. EndUpdate;
  1000. AHandled := true;
  1001. end
  1002. else
  1003. inherited KeyDown(Shift, Key, AHandled);
  1004. end;
  1005. procedure TCustomPolypointShape.KeyUp(Shift: TShiftState; Key: TSpecialKey;
  1006. var AHandled: boolean);
  1007. begin
  1008. if Key = skAlt then
  1009. begin
  1010. BeginUpdate;
  1011. FAltPressed := false;
  1012. EndUpdate;
  1013. AHandled := true;
  1014. end
  1015. else inherited KeyUp(Shift, Key, AHandled);
  1016. end;
  1017. procedure TCustomPolypointShape.QuickDefine(constref APoint1, APoint2: TPointF);
  1018. begin
  1019. BeginUpdate(TCustomPolypointShapeDiff);
  1020. FPoints := nil;
  1021. AddPoint(APoint1);
  1022. if not PointsEqual(APoint1,APoint2) then
  1023. AddPoint(APoint2);
  1024. EndUpdate;
  1025. FMousePos := APoint2;
  1026. end;
  1027. procedure TCustomPolypointShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  1028. var
  1029. x,y: Array of Single;
  1030. i: Integer;
  1031. begin
  1032. BeginUpdate;
  1033. inherited LoadFromStorage(AStorage);
  1034. Clear;
  1035. x := AStorage.FloatArray['x'];
  1036. y := AStorage.FloatArray['y'];
  1037. setlength(FPoints, max(length(x),length(y)));
  1038. for i := 0 to high(FPoints) do
  1039. begin
  1040. FPoints[i].coord := PointF(x[i],y[i]);
  1041. FPoints[i].editorIndex := -1;
  1042. FPoints[i].data := 0;
  1043. end;
  1044. FClosed:= AStorage.Bool['closed'];
  1045. if AStorage.HasAttribute('arrow-size') then
  1046. FArrowSize := AStorage.PointF['arrow-size']
  1047. else FArrowSize := DefaultArrowSize;
  1048. FArrowStartKind:= StrToArrowKind(AStorage.RawString['arrow-start-kind']);
  1049. FArrowEndKind:= StrToArrowKind(AStorage.RawString['arrow-end-kind']);
  1050. Stroker.LineCap := StrToLineCap(AStorage.RawString['line-cap']);
  1051. EndUpdate;
  1052. end;
  1053. procedure TCustomPolypointShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  1054. var
  1055. x,y: Array of Single;
  1056. i: Integer;
  1057. begin
  1058. inherited SaveToStorage(AStorage);
  1059. setlength({%H-}x, PointCount);
  1060. setlength({%H-}y, PointCount);
  1061. for i:= 0 to PointCount-1 do
  1062. begin
  1063. x[i] := Points[i].x;
  1064. y[i] := Points[i].y;
  1065. end;
  1066. AStorage.FloatArray['x'] := x;
  1067. AStorage.FloatArray['y'] := y;
  1068. AStorage.Bool['closed'] := Closed;
  1069. if ArrowStartKind=akNone then AStorage.RemoveAttribute('arrow-start-kind')
  1070. else AStorage.RawString['arrow-start-kind'] := ArrowKindToStr[ArrowStartKind];
  1071. if ArrowEndKind=akNone then AStorage.RemoveAttribute('arrow-end-kind')
  1072. else AStorage.RawString['arrow-end-kind'] := ArrowKindToStr[ArrowEndKind];
  1073. if (ArrowStartKind=akNone) and (ArrowEndKind=akNone) then AStorage.RemoveAttribute('arrow-size')
  1074. else AStorage.PointF['arrow-size'] := FArrowSize;
  1075. AStorage.RawString['line-cap'] := LineCapToStr[Stroker.LineCap];
  1076. end;
  1077. procedure TCustomPolypointShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
  1078. var
  1079. i, nbTotal: Integer;
  1080. begin
  1081. FViewMatrix := AEditor.Matrix;
  1082. if not IsAffineMatrixInversible(FViewMatrix) then exit;
  1083. FViewMatrixInverse := AffineMatrixInverse(FViewMatrix);
  1084. FGridMatrix := AEditor.GridMatrix;
  1085. AEditor.AddStartMoveHandler(@OnStartMove);
  1086. AEditor.AddClickPointHandler(@OnClickPoint);
  1087. AEditor.AddHoverPointHandler(@OnHoverPoint);
  1088. FCenterPoint := PointF(0,0);
  1089. nbTotal := 0;
  1090. for i:= 0 to PointCount-1 do
  1091. if isEmptyPointF(Points[i]) then
  1092. FPoints[i].editorIndex := -1
  1093. else if (FAddingPoint and (i = PointCount-1) and (GetLoopPointCount > 1)) then
  1094. begin
  1095. FPoints[i].editorIndex := -1;
  1096. FCenterPoint += Points[i];
  1097. inc(nbTotal);
  1098. end
  1099. else
  1100. begin
  1101. if CanMovePoints then
  1102. FPoints[i].editorIndex := AEditor.AddPoint(Points[i], @OnMovePoint, false)
  1103. else
  1104. FPoints[i].editorIndex := AEditor.AddFixedPoint(Points[i], false);
  1105. FCenterPoint += Points[i];
  1106. if i = HoverPoint then
  1107. AEditor.PointHighlighted[FPoints[i].editorIndex] := true;
  1108. inc(nbTotal);
  1109. end;
  1110. if nbTotal > 0 then
  1111. FCenterPoint *= 1/nbTotal
  1112. else FCenterPoint := EmptyPointF;
  1113. if ((FAddingPoint and (nbTotal > 2)) or (not FAddingPoint and (nbTotal > 1)))
  1114. and not FAltPressed then
  1115. begin
  1116. FCenterPointEditorIndex := AEditor.AddPoint(FCenterPoint, @OnMoveCenterPoint, true);
  1117. AEditor.PointHighlighted[FCenterPointEditorIndex] := HoverCenter;
  1118. end else
  1119. FCenterPointEditorIndex := -1;
  1120. end;
  1121. procedure TCustomPolypointShape.TransformFrame(const AMatrix: TAffineMatrix);
  1122. var
  1123. i: Integer;
  1124. m: TAffineMatrix;
  1125. begin
  1126. BeginUpdate(TCustomPolypointShapeDiff);
  1127. m := MatrixForPixelCentered(AMatrix);
  1128. for i := 0 to PointCount-1 do
  1129. FPoints[i].coord := m*FPoints[i].coord;
  1130. EndUpdate;
  1131. end;
  1132. { TPolylineShape }
  1133. class function TPolylineShape.Fields: TVectorShapeFields;
  1134. begin
  1135. Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfJoinStyle, vsfBackFill];
  1136. end;
  1137. procedure TPolylineShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  1138. ADraft: boolean);
  1139. var
  1140. pts: array of TPointF;
  1141. backScan, penScan: TBGRACustomScanner;
  1142. begin
  1143. if not GetBackVisible and not GetPenVisible then exit;
  1144. pts := GetCurve(AMatrix);
  1145. if GetBackVisible then
  1146. begin
  1147. if BackFill.FillType = vftSolid then backScan := nil
  1148. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  1149. if ADraft then
  1150. begin
  1151. if Assigned(backScan) then
  1152. ADest.FillPoly(pts, backScan, dmDrawWithTransparency) else
  1153. ADest.FillPoly(pts, BackFill.SolidColor, dmDrawWithTransparency);
  1154. end
  1155. else
  1156. begin
  1157. if Assigned(backScan) then
  1158. ADest.FillPolyAntialias(pts, backScan) else
  1159. ADest.FillPolyAntialias(pts, BackFill.SolidColor);
  1160. end;
  1161. backScan.Free;
  1162. end;
  1163. if GetPenVisible then
  1164. begin
  1165. if PenFill.FillType = vftSolid then penScan := nil
  1166. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  1167. pts := ComputeStroke(pts, Closed, AMatrix);
  1168. if ADraft and (PenWidth > 4) then
  1169. begin
  1170. if Assigned(penScan) then
  1171. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  1172. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency);
  1173. end
  1174. else
  1175. begin
  1176. if Assigned(penScan) then
  1177. ADest.FillPolyAntialias(pts, penScan) else
  1178. ADest.FillPolyAntialias(pts, PenColor);
  1179. end;
  1180. penScan.Free;
  1181. end;
  1182. end;
  1183. function TPolylineShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  1184. var
  1185. p: TBGRAPath;
  1186. begin
  1187. p := GetPath(AffineMatrixIdentity);
  1188. result := AContent.AppendPath(p.SvgString);
  1189. p.Free;
  1190. ApplyStrokeStyleToSVG(result, ADefs);
  1191. if PenVisible then
  1192. result.strokeLineCapLCL := LineCap;
  1193. ApplyFillStyleToSVG(result, ADefs);
  1194. end;
  1195. function TPolylineShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
  1196. var
  1197. pts: ArrayOfTPointF;
  1198. xMargin, yMargin: single;
  1199. fillBounds, penBounds: TRectF;
  1200. begin
  1201. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
  1202. result:= EmptyRectF
  1203. else
  1204. begin
  1205. pts := GetCurve(AMatrix);
  1206. if GetPenVisible(rboAssumePenFill in AOptions) then
  1207. begin
  1208. if (JoinStyle = pjsRound) and (ArrowStartKind = akNone) and (ArrowEndKind = akNone) then
  1209. begin
  1210. xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
  1211. yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
  1212. if LineCap = pecSquare then
  1213. begin
  1214. xMargin *= sqrt(2);
  1215. yMargin *= sqrt(2);
  1216. end;
  1217. result := GetPointsBoundsF(pts);
  1218. result.Left -= xMargin;
  1219. result.Top -= yMargin;
  1220. result.Right += xMargin;
  1221. result.Bottom += yMargin;
  1222. end else
  1223. begin
  1224. if GetBackVisible or (rboAssumeBackFill in AOptions) then fillBounds := GetPointsBoundsF(pts)
  1225. else fillBounds := EmptyRectF;
  1226. pts := ComputeStroke(pts, Closed, AMatrix);
  1227. penBounds := GetPointsBoundsF(pts);
  1228. result := fillBounds.Union(penBounds, true);
  1229. end;
  1230. end
  1231. else
  1232. result := GetPointsBoundsF(pts);
  1233. end;
  1234. result.Offset(0.5,0.5);
  1235. end;
  1236. function TPolylineShape.PointInShape(APoint: TPointF): boolean;
  1237. var
  1238. pts: ArrayOfTPointF;
  1239. begin
  1240. if not GetBackVisible and not GetPenVisible then exit(false);
  1241. pts := GetCurve(AffineMatrixIdentity);
  1242. if GetBackVisible and IsPointInPolygon(pts, APoint, true) then exit(true);
  1243. if GetPenVisible then
  1244. begin
  1245. pts := ComputeStroke(pts, Closed, AffineMatrixIdentity);
  1246. if IsPointInPolygon(pts, APoint, true) then exit(true);
  1247. end;
  1248. result := false;
  1249. end;
  1250. function TPolylineShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1251. var
  1252. pts: ArrayOfTPointF;
  1253. begin
  1254. if not GetBackVisible and not GetPenVisible then exit(false);
  1255. pts := GetCurve(AffineMatrixIdentity);
  1256. pts := ComputeStrokeEnvelope(pts, Closed, ARadius*2);
  1257. result := IsPointInPolygon(pts, APoint, true);
  1258. end;
  1259. function TPolylineShape.PointInBack(APoint: TPointF): boolean;
  1260. var
  1261. pts: ArrayOfTPointF;
  1262. scan: TBGRACustomScanner;
  1263. begin
  1264. if GetBackVisible then
  1265. begin
  1266. pts := GetCurve(AffineMatrixIdentity);
  1267. result := IsPointInPolygon(pts, APoint, true);
  1268. if result and (BackFill.FillType = vftTexture) then
  1269. begin
  1270. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1271. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1272. scan.Free;
  1273. end;
  1274. end else
  1275. result := false;
  1276. end;
  1277. function TPolylineShape.PointInPen(APoint: TPointF): boolean;
  1278. var
  1279. pts: ArrayOfTPointF;
  1280. begin
  1281. if GetBackVisible then
  1282. begin
  1283. pts := GetCurve(AffineMatrixIdentity);
  1284. pts := ComputeStroke(pts, Closed, AffineMatrixIdentity);
  1285. result := IsPointInPolygon(pts, APoint, true);
  1286. end else
  1287. result := false;
  1288. end;
  1289. function TPolylineShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  1290. var pts: ArrayOfTPointF;
  1291. i: Integer;
  1292. ptsBounds: TRectF;
  1293. backSurface: Single;
  1294. penLength, zoomFactor, penSurface, totalSurface: single;
  1295. begin
  1296. if not GetPenVisible and not GetBackVisible or (PointCount = 0) then exit(false);
  1297. setlength({%H-}pts, PointCount);
  1298. for i := 0 to high(pts) do
  1299. pts[i] := AMatrix * Points[i];
  1300. if GetPenVisible then
  1301. begin
  1302. penLength := 0;
  1303. zoomFactor := max(VectLen(AMatrix[1,1],AMatrix[2,1]), VectLen(AMatrix[1,2],AMatrix[2,2]));
  1304. for i := 0 to high(pts) do
  1305. if (i > 0) then
  1306. begin
  1307. if pts[i-1].IsEmpty then
  1308. begin
  1309. if not pts[i].IsEmpty and (LineCap <> pecFlat) then penLength += penWidth/2*zoomFactor;
  1310. end else
  1311. if pts[i].IsEmpty then
  1312. begin
  1313. if not pts[i-1].IsEmpty and (LineCap <> pecFlat) then penLength += penWidth/2*zoomFactor;
  1314. end else
  1315. penLength += VectLen(pts[i]-pts[i-1]);
  1316. end;
  1317. penSurface := penLength*PenWidth*zoomFactor;
  1318. end else penSurface := 0;
  1319. if GetBackVisible then
  1320. begin
  1321. ptsBounds := GetPointsBoundsF(pts);
  1322. backSurface := ptsBounds.Width*ptsBounds.Height;
  1323. end else
  1324. backSurface := 0;
  1325. if GetPenVisible and GetBackVisible then totalSurface := backSurface+penSurface/2
  1326. else totalSurface := backSurface+penSurface;
  1327. Result:= (PointCount > 40) or
  1328. ((penSurface > 320*240) and PenFill.IsSlow(AMatrix)) or
  1329. ((backSurface > 320*240) and BackFill.IsSlow(AMatrix)) or
  1330. (totalSurface > 640*480);
  1331. end;
  1332. class function TPolylineShape.StorageClassName: RawByteString;
  1333. begin
  1334. result := 'polyline';
  1335. end;
  1336. { TCurveShape }
  1337. procedure TCurveShape.SetSplineStyle(AValue: TSplineStyle);
  1338. begin
  1339. if FSplineStyle=AValue then Exit;
  1340. BeginUpdate(TCurveShapeDiff);
  1341. FSplineStyle:=AValue;
  1342. EndUpdate;
  1343. end;
  1344. function TCurveShape.GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
  1345. begin
  1346. if (AIndex < 0) or (AIndex >= PointCount) then exit(cmCurve);
  1347. result := TEasyBezierCurveMode(FPoints[AIndex].data);
  1348. end;
  1349. procedure TCurveShape.SetCosineAngle(AValue: single);
  1350. begin
  1351. if FCosineAngle=AValue then Exit;
  1352. BeginUpdate(TCurveShapeDiff);
  1353. FCosineAngle:=AValue;
  1354. EndUpdate;
  1355. end;
  1356. procedure TCurveShape.SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
  1357. begin
  1358. if (AIndex < 0) or (AIndex >= PointCount) then exit;
  1359. if CurveMode[AIndex] = AValue then exit;
  1360. BeginUpdate(TCustomPolypointShapeDiff);
  1361. FPoints[AIndex].data := ord(AValue);
  1362. EndUpdate
  1363. end;
  1364. function TCurveShape.GetCurve(AMatrix: TAffineMatrix): ArrayOfTPointF;
  1365. var
  1366. pts: array of TPointF;
  1367. cm: array of TEasyBezierCurveMode;
  1368. i: Integer;
  1369. eb: TEasyBezierCurve;
  1370. begin
  1371. pts := inherited GetCurve(AMatrix);
  1372. if FSplineStyle = ssEasyBezier then
  1373. begin
  1374. setlength({%H-}cm, PointCount);
  1375. for i := 0 to PointCount-1 do
  1376. cm[i] := CurveMode[i];
  1377. eb := EasyBezierCurve(pts, Closed, cm, CosineAngle);
  1378. result := eb.ToPoints;
  1379. end else
  1380. begin
  1381. if Closed then result := ComputeClosedSpline(pts, FSplineStyle)
  1382. else result := ComputeOpenedSpline(pts, FSplineStyle);
  1383. end;
  1384. end;
  1385. function TCurveShape.GetPath(AMatrix: TAffineMatrix): TBGRAPath;
  1386. var
  1387. pts: array of TPointF;
  1388. cm: array of TEasyBezierCurveMode;
  1389. i: Integer;
  1390. eb: TEasyBezierCurve;
  1391. begin
  1392. pts := inherited GetCurve(AMatrix);
  1393. if FSplineStyle = ssEasyBezier then
  1394. begin
  1395. setlength({%H-}cm, PointCount);
  1396. for i := 0 to PointCount-1 do
  1397. cm[i] := CurveMode[i];
  1398. eb := EasyBezierCurve(pts, Closed, cm, CosineAngle);
  1399. result := TBGRAPath.Create;
  1400. eb.CopyToPath(result);
  1401. end else
  1402. begin
  1403. if Closed then pts := ComputeClosedSpline(pts, FSplineStyle)
  1404. else pts := ComputeOpenedSpline(pts, FSplineStyle);
  1405. result := GetPath(pts);
  1406. end;
  1407. end;
  1408. function TCurveShape.CanMovePoints: boolean;
  1409. begin
  1410. Result:= Usermode in [vsuCreate,vsuEdit];
  1411. end;
  1412. procedure TCurveShape.DoClickPoint(APointIndex: integer; AShift: TShiftState);
  1413. begin
  1414. case Usermode of
  1415. vsuCurveSetAuto: CurveMode[APointIndex] := cmAuto;
  1416. vsuCurveSetCurve: CurveMode[APointIndex] := cmCurve;
  1417. vsuCurveSetAngle: CurveMode[APointIndex] := cmAngle;
  1418. else
  1419. inherited DoClickPoint(APointIndex, AShift);
  1420. end;
  1421. end;
  1422. class function TCurveShape.Usermodes: TVectorShapeUsermodes;
  1423. begin
  1424. Result:=inherited Usermodes + [vsuCurveSetAuto, vsuCurveSetCurve, vsuCurveSetAngle];
  1425. end;
  1426. constructor TCurveShape.Create(AContainer: TVectorOriginal);
  1427. begin
  1428. inherited Create(AContainer);
  1429. FSplineStyle:= ssEasyBezier;
  1430. end;
  1431. constructor TCurveShape.CreateFrom(AContainer: TVectorOriginal;
  1432. AShape: TVectorShape);
  1433. var
  1434. r: TCustomRectShape;
  1435. u, v: TPointF;
  1436. p: TCustomPolypointShape;
  1437. i: Integer;
  1438. f: TVectorShapeFields;
  1439. sq2m1: single;
  1440. begin
  1441. Create(AContainer);
  1442. if AShape is TEllipseShape then
  1443. begin
  1444. r := AShape as TCustomRectShape;
  1445. u := r.XAxis-r.Origin;
  1446. v := r.YAxis-r.Origin;
  1447. sq2m1 := sqrt(2)-1;
  1448. AddPoint(r.Origin-v+u*sq2m1);
  1449. AddPoint(r.Origin-v*sq2m1+u);
  1450. AddPoint(r.Origin+v*sq2m1+u);
  1451. AddPoint(r.Origin+v+u*sq2m1);
  1452. AddPoint(r.Origin+v-u*sq2m1);
  1453. AddPoint(r.Origin+v*sq2m1-u);
  1454. AddPoint(r.Origin-v*sq2m1-u);
  1455. AddPoint(r.Origin-v-u*sq2m1);
  1456. Closed := true;
  1457. end else
  1458. if AShape is TRectShape then
  1459. begin
  1460. r := AShape as TCustomRectShape;
  1461. u := r.XAxis-r.Origin;
  1462. v := r.YAxis-r.Origin;
  1463. AddPoint(r.Origin-v-u, cmAngle);
  1464. AddPoint(r.Origin-v+u, cmAngle);
  1465. AddPoint(r.Origin+v+u, cmAngle);
  1466. AddPoint(r.Origin+v-u, cmAngle);
  1467. Closed := true;
  1468. end else
  1469. if (AShape is TPolylineShape) and not
  1470. (AShape is TCurveShape) then
  1471. begin
  1472. p := AShape as TCustomPolypointShape;
  1473. for i := 0 to p.PointCount-1 do
  1474. AddPoint(p.Points[i], cmAngle);
  1475. Closed := p.Closed;
  1476. end else
  1477. raise exception.Create(errShapeNotHandled);
  1478. f := AShape.Fields;
  1479. if vsfPenFill in f then PenFill.Assign(AShape.PenFill);
  1480. if vsfPenWidth in f then PenWidth := AShape.PenWidth;
  1481. if vsfPenStyle in f then PenStyle := AShape.PenStyle;
  1482. if vsfJoinStyle in f then JoinStyle := AShape.JoinStyle;
  1483. if vsfBackFill in f then BackFill.Assign(AShape.BackFill);
  1484. end;
  1485. class function TCurveShape.CanCreateFrom(AShape: TVectorShape): boolean;
  1486. begin
  1487. result := (AShape is TEllipseShape) or
  1488. (AShape is TRectShape) or
  1489. ((AShape is TPolylineShape) and not
  1490. (AShape is TCurveShape));
  1491. end;
  1492. function TCurveShape.AddPoint(const APoint: TPointF): integer;
  1493. begin
  1494. if (PointCount > 1) and (APoint = Points[PointCount-1]) then
  1495. begin
  1496. BeginUpdate;
  1497. CurveMode[PointCount-1] := CurveMode[PointCount-2];
  1498. Result:=inherited AddPoint(APoint);
  1499. EndUpdate;
  1500. end
  1501. else Result:=inherited AddPoint(APoint);
  1502. end;
  1503. function TCurveShape.AddPoint(const APoint: TPointF; AMode: TEasyBezierCurveMode): integer;
  1504. begin
  1505. result := inherited AddPoint(APoint);
  1506. CurveMode[result] := AMode;
  1507. end;
  1508. procedure TCurveShape.KeyPress(UTF8Key: string; var AHandled: boolean);
  1509. var
  1510. targetPoint: Integer;
  1511. begin
  1512. if HoverPoint<>-1 then
  1513. targetPoint := HoverPoint
  1514. else if FAddingPoint and (PointCount > 1) then
  1515. targetPoint := PointCount-2
  1516. else
  1517. targetPoint := -1;
  1518. if (targetPoint >= 0) and (targetPoint < PointCount) then
  1519. begin
  1520. if (UTF8Key = 'A') or (UTF8Key = 'a') then
  1521. begin
  1522. CurveMode[targetPoint] := cmAuto;
  1523. AHandled := true;
  1524. end else
  1525. if (UTF8Key = 'S') or (UTF8Key = 's') then
  1526. begin
  1527. CurveMode[targetPoint] := cmCurve;
  1528. AHandled:= true;
  1529. end else
  1530. if (UTF8Key = 'X') or (UTF8Key = 'x') then
  1531. begin
  1532. CurveMode[targetPoint] := cmAngle;
  1533. AHandled:= true;
  1534. end;
  1535. end;
  1536. if not AHandled then
  1537. inherited KeyPress(UTF8Key, AHandled);
  1538. end;
  1539. procedure TCurveShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  1540. var
  1541. i: Integer;
  1542. cm: array of Single;
  1543. begin
  1544. BeginUpdate;
  1545. inherited LoadFromStorage(AStorage);
  1546. case AStorage.RawString['spline-style'] of
  1547. 'inside': SplineStyle := ssInside;
  1548. 'inside+ends': SplineStyle := ssInsideWithEnds;
  1549. 'crossing': SplineStyle := ssCrossing;
  1550. 'crossing+ends': SplineStyle := ssCrossingWithEnds;
  1551. 'outside': SplineStyle := ssOutside;
  1552. 'round-outside': SplineStyle := ssRoundOutside;
  1553. 'vertex-to-side': SplineStyle := ssVertexToSide;
  1554. else
  1555. {'easy-bezier'} SplineStyle := ssEasyBezier;
  1556. end;
  1557. if SplineStyle = ssEasyBezier then
  1558. begin
  1559. cm := AStorage.FloatArray['curve-mode'];
  1560. for i := 0 to min(high(cm),PointCount-1) do
  1561. case round(cm[i]) of
  1562. 1: CurveMode[i] := cmCurve;
  1563. 2: CurveMode[i] := cmAngle;
  1564. end;
  1565. if length(cm) < PointCount then
  1566. for i:= length(cm) to PointCount-1 do
  1567. CurveMode[i] := cmCurve;
  1568. end;
  1569. CosineAngle := AStorage.FloatDef['cosine-angle', EasyBezierDefaultMinimumDotProduct];
  1570. EndUpdate;
  1571. end;
  1572. procedure TCurveShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  1573. var s: string;
  1574. cm: array of single;
  1575. i: Integer;
  1576. begin
  1577. inherited SaveToStorage(AStorage);
  1578. case SplineStyle of
  1579. ssInside: s := 'inside';
  1580. ssInsideWithEnds: s := 'inside+ends';
  1581. ssCrossing: s := 'crossing';
  1582. ssCrossingWithEnds: s := 'crossing+ends';
  1583. ssOutside: s := 'outside';
  1584. ssRoundOutside: s := 'round-outside';
  1585. ssVertexToSide: s := 'vertex-to-side';
  1586. ssEasyBezier: s := 'easy-bezier';
  1587. else s := '';
  1588. end;
  1589. AStorage.RawString['spline-style'] := s;
  1590. if SplineStyle = ssEasyBezier then
  1591. begin
  1592. setlength({%H-}cm, PointCount);
  1593. for i := 0 to PointCount-1 do
  1594. cm[i] := ord(CurveMode[i]);
  1595. AStorage.FloatArray['curve-mode'] := cm;
  1596. end;
  1597. AStorage.Float['cosine-angle'] := CosineAngle;
  1598. end;
  1599. class function TCurveShape.StorageClassName: RawByteString;
  1600. begin
  1601. Result:= 'curve';
  1602. end;
  1603. initialization
  1604. RegisterVectorShape(TPolylineShape);
  1605. RegisterVectorShape(TCurveShape);
  1606. end.