utoolpolygon.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UToolPolygon;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, UTool, UToolVectorial, BGRABitmap, BGRABitmapTypes,
  7. LCVectorOriginal, LCLType;
  8. const
  9. EasyBezierMinimumDotProduct = 0.5;
  10. type
  11. { TToolRectangle }
  12. TToolRectangle = class(TVectorialTool)
  13. protected
  14. function ShapeClass: TVectorShapeAny; override;
  15. end;
  16. { TToolEllipse }
  17. TToolEllipse = class(TVectorialTool)
  18. protected
  19. function ShapeClass: TVectorShapeAny; override;
  20. function GetGridMatrix: TAffineMatrix; override;
  21. end;
  22. { TToolPolygon }
  23. TToolPolygon = class(TVectorialTool)
  24. protected
  25. class var RightClickHintShown: boolean;
  26. class var RemovePointHintShown: boolean;
  27. initiallyClosed : boolean;
  28. function ShapeClass: TVectorShapeAny; override;
  29. function CreateShape: TVectorShape; override;
  30. function ShouldCloseShape: boolean; virtual;
  31. procedure UpdateManagerCloseShape({%H-}AClose: boolean); virtual;
  32. procedure AssignShapeStyleClosed(AShape: TVectorShape); virtual;
  33. procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
  34. procedure UpdateUserMode; virtual;
  35. procedure ShapeValidated; override;
  36. function DoToolKeyDown(var key: Word): TRect; override;
  37. function RoundCoordinate(constref ptF: TPointF): TPointF; override;
  38. public
  39. class procedure ForgetHintShown;
  40. function ToolUp: TRect; override;
  41. function ToolKeyPress(var key: TUTF8Char): TRect; override;
  42. end;
  43. { TToolPolyline }
  44. TToolPolyline = class(TToolPolygon)
  45. protected
  46. function CreateShape: TVectorShape; override;
  47. function ShouldCloseShape: boolean; override;
  48. procedure AssignShapeStyleClosed(AShape: TVectorShape); override;
  49. procedure UpdateManagerCloseShape({%H-}AClose: boolean); override;
  50. function GetManagerShapeOptions: TShapeOptions; override;
  51. function HasBrush: boolean; override;
  52. public
  53. function HasPen: boolean; override;
  54. function GetContextualToolbars: TContextualToolbars; override;
  55. end;
  56. { TToolSpline }
  57. TToolSpline = class(TToolPolygon)
  58. private
  59. FCurrentMode: TToolSplineMode;
  60. FNextCurveMode: TEasyBezierCurveMode;
  61. FCurveModeHintShown: Boolean;
  62. function GetCurrentMode: TToolSplineMode;
  63. procedure SetCurrentMode(AValue: TToolSplineMode);
  64. protected
  65. function ShapeClass: TVectorShapeAny; override;
  66. function CreateShape: TVectorShape; override;
  67. procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
  68. procedure UpdateUserMode; override;
  69. public
  70. constructor Create(AManager: TToolManager); override;
  71. function ToolKeyPress(var key: TUTF8Char): TRect; override;
  72. property CurrentMode: TToolSplineMode read GetCurrentMode write SetCurrentMode;
  73. end;
  74. { TToolOpenedCurve }
  75. TToolOpenedCurve = class(TToolSpline)
  76. protected
  77. function ShouldCloseShape: boolean; override;
  78. procedure UpdateManagerCloseShape({%H-}AClose: boolean); override;
  79. function GetManagerShapeOptions: TShapeOptions; override;
  80. function HasBrush: boolean; override;
  81. public
  82. function HasPen: boolean; override;
  83. function GetContextualToolbars: TContextualToolbars; override;
  84. end;
  85. implementation
  86. uses LazPaintType, LCVectorRectShapes, LCVectorPolyShapes, BGRATransform;
  87. { TToolOpenedCurve }
  88. function TToolOpenedCurve.ShouldCloseShape: boolean;
  89. begin
  90. result := false;
  91. end;
  92. procedure TToolOpenedCurve.UpdateManagerCloseShape(AClose: boolean);
  93. begin
  94. //nothing
  95. end;
  96. function TToolOpenedCurve.GetManagerShapeOptions: TShapeOptions;
  97. begin
  98. Result:= manager.ShapeOptions - [toFillShape] + [toDrawShape];
  99. end;
  100. function TToolOpenedCurve.HasPen: boolean;
  101. begin
  102. Result:= true;
  103. end;
  104. function TToolOpenedCurve.HasBrush: boolean;
  105. begin
  106. Result:= false;
  107. end;
  108. function TToolOpenedCurve.GetContextualToolbars: TContextualToolbars;
  109. begin
  110. Result:= inherited GetContextualToolbars - [ctShape, ctCloseShape];
  111. end;
  112. { TToolPolyline }
  113. function TToolPolyline.CreateShape: TVectorShape;
  114. begin
  115. Result:=inherited CreateShape;
  116. inherited AssignShapeStyleClosed(Result);
  117. end;
  118. function TToolPolyline.ShouldCloseShape: boolean;
  119. begin
  120. result := false;
  121. end;
  122. procedure TToolPolyline.AssignShapeStyleClosed(AShape: TVectorShape);
  123. begin
  124. //nothing
  125. end;
  126. procedure TToolPolyline.UpdateManagerCloseShape(AClose: boolean);
  127. begin
  128. //nothing
  129. end;
  130. function TToolPolyline.GetManagerShapeOptions: TShapeOptions;
  131. begin
  132. Result:= manager.ShapeOptions - [toFillShape] + [toDrawShape];
  133. end;
  134. function TToolPolyline.HasPen: boolean;
  135. begin
  136. Result:= true;
  137. end;
  138. function TToolPolyline.HasBrush: boolean;
  139. begin
  140. Result:= false;
  141. end;
  142. function TToolPolyline.GetContextualToolbars: TContextualToolbars;
  143. begin
  144. Result:= inherited GetContextualToolbars - [ctShape, ctCloseShape];
  145. end;
  146. { TToolEllipse }
  147. function TToolEllipse.ShapeClass: TVectorShapeAny;
  148. begin
  149. result := TEllipseShape;
  150. end;
  151. function TToolEllipse.GetGridMatrix: TAffineMatrix;
  152. begin
  153. Result:= AffineMatrixScale(0.5, 0.5);
  154. end;
  155. { TToolRectangle }
  156. function TToolRectangle.ShapeClass: TVectorShapeAny;
  157. begin
  158. result := TRectShape;
  159. end;
  160. { TToolSpline }
  161. function TToolSpline.GetCurrentMode: TToolSplineMode;
  162. begin
  163. if Assigned(FShape) then
  164. FCurrentMode := ToolSplineModeFromShape(FShape);
  165. result := FCurrentMode;
  166. end;
  167. procedure TToolSpline.SetCurrentMode(AValue: TToolSplineMode);
  168. begin
  169. if FCurrentMode = AValue then exit;
  170. FCurrentMode := AValue;
  171. UpdateUserMode;
  172. end;
  173. function TToolSpline.ShapeClass: TVectorShapeAny;
  174. begin
  175. result := TCurveShape;
  176. end;
  177. procedure TToolSpline.UpdateUserMode;
  178. var
  179. c: TCurveShape;
  180. begin
  181. if FShape = nil then exit;
  182. if FQuickDefine then
  183. begin
  184. FShape.Usermode := vsuCreate;
  185. exit;
  186. end;
  187. c := TCurveShape(FShape);
  188. case FCurrentMode of
  189. tsmMovePoint: if not (c.Usermode in [vsuEdit,vsuCreate]) then c.Usermode := vsuEdit;
  190. tsmCurveModeAuto: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetAuto else
  191. if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmAuto;
  192. tsmCurveModeAngle: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetAngle else
  193. if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmAngle;
  194. tsmCurveModeSpline: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetCurve else
  195. if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmCurve;
  196. end;
  197. end;
  198. function TToolSpline.CreateShape: TVectorShape;
  199. begin
  200. result := inherited CreateShape;
  201. TCurveShape(result).CosineAngle:= EasyBezierMinimumDotProduct;
  202. if not FCurveModeHintShown then
  203. begin
  204. Manager.ToolPopup(tpmCurveModeHint);
  205. FCurveModeHintShown := true;
  206. end;
  207. end;
  208. procedure TToolSpline.AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean);
  209. begin
  210. inherited AssignShapeStyle(AMatrix, AAlwaysFit);
  211. TCurveShape(FShape).SplineStyle:= Manager.SplineStyle;
  212. end;
  213. constructor TToolSpline.Create(AManager: TToolManager);
  214. begin
  215. inherited Create(AManager);
  216. FNextCurveMode := cmAuto;
  217. end;
  218. function TToolSpline.ToolKeyPress(var key: TUTF8Char): TRect;
  219. begin
  220. if (Key='z') or (Key = 'Z') then
  221. begin
  222. CurrentMode:= tsmMovePoint;
  223. result := OnlyRenderChange;
  224. Key := #0;
  225. end else
  226. begin
  227. Result:=inherited ToolKeyPress(key);
  228. if Key='x' then Key := #0;
  229. end;
  230. end;
  231. { TToolPolygon }
  232. function TToolPolygon.ShapeClass: TVectorShapeAny;
  233. begin
  234. result := TPolylineShape;
  235. end;
  236. function TToolPolygon.CreateShape: TVectorShape;
  237. begin
  238. result := inherited CreateShape;
  239. initiallyClosed := ShouldCloseShape;
  240. if not RightClickHintShown then
  241. begin
  242. Manager.ToolPopup(tpmRightClickFinishShape);
  243. RightClickHintShown := true;
  244. end;
  245. end;
  246. function TToolPolygon.ShouldCloseShape: boolean;
  247. begin
  248. result := toCloseShape in Manager.ShapeOptions;
  249. end;
  250. procedure TToolPolygon.UpdateManagerCloseShape(AClose: boolean);
  251. var
  252. opt: TShapeOptions;
  253. begin
  254. opt := Manager.ShapeOptions;
  255. if AClose then
  256. include(opt, toCloseShape)
  257. else
  258. exclude(opt, toCloseShape);
  259. Manager.ShapeOptions:= opt;
  260. end;
  261. procedure TToolPolygon.AssignShapeStyleClosed(AShape: TVectorShape);
  262. begin
  263. (AShape as TCustomPolypointShape).Closed := ShouldCloseShape;
  264. end;
  265. procedure TToolPolygon.AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean);
  266. begin
  267. inherited AssignShapeStyle(AMatrix, AAlwaysFit);
  268. AssignShapeStyleClosed(TCustomPolypointShape(FShape));
  269. TCustomPolypointShape(FShape).ArrowStartKind := Manager.ArrowStart;
  270. TCustomPolypointShape(FShape).ArrowEndKind := Manager.ArrowEnd;
  271. TCustomPolypointShape(FShape).ArrowSize := Manager.ArrowSize;
  272. TCustomPolypointShape(FShape).LineCap:= Manager.LineCap;
  273. UpdateUserMode;
  274. end;
  275. procedure TToolPolygon.UpdateUserMode;
  276. begin
  277. if FShape = nil then exit;
  278. if FQuickDefine then FShape.Usermode := vsuCreate;
  279. end;
  280. procedure TToolPolygon.ShapeValidated;
  281. begin
  282. inherited ShapeValidated;
  283. if not initiallyClosed then UpdateManagerCloseShape(False);
  284. end;
  285. function TToolPolygon.ToolUp: TRect;
  286. begin
  287. Result:=inherited ToolUp;
  288. if Assigned(FShape) then
  289. begin
  290. UpdateManagerCloseShape((FShape as TCustomPolypointShape).Closed);
  291. if not RemovePointHintShown and ((FShape as TCustomPolypointShape).ValidatedPointCount >= 3) then
  292. begin
  293. Manager.ToolPopup(tpmBackspaceRemoveLastPoint);
  294. RemovePointHintShown := true;
  295. end;
  296. end;
  297. end;
  298. function TToolPolygon.ToolKeyPress(var key: TUTF8Char): TRect;
  299. var
  300. keyCode: Word;
  301. begin
  302. if (Key='i') or (Key='I') then
  303. begin
  304. keyCode := VK_INSERT;
  305. ToolKeyDown(keyCode);
  306. if keyCode = 0 then key := #0;
  307. keyCode := VK_INSERT;
  308. ToolKeyUp(keyCode);
  309. result := EmptyRect;
  310. end else
  311. Result:=inherited ToolKeyPress(key);
  312. end;
  313. function TToolPolygon.DoToolKeyDown(var key: Word): TRect;
  314. begin
  315. if (key = VK_RETURN) and Assigned(FShape)
  316. and (FShape.Usermode = vsuCreate) then
  317. begin
  318. FShape.Usermode:= vsuEdit;
  319. result := OnlyRenderChange;
  320. key := 0;
  321. exit;
  322. end else
  323. Result:=inherited DoToolKeyDown(key);
  324. end;
  325. function TToolPolygon.RoundCoordinate(constref ptF: TPointF): TPointF;
  326. begin
  327. If Editor.GridActive then
  328. result := Editor.SnapToGrid(ptF, false)
  329. else
  330. result := ptF;
  331. end;
  332. class procedure TToolPolygon.ForgetHintShown;
  333. begin
  334. RemovePointHintShown := false;
  335. RemovePointHintShown := false;
  336. end;
  337. initialization
  338. RegisterTool(ptRect,TToolRectangle);
  339. RegisterTool(ptEllipse,TToolEllipse);
  340. RegisterTool(ptPolygon,TToolPolygon);
  341. RegisterTool(ptSpline,TToolSpline);
  342. RegisterTool(ptPolyline,TToolPolyline);
  343. RegisterTool(ptOpenedCurve,TToolOpenedCurve);
  344. end.