lcvectorrectshapes.pas 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit LCVectorRectShapes;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, Types, LCVectorOriginal, BGRABitmapTypes, BGRALayerOriginal,
  7. BGRABitmap, BGRATransform, BGRAGradients, BGRASVGShapes, BGRASVGType, BGRAUnits;
  8. type
  9. TCustomRectShape = class;
  10. { TCustomRectShapeDiff }
  11. TCustomRectShapeDiff = class(TVectorShapeDiff)
  12. protected
  13. FStartOrigin, FStartXAxis, FStartYAxis: TPointF;
  14. FStartFixedRatio: Single;
  15. FEndOrigin, FEndXAxis, FEndYAxis: TPointF;
  16. FEndFixedRatio: Single;
  17. public
  18. constructor Create(AStartShape: TVectorShape); override;
  19. procedure ComputeDiff(AEndShape: TVectorShape); override;
  20. procedure Apply(AStartShape: TVectorShape); override;
  21. procedure Unapply(AEndShape: TVectorShape); override;
  22. procedure Append(ADiff: TVectorShapeDiff); override;
  23. function IsIdentity: boolean; override;
  24. end;
  25. { TCustomRectShape }
  26. TCustomRectShape = class(TVectorShape)
  27. private
  28. procedure SetXAxis(AValue: TPointF);
  29. procedure SetYAxis(AValue: TPointF);
  30. protected
  31. FOrigin, FXAxis, FYAxis: TPointF;
  32. FOriginBackup,FXUnitBackup,FYUnitBackup,
  33. FXAxisBackup,FYAxisBackup: TPointF;
  34. FXSizeBackup,FYSizeBackup: single;
  35. FMatrixBackup: TAffineMatrix;
  36. FFixedRatio: single;
  37. FDisableHitBox: boolean;
  38. procedure DoMoveXAxis(ANewCoord: TPointF; AShift: TShiftState; AFactor: single);
  39. procedure DoMoveYAxis(ANewCoord: TPointF; AShift: TShiftState; AFactor: single);
  40. procedure DoMoveXYCorner(ANewCoord: TPointF; AShift: TShiftState; AFactorX, AFactorY: single);
  41. procedure OnMoveOrigin({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
  42. procedure OnMoveXAxis({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  43. procedure OnMoveYAxis({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  44. procedure OnMoveXAxisNeg({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  45. procedure OnMoveYAxisNeg({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  46. procedure OnMoveXAxisAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  47. procedure OnMoveYAxisAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  48. procedure OnMoveXAxisNegAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  49. procedure OnMoveYAxisNegAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  50. procedure OnMoveXYCorner({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  51. procedure OnMoveXNegYCorner({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  52. procedure OnMoveXYNegCorner({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  53. procedure OnMoveXNegYNegCorner({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  54. procedure OnMoveXYCornerAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  55. procedure OnMoveXNegYCornerAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  56. procedure OnMoveXYNegCornerAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  57. procedure OnMoveXNegYNegCornerAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  58. procedure OnStartMove({%H-}ASender: TObject; {%H-}APointIndex: integer; {%H-}AShift: TShiftState);
  59. procedure UpdateFillFromRectDiff;
  60. function GetCornerPositition: single; virtual; abstract;
  61. function GetOrthoRect(AMatrix: TAffineMatrix; out ARect: TRectF): boolean;
  62. function ShowArrows: boolean; virtual;
  63. procedure SetOrigin(AValue: TPointF);
  64. function GetHeight: single;
  65. function GetWidth: single;
  66. procedure SetHeight(AValue: single);
  67. procedure SetWidth(AValue: single);
  68. procedure SetFixedRatio(AValue: single);
  69. procedure EnsureRatio(ACenterX,ACenterY: single);
  70. public
  71. procedure QuickDefine(constref APoint1,APoint2: TPointF); override;
  72. function SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox; override;
  73. procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
  74. procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
  75. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; {%H-}AOptions: TRenderBoundsOptions = []): TRectF; override;
  76. procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
  77. function GetAffineBox(const AMatrix: TAffineMatrix; APixelCentered: boolean): TAffineBox;
  78. procedure TransformFrame(const AMatrix: TAffineMatrix); override;
  79. procedure AlignTransform(const AMatrix: TAffineMatrix); override;
  80. property Origin: TPointF read FOrigin write SetOrigin;
  81. property XAxis: TPointF read FXAxis write SetXAxis;
  82. property YAxis: TPointF read FYAxis write SetYAxis;
  83. property Width: single read GetWidth write SetWidth;
  84. property Height: single read GetHeight write SetHeight;
  85. property FixedRatio: single read FFixedRatio write SetFixedRatio;
  86. end;
  87. { TRectShape }
  88. TRectShape = class(TCustomRectShape)
  89. protected
  90. function GetCornerPositition: single; override;
  91. public
  92. class function Fields: TVectorShapeFields; override;
  93. function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
  94. procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
  95. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
  96. function PointInShape(APoint: TPointF): boolean; overload; override;
  97. function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
  98. function PointInBack(APoint: TPointF): boolean; overload; override;
  99. function PointInPen(APoint: TPointF): boolean; overload; override;
  100. function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
  101. class function StorageClassName: RawByteString; override;
  102. end;
  103. { TEllipseShape }
  104. TEllipseShape = class(TCustomRectShape)
  105. protected
  106. function GetCornerPositition: single; override;
  107. public
  108. constructor Create(AContainer: TVectorOriginal); override;
  109. class function Fields: TVectorShapeFields; override;
  110. function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
  111. function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
  112. procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
  113. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
  114. function PointInShape(APoint: TPointF): boolean; overload; override;
  115. function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
  116. function PointInBack(APoint: TPointF): boolean; overload; override;
  117. function PointInPen(APoint: TPointF): boolean; overload; override;
  118. function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
  119. class function StorageClassName: RawByteString; override;
  120. end;
  121. TPhongShapeKind = (pskRectangle, pskRoundRectangle, pskHalfSphere, pskConeTop, pskConeSide,
  122. pskHorizCylinder, pskVertCylinder);
  123. const
  124. DefaultPhongShapeAltitudePercent = 20;
  125. DefaultPhongBorderSizePercent = 20;
  126. type
  127. TPhongShape = class;
  128. { TPhongShapeDiff }
  129. TPhongShapeDiff = class(TVectorShapeDiff)
  130. protected
  131. FStartShapeKind: TPhongShapeKind;
  132. FStartLightPosition: TPointF;
  133. FStartShapeAltitudePercent,FStartBorderSizePercent: single;
  134. FEndShapeKind: TPhongShapeKind;
  135. FEndLightPosition: TPointF;
  136. FEndShapeAltitudePercent,FEndBorderSizePercent: single;
  137. public
  138. constructor Create(AStartShape: TVectorShape); override;
  139. procedure ComputeDiff(AEndShape: TVectorShape); override;
  140. procedure Apply(AStartShape: TVectorShape); override;
  141. procedure Unapply(AEndShape: TVectorShape); override;
  142. procedure Append(ADiff: TVectorShapeDiff); override;
  143. function IsIdentity: boolean; override;
  144. end;
  145. { TPhongShape }
  146. TPhongShape = class(TCustomRectShape)
  147. private
  148. FShapeKind: TPhongShapeKind;
  149. FLightPosition: TPointF;
  150. FShapeAltitudePercent: single;
  151. FBorderSizePercent: single;
  152. procedure OnMoveLightPos({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF;
  153. {%H-}AShift: TShiftState);
  154. procedure SetBorderSizePercent(AValue: single);
  155. procedure SetLightPosition(AValue: TPointF);
  156. procedure SetShapeAltitudePercent(AValue: single);
  157. procedure SetShapeKind(AValue: TPhongShapeKind);
  158. function GetEnvelope: ArrayOfTPointF;
  159. public
  160. constructor Create(AContainer: TVectorOriginal); override;
  161. destructor Destroy; override;
  162. function GetCornerPositition: single; override;
  163. class function Fields: TVectorShapeFields; override;
  164. class function PreferPixelCentered: boolean; override;
  165. function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
  166. function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
  167. procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
  168. procedure MouseDown(RightButton: boolean; {%H-}ClickCount: integer; Shift: TShiftState; X, Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
  169. procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
  170. procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
  171. procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
  172. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
  173. function PointInShape(APoint: TPointF): boolean; overload; override;
  174. function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
  175. function PointInBack(APoint: TPointF): boolean; overload; override;
  176. function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
  177. function GetGenericCost: integer; override;
  178. procedure Transform(const AMatrix: TAffineMatrix); override;
  179. function AllowShearTransform: boolean; override;
  180. class function StorageClassName: RawByteString; override;
  181. property ShapeKind: TPhongShapeKind read FShapeKind write SetShapeKind;
  182. property LightPosition: TPointF read FLightPosition write SetLightPosition;
  183. property ShapeAltitudePercent: single read FShapeAltitudePercent write SetShapeAltitudePercent;
  184. property BorderSizePercent: single read FBorderSizePercent write SetBorderSizePercent;
  185. end;
  186. implementation
  187. uses BGRAPen, BGRAGraphics, BGRAFillInfo, BGRAPath, math, LCVectorialFill, LCResourceString;
  188. { TPhongShapeDiff }
  189. constructor TPhongShapeDiff.Create(AStartShape: TVectorShape);
  190. begin
  191. with (AStartShape as TPhongShape) do
  192. begin
  193. FStartShapeKind:= ShapeKind;
  194. FStartLightPosition:= LightPosition;
  195. FStartShapeAltitudePercent:= ShapeAltitudePercent;
  196. FStartBorderSizePercent:= BorderSizePercent;
  197. end;
  198. end;
  199. procedure TPhongShapeDiff.ComputeDiff(AEndShape: TVectorShape);
  200. begin
  201. with (AEndShape as TPhongShape) do
  202. begin
  203. FEndShapeKind:= ShapeKind;
  204. FEndLightPosition:= LightPosition;
  205. FEndShapeAltitudePercent:= ShapeAltitudePercent;
  206. FEndBorderSizePercent:= BorderSizePercent;
  207. end;
  208. end;
  209. procedure TPhongShapeDiff.Apply(AStartShape: TVectorShape);
  210. begin
  211. with (AStartShape as TPhongShape) do
  212. begin
  213. BeginUpdate;
  214. FShapeKind := FEndShapeKind;
  215. FLightPosition := FEndLightPosition;
  216. FShapeAltitudePercent := FEndShapeAltitudePercent;
  217. FBorderSizePercent := FEndBorderSizePercent;
  218. EndUpdate;
  219. end;
  220. end;
  221. procedure TPhongShapeDiff.Unapply(AEndShape: TVectorShape);
  222. begin
  223. with (AEndShape as TPhongShape) do
  224. begin
  225. BeginUpdate;
  226. FShapeKind := FStartShapeKind;
  227. FLightPosition := FStartLightPosition;
  228. FShapeAltitudePercent := FStartShapeAltitudePercent;
  229. FBorderSizePercent := FStartBorderSizePercent;
  230. EndUpdate;
  231. end;
  232. end;
  233. procedure TPhongShapeDiff.Append(ADiff: TVectorShapeDiff);
  234. var
  235. next: TPhongShapeDiff;
  236. begin
  237. next := ADiff as TPhongShapeDiff;
  238. FEndShapeKind := next.FEndShapeKind;
  239. FEndLightPosition := next.FEndLightPosition;
  240. FEndShapeAltitudePercent := next.FEndShapeAltitudePercent;
  241. FEndBorderSizePercent := next.FEndBorderSizePercent;
  242. end;
  243. function TPhongShapeDiff.IsIdentity: boolean;
  244. begin
  245. result := (FStartShapeKind = FEndShapeKind) and
  246. (FStartLightPosition = FEndLightPosition) and
  247. (FStartShapeAltitudePercent = FEndShapeAltitudePercent) and
  248. (FStartBorderSizePercent = FEndBorderSizePercent);
  249. end;
  250. { TCustomRectShapeDiff }
  251. constructor TCustomRectShapeDiff.Create(AStartShape: TVectorShape);
  252. begin
  253. with (AStartShape as TCustomRectShape) do
  254. begin
  255. FStartOrigin := Origin;
  256. FStartXAxis := XAxis;
  257. FStartYAxis := YAxis;
  258. FStartFixedRatio := FixedRatio;
  259. end;
  260. end;
  261. procedure TCustomRectShapeDiff.ComputeDiff(AEndShape: TVectorShape);
  262. begin
  263. with (AEndShape as TCustomRectShape) do
  264. begin
  265. FEndOrigin := Origin;
  266. FEndXAxis := XAxis;
  267. FEndYAxis := YAxis;
  268. FEndFixedRatio := FixedRatio;
  269. end;
  270. end;
  271. procedure TCustomRectShapeDiff.Apply(AStartShape: TVectorShape);
  272. begin
  273. with (AStartShape as TCustomRectShape) do
  274. begin
  275. BeginUpdate;
  276. FOrigin := FEndOrigin;
  277. FXAxis := FEndXAxis;
  278. FYAxis := FEndYAxis;
  279. FFixedRatio := FEndFixedRatio;
  280. EndUpdate;
  281. end;
  282. end;
  283. procedure TCustomRectShapeDiff.Unapply(AEndShape: TVectorShape);
  284. begin
  285. with (AEndShape as TCustomRectShape) do
  286. begin
  287. BeginUpdate;
  288. FOrigin := FStartOrigin;
  289. FXAxis := FStartXAxis;
  290. FYAxis := FStartYAxis;
  291. FFixedRatio := FStartFixedRatio;
  292. EndUpdate;
  293. end;
  294. end;
  295. procedure TCustomRectShapeDiff.Append(ADiff: TVectorShapeDiff);
  296. var
  297. next: TCustomRectShapeDiff;
  298. begin
  299. next := ADiff as TCustomRectShapeDiff;
  300. FEndOrigin := next.FEndOrigin;
  301. FEndXAxis := next.FEndXAxis;
  302. FEndYAxis := next.FEndYAxis;
  303. FEndFixedRatio := next.FEndFixedRatio;
  304. end;
  305. function TCustomRectShapeDiff.IsIdentity: boolean;
  306. begin
  307. result := (FStartOrigin = FEndOrigin) and
  308. (FStartXAxis = FEndXAxis) and
  309. (FStartYAxis = FEndYAxis) and
  310. (FStartFixedRatio = FEndFixedRatio);
  311. end;
  312. { TCustomRectShape }
  313. procedure TCustomRectShape.SetOrigin(AValue: TPointF);
  314. var
  315. delta: TPointF;
  316. t: TAffineMatrix;
  317. begin
  318. if FOrigin=AValue then Exit;
  319. BeginUpdate(TCustomRectShapeDiff);
  320. delta := AValue - FOrigin;
  321. t := AffineMatrixTranslation(delta.x, delta.y);
  322. FOrigin := AValue;
  323. FXAxis := t*FXAxis;
  324. FYAxis := t*FYAxis;
  325. TransformFill(t, False);
  326. EndUpdate;
  327. end;
  328. function TCustomRectShape.GetHeight: single;
  329. begin
  330. result := VectLen(YAxis-Origin);
  331. end;
  332. function TCustomRectShape.GetWidth: single;
  333. begin
  334. result := VectLen(XAxis-Origin);
  335. end;
  336. procedure TCustomRectShape.SetHeight(AValue: single);
  337. var u,v: TPointF;
  338. h,w: single;
  339. begin
  340. h := GetHeight;
  341. if h <> 0 then v := (YAxis-Origin)*(1/h)
  342. else
  343. begin
  344. w := GetWidth;
  345. if w <> 0 then
  346. begin
  347. u := (XAxis-Origin)*(1/w);
  348. v := PointF(-u.y,u.x);
  349. end else
  350. v := PointF(0,1/2);
  351. end;
  352. FYAxis := Origin + v*AValue;
  353. end;
  354. procedure TCustomRectShape.SetWidth(AValue: single);
  355. var u,v: TPointF;
  356. h,w: single;
  357. begin
  358. w := GetWidth;
  359. if w <> 0 then u := (XAxis-Origin)*(1/w)
  360. else
  361. begin
  362. h := GetHeight;
  363. if h <> 0 then
  364. begin
  365. v := (YAxis-Origin)*(1/h);
  366. u := PointF(v.y,-v.x);
  367. end else
  368. u := PointF(1/2,0);
  369. end;
  370. FXAxis := Origin + u*AValue;
  371. end;
  372. procedure TCustomRectShape.EnsureRatio(ACenterX,ACenterY: single);
  373. var
  374. h, w, curRatio,ratioFactor,fracPower: Single;
  375. refPoint, newRefPoint: TPointF;
  376. begin
  377. if (FFixedRatio<>EmptySingle) and (FFixedRatio<>0) then
  378. begin
  379. h := Height;
  380. w := Width;
  381. if h = 0 then
  382. Height := w/FFixedRatio
  383. else if w = 0 then
  384. Width := h*FFixedRatio
  385. else
  386. begin
  387. curRatio := Width/Height;
  388. if FFixedRatio <> curRatio then
  389. begin
  390. ratioFactor := FFixedRatio/curRatio;
  391. BeginUpdate(TCustomRectShapeDiff);
  392. refPoint := Origin + (XAxis-Origin)*ACenterX + (YAxis-Origin)*ACenterY;
  393. if (ACenterX=0) and (ACenterY=0) then fracPower := 1/2
  394. else fracPower := abs(ACenterY)/(abs(ACenterX)+abs(ACenterY));
  395. Width := Width*Power(ratioFactor, fracPower);
  396. if (ACenterX=0) and (ACenterY=0) then fracPower := 1/2
  397. else fracPower := abs(ACenterX)/(abs(ACenterX)+abs(ACenterY));
  398. Height := Height/Power(ratioFactor, fracPower);
  399. newRefPoint := Origin + (XAxis-Origin)*ACenterX + (YAxis-Origin)*ACenterY;
  400. Origin := Origin + (refPoint-newRefPoint);
  401. EndUpdate;
  402. end;
  403. end;
  404. end;
  405. end;
  406. procedure TCustomRectShape.SetFixedRatio(AValue: single);
  407. begin
  408. if FFixedRatio=AValue then Exit;
  409. FFixedRatio:=AValue;
  410. EnsureRatio(0,0);
  411. end;
  412. procedure TCustomRectShape.SetXAxis(AValue: TPointF);
  413. begin
  414. if FXAxis=AValue then Exit;
  415. BeginUpdate(TCustomRectShapeDiff);
  416. FXAxis:=AValue;
  417. EndUpdate;
  418. end;
  419. procedure TCustomRectShape.SetYAxis(AValue: TPointF);
  420. begin
  421. if FYAxis=AValue then Exit;
  422. BeginUpdate(TCustomRectShapeDiff);
  423. FYAxis:=AValue;
  424. EndUpdate;
  425. end;
  426. procedure TCustomRectShape.DoMoveXAxis(ANewCoord: TPointF; AShift: TShiftState; AFactor: single);
  427. var
  428. newSize: Single;
  429. u: TPointF;
  430. begin
  431. BeginUpdate(TCustomRectShapeDiff);
  432. if AllowShearTransform and ((ssAlt in AShift) or (FXUnitBackup = PointF(0,0))) then
  433. begin
  434. FXAxis := FOriginBackup + AFactor*(ANewCoord - FOriginBackup);
  435. FYAxis := FYAxisBackup;
  436. FOrigin := FOriginBackup;
  437. end else
  438. if FXUnitBackup = PointF(0,0) then
  439. begin
  440. u := ANewCoord - FOriginBackup;
  441. FXAxis := FOriginBackup + u;
  442. FYAxis := FOriginBackup + PointF(-u.y,u.x);
  443. FOrigin := FOriginBackup;
  444. end else
  445. begin
  446. newSize := AFactor*FXUnitBackup*(ANewCoord-FOriginBackup);
  447. if ssShift in AShift then
  448. begin
  449. FXAxis := FOriginBackup+FXUnitBackup*newSize;
  450. FYAxis := FYAxisBackup;
  451. FOrigin := FOriginBackup;
  452. end else
  453. begin
  454. FXAxis := FXAxisBackup + ((AFactor+1)*0.5)*(newSize-FXSizeBackup)*FXUnitBackup;
  455. FYAxis := FYAxisBackup + AFactor*(newSize-FXSizeBackup)*0.5*FXUnitBackup;
  456. FOrigin := FOriginBackup + AFactor*(newSize-FXSizeBackup)*0.5*FXUnitBackup;
  457. end;
  458. end;
  459. EnsureRatio(-AFactor,0);
  460. UpdateFillFromRectDiff;
  461. EndUpdate;
  462. end;
  463. procedure TCustomRectShape.DoMoveYAxis(ANewCoord: TPointF; AShift: TShiftState;
  464. AFactor: single);
  465. var
  466. newSizeY: Single;
  467. u: TPointF;
  468. begin
  469. BeginUpdate(TCustomRectShapeDiff);
  470. if AllowShearTransform and ((ssAlt in AShift) or (FYUnitBackup = PointF(0,0))) then
  471. begin
  472. FYAxis := FOriginBackup + AFactor*(ANewCoord - FOriginBackup);
  473. FXAxis := FXAxisBackup;
  474. FOrigin := FOriginBackup;
  475. end else
  476. if FYUnitBackup = PointF(0,0) then
  477. begin
  478. u := ANewCoord - FOriginBackup;
  479. FXAxis := FOriginBackup + PointF(u.y,-u.x);
  480. FYAxis := FOriginBackup + u;
  481. FOrigin := FOriginBackup;
  482. end else
  483. begin
  484. newSizeY := AFactor*FYUnitBackup*(ANewCoord-FOriginBackup);
  485. if ssShift in AShift then
  486. begin
  487. FYAxis := FOriginBackup+FYUnitBackup*newSizeY;
  488. FXAxis := FXAxisBackup;
  489. FOrigin := FOriginBackup;
  490. end else
  491. begin
  492. FYAxis := FYAxisBackup + ((AFactor+1)*0.5)*(newSizeY-FYSizeBackup)*FYUnitBackup;
  493. FXAxis := FXAxisBackup + AFactor*(newSizeY-FYSizeBackup)*0.5*FYUnitBackup;
  494. FOrigin := FOriginBackup + AFactor*(newSizeY-FYSizeBackup)*0.5*FYUnitBackup;
  495. end;
  496. end;
  497. EnsureRatio(0,-AFactor);
  498. UpdateFillFromRectDiff;
  499. EndUpdate;
  500. end;
  501. procedure TCustomRectShape.DoMoveXYCorner(ANewCoord: TPointF;
  502. AShift: TShiftState; AFactorX, AFactorY: single);
  503. var
  504. ratio, d: single;
  505. m: TAffineMatrix;
  506. newSize, prevCornerVect, newCornerVect: TPointF;
  507. angle,deltaAngle, zoom: single;
  508. begin
  509. BeginUpdate(TCustomRectShapeDiff);
  510. if (ssAlt in AShift) and (VectDet(FXUnitBackup,FYUnitBackup)<>0) and (FXSizeBackup<>0) and (FYSizeBackup<>0) then
  511. begin
  512. prevCornerVect := AFactorX*(FXAxisBackup - FOriginBackup) + AFactorY*(FYAxisBackup - FOriginBackup);
  513. newCornerVect := (ANewCoord - FOriginBackup)*(1/GetCornerPositition);
  514. m := AffineMatrixScaledRotation(prevCornerVect, newCornerVect);
  515. if not (ssShift in AShift) then
  516. begin
  517. angle := arctan2(-m[2,1],m[1,1])*2/Pi;
  518. deltaAngle := 0;
  519. if abs(frac(angle)) < 0.1 then deltaAngle := -frac(angle)
  520. else if frac(angle) > 0.9 then deltaAngle := +1-frac(angle)
  521. else if frac(angle) < -0.9 then deltaAngle := -1-frac(angle)
  522. else if abs(frac(angle)-0.5) < 0.1 then deltaAngle := 0.5-frac(angle)
  523. else if abs(frac(angle)+0.5) < 0.1 then deltaAngle := -0.5-frac(angle);
  524. if deltaAngle <> 0 then
  525. begin
  526. angle := (angle+deltaAngle)*Pi/2;
  527. zoom := VectLen(m[1,1],m[2,1]);
  528. m := AffineMatrixRotationRad(angle)*AffineMatrixScale(zoom,zoom);
  529. end;
  530. end;
  531. m := AffineMatrixTranslation(FOriginBackup.x,FOriginBackup.y)*m
  532. *AffineMatrixTranslation(-FOriginBackup.x,-FOriginBackup.y);
  533. FOrigin := FOriginBackup;
  534. FXAxis := m * FXAxisBackup;
  535. FYAxis := m * FYAxisBackup;
  536. end else
  537. begin
  538. d := GetCornerPositition;
  539. m := AffineMatrix(AFactorX*FXUnitBackup*d,AFactorY*FYUnitBackup*d,FOriginBackup);
  540. if IsAffineMatrixInversible(m) then
  541. begin
  542. m := AffineMatrixInverse(m);
  543. newSize := m*ANewCoord;
  544. if (ssShift in AShift) and (FXSizeBackup <> 0) and (FYSizeBackup <> 0) then
  545. begin
  546. ratio := (newSize.X/FXSizeBackup + newSize.Y/FYSizeBackup)/2;
  547. newSize.X := ratio*FXSizeBackup;
  548. newSize.Y := ratio*FYSizeBackup;
  549. end;
  550. FXAxis := FXAxisBackup + (AFactorX+1)*0.5*sqrt(d)*(newSize.X-FXSizeBackup)*FXUnitBackup + AFactorY*(newSize.Y-FYSizeBackup)*0.5*sqrt(d)*FYUnitBackup;
  551. FYAxis := FYAxisBackup + (AFactorY+1)*0.5*sqrt(d)*(newSize.Y-FYSizeBackup)*FYUnitBackup + AFactorX*(newSize.X-FXSizeBackup)*0.5*sqrt(d)*FXUnitBackup;
  552. FOrigin := FOriginBackup + AFactorX*(newSize.X-FXSizeBackup)*0.5*sqrt(d)*FXUnitBackup
  553. + AFactorY*(newSize.Y-FYSizeBackup)*0.5*sqrt(d)*FYUnitBackup;
  554. end;
  555. end;
  556. EnsureRatio(-AFactorX,-AFactorY);
  557. UpdateFillFromRectDiff;
  558. EndUpdate;
  559. end;
  560. procedure TCustomRectShape.OnMoveOrigin(ASender: TObject; APrevCoord,
  561. ANewCoord: TPointF; AShift: TShiftState);
  562. begin
  563. Origin := ANewCoord;
  564. end;
  565. procedure TCustomRectShape.OnMoveXAxis(ASender: TObject; APrevCoord,
  566. ANewCoord: TPointF; AShift: TShiftState);
  567. begin
  568. DoMoveXAxis(ANewCoord, AShift, 1);
  569. end;
  570. procedure TCustomRectShape.OnMoveYAxis(ASender: TObject; APrevCoord,
  571. ANewCoord: TPointF; AShift: TShiftState);
  572. begin
  573. DoMoveYAxis(ANewCoord, AShift, 1);
  574. end;
  575. procedure TCustomRectShape.OnMoveXAxisNeg(ASender: TObject; APrevCoord,
  576. ANewCoord: TPointF; AShift: TShiftState);
  577. begin
  578. DoMoveXAxis(ANewCoord, AShift, -1);
  579. end;
  580. procedure TCustomRectShape.OnMoveYAxisNeg(ASender: TObject; APrevCoord,
  581. ANewCoord: TPointF; AShift: TShiftState);
  582. begin
  583. DoMoveYAxis(ANewCoord, AShift, -1);
  584. end;
  585. procedure TCustomRectShape.OnMoveXAxisAlt(ASender: TObject; APrevCoord,
  586. ANewCoord: TPointF; AShift: TShiftState);
  587. begin
  588. DoMoveXAxis(ANewCoord, AShift+[ssAlt], 1);
  589. end;
  590. procedure TCustomRectShape.OnMoveYAxisAlt(ASender: TObject; APrevCoord,
  591. ANewCoord: TPointF; AShift: TShiftState);
  592. begin
  593. DoMoveYAxis(ANewCoord, AShift+[ssAlt], 1);
  594. end;
  595. procedure TCustomRectShape.OnMoveXAxisNegAlt(ASender: TObject; APrevCoord,
  596. ANewCoord: TPointF; AShift: TShiftState);
  597. begin
  598. DoMoveXAxis(ANewCoord, AShift+[ssAlt], -1);
  599. end;
  600. procedure TCustomRectShape.OnMoveYAxisNegAlt(ASender: TObject; APrevCoord,
  601. ANewCoord: TPointF; AShift: TShiftState);
  602. begin
  603. DoMoveYAxis(ANewCoord, AShift+[ssAlt], -1);
  604. end;
  605. procedure TCustomRectShape.OnMoveXYCorner(ASender: TObject; APrevCoord,
  606. ANewCoord: TPointF; AShift: TShiftState);
  607. begin
  608. DoMoveXYCorner(ANewCoord, AShift, 1, 1);
  609. end;
  610. procedure TCustomRectShape.OnMoveXNegYCorner(ASender: TObject; APrevCoord,
  611. ANewCoord: TPointF; AShift: TShiftState);
  612. begin
  613. DoMoveXYCorner(ANewCoord, AShift, -1, 1);
  614. end;
  615. procedure TCustomRectShape.OnMoveXYNegCorner(ASender: TObject; APrevCoord,
  616. ANewCoord: TPointF; AShift: TShiftState);
  617. begin
  618. DoMoveXYCorner(ANewCoord, AShift, 1, -1);
  619. end;
  620. procedure TCustomRectShape.OnMoveXNegYNegCorner(ASender: TObject; APrevCoord,
  621. ANewCoord: TPointF; AShift: TShiftState);
  622. begin
  623. DoMoveXYCorner(ANewCoord, AShift, -1, -1);
  624. end;
  625. procedure TCustomRectShape.OnMoveXYCornerAlt(ASender: TObject; APrevCoord,
  626. ANewCoord: TPointF; AShift: TShiftState);
  627. begin
  628. DoMoveXYCorner(ANewCoord, AShift+[ssAlt], 1, 1);
  629. end;
  630. procedure TCustomRectShape.OnMoveXNegYCornerAlt(ASender: TObject; APrevCoord,
  631. ANewCoord: TPointF; AShift: TShiftState);
  632. begin
  633. DoMoveXYCorner(ANewCoord, AShift+[ssAlt], -1, 1);
  634. end;
  635. procedure TCustomRectShape.OnMoveXYNegCornerAlt(ASender: TObject; APrevCoord,
  636. ANewCoord: TPointF; AShift: TShiftState);
  637. begin
  638. DoMoveXYCorner(ANewCoord, AShift+[ssAlt], 1, -1);
  639. end;
  640. procedure TCustomRectShape.OnMoveXNegYNegCornerAlt(ASender: TObject;
  641. APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
  642. begin
  643. DoMoveXYCorner(ANewCoord, AShift+[ssAlt], -1, -1);
  644. end;
  645. procedure TCustomRectShape.OnStartMove(ASender: TObject; APointIndex: integer;
  646. AShift: TShiftState);
  647. begin
  648. FOriginBackup := FOrigin;
  649. FXAxisBackup := FXAxis;
  650. FXUnitBackup := FXAxis-FOrigin;
  651. FXSizeBackup := VectLen(FXUnitBackup);
  652. if FXSizeBackup <> 0 then FXUnitBackup := (1/FXSizeBackup)*FXUnitBackup;
  653. FYAxisBackup := FYAxis;
  654. FYUnitBackup := FYAxis-FOrigin;
  655. FYSizeBackup := VectLen(FYUnitBackup);
  656. if FYSizeBackup <> 0 then FYUnitBackup := (1/FYSizeBackup)*FYUnitBackup;
  657. FMatrixBackup := AffineMatrix(FXAxis-FOrigin, FYAxis-FOrigin, FOrigin);
  658. end;
  659. procedure TCustomRectShape.UpdateFillFromRectDiff;
  660. var
  661. newMatrix, matrixDiff: TAffineMatrix;
  662. begin
  663. newMatrix := AffineMatrix(FXAxis-FOrigin, FYAxis-FOrigin, FOrigin);
  664. if IsAffineMatrixInversible(newMatrix) and IsAffineMatrixInversible(FMatrixBackup) then
  665. begin
  666. matrixDiff := newMatrix*AffineMatrixInverse(FMatrixBackup);
  667. TransformFill(matrixDiff, True);
  668. FMatrixBackup := newMatrix;
  669. end;
  670. end;
  671. function TCustomRectShape.GetAffineBox(const AMatrix: TAffineMatrix; APixelCentered: boolean): TAffineBox;
  672. var
  673. m: TAffineMatrix;
  674. begin
  675. if not APixelCentered then
  676. m := AffineMatrixTranslation(0.5,0.5) * MatrixForPixelCentered(AMatrix)
  677. else
  678. m := MatrixForPixelCentered(AMatrix);
  679. result := m * TAffineBox.AffineBox(FOrigin - (FXAxis - FOrigin) - (FYAxis - FOrigin),
  680. FXAxis - (FYAxis - FOrigin), FYAxis - (FXAxis - FOrigin));
  681. end;
  682. procedure TCustomRectShape.TransformFrame(const AMatrix: TAffineMatrix);
  683. var
  684. m: TAffineMatrix;
  685. begin
  686. BeginUpdate(TCustomRectShapeDiff);
  687. m := MatrixForPixelCentered(AMatrix);
  688. FOrigin := m*FOrigin;
  689. FXAxis := m*FXAxis;
  690. FYAxis := m*FYAxis;
  691. EndUpdate;
  692. end;
  693. procedure TCustomRectShape.AlignTransform(const AMatrix: TAffineMatrix);
  694. begin
  695. Origin := AMatrix*Origin;
  696. end;
  697. function TCustomRectShape.GetOrthoRect(AMatrix: TAffineMatrix; out ARect: TRectF): boolean;
  698. var
  699. sx,sy: single;
  700. o,ox,oy: TPointF;
  701. m: TAffineMatrix;
  702. begin
  703. m := MatrixForPixelCentered(AMatrix);
  704. o := m*FOrigin;
  705. ox := m*FXAxis;
  706. oy := m*FYAxis;
  707. if (abs(ox.y-o.y)<1e-4) and (abs(oy.x-o.x)<1e-4) then
  708. begin
  709. sx := abs(ox.x-o.x);
  710. sy := abs(oy.y-o.y);
  711. ARect := RectF(o.x - sx, o.y - sy, o.x + sx, o.y + sy);
  712. exit(true);
  713. end else
  714. begin
  715. ARect := EmptyRectF;
  716. exit(false);
  717. end;
  718. end;
  719. function TCustomRectShape.ShowArrows: boolean;
  720. begin
  721. result := true;
  722. end;
  723. procedure TCustomRectShape.QuickDefine(constref APoint1, APoint2: TPointF);
  724. begin
  725. BeginUpdate(TCustomRectShapeDiff);
  726. FOrigin := (APoint1+APoint2)*0.5;
  727. FXAxis := PointF(APoint2.X,FOrigin.Y);
  728. FYAxis := PointF(FOrigin.X,APoint2.Y);
  729. EnsureRatio(-1,-1);
  730. EndUpdate;
  731. end;
  732. function TCustomRectShape.SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox;
  733. begin
  734. Result:= GetAffineBox(AMatrix,False);
  735. end;
  736. procedure TCustomRectShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  737. begin
  738. BeginUpdate;
  739. inherited LoadFromStorage(AStorage);
  740. FOrigin := AStorage.PointF['origin'];
  741. FXAxis := AStorage.PointF['x-axis'];
  742. FYAxis := AStorage.PointF['y-axis'];
  743. FFixedRatio := AStorage.Float['fixed-ratio'];
  744. EndUpdate;
  745. end;
  746. procedure TCustomRectShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  747. begin
  748. inherited SaveToStorage(AStorage);
  749. AStorage.PointF['origin'] := FOrigin;
  750. AStorage.PointF['x-axis'] := FXAxis;
  751. AStorage.PointF['y-axis'] := FYAxis;
  752. AStorage.Float['fixed-ratio'] := FFixedRatio;
  753. end;
  754. function TCustomRectShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
  755. begin
  756. result := GetAffineBox(AMatrix, false).RectBoundsF;
  757. end;
  758. procedure TCustomRectShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
  759. var
  760. d: Single;
  761. u, v: TPointF;
  762. idx,idxOrig, idxX,idxY,idxXNeg,idxYNeg: Integer;
  763. begin
  764. u := FXAxis - FOrigin;
  765. v := FYAxis - FOrigin;
  766. AEditor.AddStartMoveHandler(@OnStartMove);
  767. d := GetCornerPositition;
  768. if d <> 0 then
  769. begin
  770. idx := AEditor.AddPoint(FOrigin + (u+v)*d, @OnMoveXYCorner, false);
  771. AEditor.AddPointAlternateMove(idx, @OnMoveXYCornerAlt);
  772. idx := AEditor.AddPoint(FOrigin + (-u+v)*d, @OnMoveXNegYCorner, false);
  773. AEditor.AddPointAlternateMove(idx, @OnMoveXNegYCornerAlt);
  774. idx := AEditor.AddPoint(FOrigin + (u-v)*d, @OnMoveXYNegCorner, false);
  775. AEditor.AddPointAlternateMove(idx, @OnMoveXYNegCornerAlt);
  776. idx := AEditor.AddPoint(FOrigin + (-u-v)*d, @OnMoveXNegYNegCorner, false);
  777. AEditor.AddPointAlternateMove(idx, @OnMoveXNegYNegCornerAlt);
  778. end;
  779. if ShowArrows then
  780. begin
  781. idxX := AEditor.AddArrow(FOrigin, FXAxis, @OnMoveXAxis);
  782. idxY := AEditor.AddArrow(FOrigin, FYAxis, @OnMoveYAxis);
  783. idxXNeg := AEditor.AddArrow(FOrigin, FOrigin - u, @OnMoveXAxisNeg);
  784. idxYNeg := AEditor.AddArrow(FOrigin, FOrigin - v, @OnMoveYAxisNeg);
  785. end else
  786. begin
  787. idxX := AEditor.AddPoint(FXAxis, @OnMoveXAxis);
  788. idxY := AEditor.AddPoint(FYAxis, @OnMoveYAxis);
  789. idxXNeg := AEditor.AddPoint(FOrigin - u, @OnMoveXAxisNeg);
  790. idxYNeg := AEditor.AddPoint(FOrigin - v, @OnMoveYAxisNeg);
  791. end;
  792. AEditor.AddPointAlternateMove(idxX, @OnMoveXAxisAlt);
  793. AEditor.AddPointAlternateMove(idxY, @OnMoveYAxisAlt);
  794. AEditor.AddPointAlternateMove(idxXNeg, @OnMoveXAxisNegAlt);
  795. AEditor.AddPointAlternateMove(idxYNeg, @OnMoveYAxisNegAlt);
  796. idxOrig := AEditor.AddPoint(FOrigin, @OnMoveOrigin, true);
  797. if ShowArrows and not FDisableHitBox then
  798. begin
  799. AEditor.SetHitBox(idxX, TAffineBox.AffineBox(Origin + (XAxis-Origin)*0.667 - (YAxis-Origin)*0.667,
  800. Origin + (XAxis-Origin) - (YAxis-Origin)*0.667,
  801. Origin + (XAxis-Origin)*0.667 + (YAxis-Origin)*0.667) );
  802. AEditor.SetHitBox(idxY, TAffineBox.AffineBox(Origin - (XAxis-Origin)*0.667 + (YAxis-Origin)*0.667,
  803. Origin + (XAxis-Origin)*0.667 + (YAxis-Origin)*0.667,
  804. Origin - (XAxis-Origin)*0.667 + (YAxis-Origin)) );
  805. AEditor.SetHitBox(idxXNeg, TAffineBox.AffineBox(Origin - (XAxis-Origin) - (YAxis-Origin)*0.667,
  806. Origin - (XAxis-Origin)*0.667 - (YAxis-Origin)*0.667,
  807. Origin - (XAxis-Origin) + (YAxis-Origin)*0.667) );
  808. AEditor.SetHitBox(idxYNeg, TAffineBox.AffineBox(Origin - (XAxis-Origin)*0.667 - (YAxis-Origin),
  809. Origin + (XAxis-Origin)*0.667 - (YAxis-Origin),
  810. Origin - (XAxis-Origin)*0.667 - (YAxis-Origin)*0.667) );
  811. AEditor.SetHitBox(idxOrig, TAffineBox.AffineBox(Origin - (XAxis-Origin)*0.667 - (YAxis-Origin)*0.667,
  812. Origin + (XAxis-Origin)*0.667 - (YAxis-Origin)*0.667,
  813. Origin - (XAxis-Origin)*0.667 + (YAxis-Origin)*0.667));
  814. end;
  815. end;
  816. { TRectShape }
  817. function TRectShape.GetCornerPositition: single;
  818. begin
  819. result := 1;
  820. end;
  821. function TRectShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  822. var
  823. ab: TAffineBox;
  824. backSurface, totalSurface, penSurface: Single;
  825. begin
  826. if not GetPenVisible and not GetBackVisible then
  827. result := false
  828. else
  829. begin
  830. ab := GetAffineBox(AMatrix, true);
  831. backSurface := ab.Surface;
  832. if GetPenVisible then
  833. begin
  834. penSurface := (ab.Width+ab.Height)*2*PenWidth;
  835. if GetBackVisible then
  836. totalSurface:= backSurface+penSurface/2
  837. else
  838. totalSurface := penSurface;
  839. end else
  840. totalSurface := backSurface;
  841. result := (totalSurface > 800*600) or
  842. ((backSurface > 320*240) and GetBackVisible and BackFill.IsSlow(AMatrix)) or
  843. ((penSurface > 320*240) and GetPenVisible and PenFill.IsSlow(AMatrix));
  844. end;
  845. end;
  846. class function TRectShape.Fields: TVectorShapeFields;
  847. begin
  848. Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfJoinStyle, vsfBackFill];
  849. end;
  850. function TRectShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  851. var
  852. topLeft, u, v: TPointF;
  853. w, h: Single;
  854. m: TAffineMatrix;
  855. function ApproxPointEqual(const APoint1, APoint2: TPointF): boolean;
  856. var
  857. precision: Single;
  858. begin
  859. precision := (VectLen(APoint1) + VectLen(APoint2))*1e-6;
  860. result := VectLen(APoint2-APoint1) <= precision;
  861. end;
  862. begin
  863. topLeft := Origin - (XAxis - Origin) - (YAxis - Origin);
  864. w := Width*2; h := Height*2;
  865. if (XAxis.y <> 0) or (YAxis.x <> 0) then
  866. begin
  867. u := XAxis - Origin;
  868. if w > 0 then u *= (2/w);
  869. v := YAxis - Origin;
  870. if h > 0 then v *= (2/h);
  871. m := AffineMatrixTranslation(topLeft.X, topLeft.Y) *
  872. AffineMatrix(u, v, PointF(0, 0)) *
  873. AffineMatrixTranslation(-topLeft.X, -topLeft.Y);
  874. end else
  875. m := AffineMatrixIdentity;
  876. if not PenVisible and (BackFill.FillType = vftTexture) and
  877. (BackFill.TextureRepetition = trNone) and Assigned(BackFill.Texture) and
  878. ApproxPointEqual(Origin + PointF(0.5, 0.5), BackFill.TextureMatrix * PointF(BackFill.Texture.Width/2, BackFill.Texture.Height/2)) and
  879. ApproxPointEqual(XAxis + PointF(0.5, 0.5), BackFill.TextureMatrix * PointF(BackFill.Texture.Width, BackFill.Texture.Height/2)) and
  880. ApproxPointEqual(YAxis + PointF(0.5, 0.5), BackFill.TextureMatrix * PointF(BackFill.Texture.Width/2, BackFill.Texture.Height)) then
  881. begin
  882. result := AContent.AppendImage(topLeft, PointF(w,h), BackFill.Texture, false);
  883. result.opacity:= BackFill.TextureOpacity/255;
  884. result.Matrix[cuPixel] := m;
  885. end else
  886. begin
  887. result := AContent.AppendRect(topLeft, PointF(w, h));
  888. result.Matrix[cuPixel] := m;
  889. ApplyStrokeStyleToSVG(result, ADefs);
  890. ApplyFillStyleToSVG(result, ADefs);
  891. end;
  892. end;
  893. procedure TRectShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  894. ADraft: boolean);
  895. const GradientDithering = false;
  896. var
  897. pts: Array of TPointF;
  898. orthoRect: TRectF;
  899. r: TRect;
  900. backScan, penScan: TBGRACustomScanner;
  901. temp: TBGRABitmap;
  902. i: Integer;
  903. begin
  904. pts := GetAffineBox(AMatrix, true).AsPolygon;
  905. If GetBackVisible and (Width <> 0) and (Height <> 0) then
  906. begin
  907. if (BackFill.FillType = vftSolid) then backScan := nil
  908. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  909. if GetOrthoRect(AMatrix, orthoRect) then
  910. begin
  911. if ADraft then
  912. begin
  913. r:= rect(round(orthoRect.Left+0.5),round(orthoRect.Top+0.5),round(orthoRect.Right+0.5),round(orthoRect.Bottom+0.5));
  914. if Assigned(backScan) then
  915. ADest.FillRect(r, backScan, dmDrawWithTransparency) else
  916. ADest.FillRect(r, BackFill.SolidColor, dmDrawWithTransparency)
  917. end
  918. else
  919. begin
  920. if Assigned(backScan) then
  921. begin
  922. if (BackFill.FillType = vftGradient) and GradientDithering then
  923. begin
  924. with orthoRect do
  925. r := rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
  926. temp := TBGRABitmap.Create(0,0);
  927. temp.SetSize(r.Width,r.Height);
  928. temp.FillRect(0,0,r.Width,r.Height,backScan,dmSet,Point(r.Left,r.Top),daFloydSteinberg);
  929. temp.ScanOffset := Point(-r.Left,-r.Top);
  930. ADest.FillRectAntialias(orthoRect, temp);
  931. temp.Free;
  932. end else
  933. ADest.FillRectAntialias(orthoRect, backScan);
  934. end else
  935. ADest.FillRectAntialias(orthoRect, BackFill.SolidColor);
  936. end;
  937. end else
  938. begin
  939. if ADraft then
  940. begin
  941. if Assigned(backScan) then
  942. ADest.FillPoly(pts, backScan, dmDrawWithTransparency) else
  943. ADest.FillPoly(pts, BackFill.SolidColor, dmDrawWithTransparency)
  944. end
  945. else
  946. begin
  947. if Assigned(backScan) then
  948. begin
  949. if BackFill.FillType = vftGradient then
  950. begin
  951. r := rect(floor(pts[0].x),floor(pts[0].y),ceil(pts[0].x),ceil(pts[0].y));
  952. for i := 1 to high(pts) do
  953. r.Union(rect(floor(pts[i].x),floor(pts[i].y),ceil(pts[i].x),ceil(pts[i].y)));
  954. temp := TBGRABitmap.Create(0,0);
  955. temp.SetSize(r.Width,r.Height);
  956. temp.FillRect(0,0,r.Width,r.Height,backScan,dmSet,Point(r.Left,r.Top),daFloydSteinberg);
  957. temp.ScanOffset := Point(-r.Left,-r.Top);
  958. ADest.FillPolyAntialias(pts, temp);
  959. temp.Free;
  960. end else
  961. ADest.FillPolyAntialias(pts, backScan);
  962. end else
  963. ADest.FillPolyAntialias(pts, BackFill.SolidColor);
  964. end;
  965. end;
  966. backScan.Free;
  967. end;
  968. if GetPenVisible then
  969. begin
  970. if (PenFill.FillType = vftSolid) then penScan := nil
  971. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  972. pts := ComputeStroke(pts,true, AMatrix);
  973. if ADraft and (PenWidth > 4) then
  974. begin
  975. if Assigned(penScan) then
  976. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  977. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency)
  978. end
  979. else
  980. begin
  981. if Assigned(penScan) then
  982. ADest.FillPolyAntialias(pts, penScan) else
  983. ADest.FillPolyAntialias(pts, PenColor);
  984. end;
  985. penScan.Free;
  986. end;
  987. end;
  988. function TRectShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
  989. var
  990. i: Integer;
  991. pts: ArrayOfTPointF;
  992. xMargin, yMargin: single;
  993. begin
  994. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
  995. result:= EmptyRectF
  996. else
  997. begin
  998. result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
  999. if GetPenVisible(rboAssumePenFill in AOptions) then
  1000. begin
  1001. if (JoinStyle <> pjsMiter) or (Stroker.MiterLimit <= 1) then
  1002. begin
  1003. xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
  1004. yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
  1005. result.Left -= xMargin;
  1006. result.Top -= yMargin;
  1007. result.Right += xMargin;
  1008. result.Bottom += yMargin;
  1009. end else
  1010. begin
  1011. pts := ComputeStroke(GetAffineBox(AMatrix, false).AsPolygon, true, AMatrix);
  1012. for i := 0 to high(pts) do
  1013. if not IsEmptyPointF(pts[i]) then
  1014. begin
  1015. if pts[i].x < result.Left then result.Left := pts[i].x;
  1016. if pts[i].x > result.Right then result.Right := pts[i].x;
  1017. if pts[i].y < result.Top then result.Top := pts[i].y;
  1018. if pts[i].y > result.Bottom then result.Bottom := pts[i].y;
  1019. end;
  1020. end;
  1021. end;
  1022. end;
  1023. end;
  1024. function TRectShape.PointInShape(APoint: TPointF): boolean;
  1025. var
  1026. pts: ArrayOfTPointF;
  1027. box: TAffineBox;
  1028. begin
  1029. box := GetAffineBox(AffineMatrixIdentity, true);
  1030. if GetBackVisible and box.Contains(APoint) then
  1031. result := true else
  1032. if GetPenVisible then
  1033. begin
  1034. pts := ComputeStroke(box.AsPolygon, true, AffineMatrixIdentity);
  1035. result:= IsPointInPolygon(pts, APoint, true);
  1036. end else
  1037. result := false;
  1038. end;
  1039. function TRectShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1040. var
  1041. pts: ArrayOfTPointF;
  1042. box: TAffineBox;
  1043. begin
  1044. if GetPenVisible or GetBackVisible then
  1045. begin
  1046. box := GetAffineBox(AffineMatrixIdentity, true);
  1047. pts := ComputeStrokeEnvelope(box.AsPolygon, true, ARadius*2);
  1048. result:= IsPointInPolygon(pts, APoint, true);
  1049. end
  1050. else result := false;
  1051. end;
  1052. function TRectShape.PointInBack(APoint: TPointF): boolean;
  1053. var
  1054. box: TAffineBox;
  1055. scan: TBGRACustomScanner;
  1056. begin
  1057. if GetBackVisible then
  1058. begin
  1059. box := GetAffineBox(AffineMatrixIdentity, true);
  1060. result := box.Contains(APoint);
  1061. if result and (BackFill.FillType = vftTexture) then
  1062. begin
  1063. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1064. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1065. scan.Free;
  1066. end;
  1067. end else
  1068. result := false;
  1069. end;
  1070. function TRectShape.PointInPen(APoint: TPointF): boolean;
  1071. var
  1072. pts: ArrayOfTPointF;
  1073. begin
  1074. if GetPenVisible then
  1075. begin
  1076. pts := GetAffineBox(AffineMatrixIdentity, true).AsPolygon;
  1077. pts := ComputeStroke(pts,true, AffineMatrixIdentity);
  1078. result:= IsPointInPolygon(pts, APoint, true);
  1079. end else
  1080. result := false;
  1081. end;
  1082. class function TRectShape.StorageClassName: RawByteString;
  1083. begin
  1084. result := 'rect';
  1085. end;
  1086. { TEllipseShape }
  1087. function TEllipseShape.GetCornerPositition: single;
  1088. begin
  1089. result := sqrt(2)/2;
  1090. end;
  1091. constructor TEllipseShape.Create(AContainer: TVectorOriginal);
  1092. begin
  1093. inherited Create(AContainer);
  1094. inherited SetJoinStyle(pjsRound);
  1095. end;
  1096. class function TEllipseShape.Fields: TVectorShapeFields;
  1097. begin
  1098. Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfBackFill];
  1099. end;
  1100. function TEllipseShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  1101. var
  1102. u, v: TPointF;
  1103. rx, ry: Single;
  1104. begin
  1105. rx := Width; ry := Height;
  1106. if rx <> ry then
  1107. result := AContent.AppendEllipse(Origin, PointF(rx, ry))
  1108. else result := AContent.AppendCircle(Origin, rx);
  1109. if (XAxis.y <> 0) or (YAxis.x <> 0) then
  1110. begin
  1111. u := XAxis - Origin;
  1112. if rx > 0 then u *= (1/rx);
  1113. v := YAxis - Origin;
  1114. if ry > 0 then v *= (1/ry);
  1115. result.matrix[cuPixel] := AffineMatrixTranslation(Origin.X, Origin.Y) *
  1116. AffineMatrix(u, v, PointF(0, 0)) *
  1117. AffineMatrixTranslation(-Origin.X, -Origin.Y);
  1118. end;
  1119. ApplyStrokeStyleToSVG(result, ADefs);
  1120. ApplyFillStyleToSVG(result, ADefs);
  1121. end;
  1122. function TEllipseShape.GetAlignBounds(const ALayoutRect: TRect;
  1123. const AMatrix: TAffineMatrix): TRectF;
  1124. var
  1125. m: TAffineMatrix;
  1126. pts: ArrayOfTPointF;
  1127. i: Integer;
  1128. zoom: Single;
  1129. procedure IncludePoint(const APoint: TPointF);
  1130. begin
  1131. if APoint.x < result.Left then result.Left := APoint.x else
  1132. if APoint.x > result.Right then result.Right := APoint.x;
  1133. if APoint.y < result.Top then result.Top := APoint.y else
  1134. if APoint.y > result.Bottom then result.Bottom := APoint.y;
  1135. end;
  1136. begin
  1137. m:= AffineMatrixTranslation(0.5,0.5)*MatrixForPixelCentered(AMatrix);
  1138. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1139. if pts = nil then exit(EmptyRectF);
  1140. result.TopLeft := pts[0];
  1141. result.BottomRight := pts[0];
  1142. for i := 0 to high(pts) do IncludePoint(pts[i]);
  1143. IncludePoint(m*XAxis);
  1144. IncludePoint(m*YAxis);
  1145. IncludePoint(m*(Origin-(XAxis-Origin)));
  1146. IncludePoint(m*(Origin-(YAxis-Origin)));
  1147. if GetPenVisible then
  1148. begin
  1149. zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
  1150. result.Left -= zoom*PenWidth/2;
  1151. result.Right += zoom*PenWidth/2;
  1152. result.Top -= zoom*PenWidth/2;
  1153. result.Bottom += zoom*PenWidth/2;
  1154. end;
  1155. end;
  1156. procedure TEllipseShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  1157. ADraft: boolean);
  1158. var
  1159. pts: Array of TPointF;
  1160. orthoRect: TRectF;
  1161. center, radius: TPointF;
  1162. draftPen, isOrtho: Boolean;
  1163. r: TRect;
  1164. backScan, penScan: TBGRACustomScanner;
  1165. penZoom: Single;
  1166. m: TAffineMatrix;
  1167. begin
  1168. isOrtho := GetOrthoRect(AMatrix, orthoRect);
  1169. if isOrtho then
  1170. begin
  1171. center := (orthoRect.TopLeft+orthoRect.BottomRight)*0.5;
  1172. radius := (orthoRect.BottomRight-orthoRect.TopLeft)*0.5;
  1173. If GetBackVisible then
  1174. begin
  1175. if BackFill.FillType = vftSolid then backScan := nil
  1176. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  1177. if ADraft then
  1178. begin
  1179. r := rect(round(orthoRect.Left),round(orthoRect.Top),round(orthoRect.Right),round(orthoRect.Bottom));
  1180. if Assigned(backScan) then
  1181. ADest.FillEllipseInRect(r, backScan, dmDrawWithTransparency) else
  1182. ADest.FillEllipseInRect(r, BackFill.SolidColor, dmDrawWithTransparency)
  1183. end
  1184. else
  1185. begin
  1186. if Assigned(backScan) then
  1187. ADest.FillEllipseAntialias(center.x, center.y, radius.x, radius.y, backScan) else
  1188. ADest.FillEllipseAntialias(center.x, center.y, radius.x, radius.y, BackFill.SolidColor);
  1189. end;
  1190. backScan.Free;
  1191. end;
  1192. if GetPenVisible then
  1193. begin
  1194. if PenFill.FillType = vftSolid then penScan := nil
  1195. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  1196. draftPen := ADraft and (PenWidth > 4);
  1197. if IsAffineMatrixScaledRotation(AMatrix) and not (draftPen and Assigned(penScan)) then
  1198. begin
  1199. penZoom := VectLen(AMatrix[1,1],AMatrix[2,1]);
  1200. ADest.CustomPenStyle := PenStyle;
  1201. if draftPen then
  1202. ADest.Ellipse(center.x, center.y, radius.x, radius.y, PenColor, PenWidth*penZoom, dmDrawWithTransparency)
  1203. else
  1204. begin
  1205. if Assigned(penScan) then
  1206. ADest.EllipseAntialias(center.x, center.y, radius.x, radius.y, penScan, PenWidth*penZoom) else
  1207. ADest.EllipseAntialias(center.x, center.y, radius.x, radius.y, PenColor, PenWidth*penZoom);
  1208. end;
  1209. ADest.PenStyle := psSolid;
  1210. end else
  1211. begin
  1212. m:= MatrixForPixelCentered(AMatrix);
  1213. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1214. pts := ComputeStroke(pts,true, AMatrix);
  1215. if draftPen then
  1216. begin
  1217. if Assigned(penScan) then
  1218. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  1219. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency)
  1220. end
  1221. else
  1222. begin
  1223. if Assigned(penScan) then
  1224. ADest.FillPolyAntialias(pts, penScan) else
  1225. ADest.FillPolyAntialias(pts, PenColor);
  1226. end;
  1227. end;
  1228. penScan.Free;
  1229. end;
  1230. end else
  1231. begin
  1232. m:= MatrixForPixelCentered(AMatrix);
  1233. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1234. If GetBackVisible then
  1235. begin
  1236. if BackFill.FillType = vftSolid then backScan := nil
  1237. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  1238. if ADraft then
  1239. begin
  1240. if Assigned(backScan) then
  1241. ADest.FillPoly(pts, backScan, dmDrawWithTransparency) else
  1242. ADest.FillPoly(pts, BackFill.SolidColor, dmDrawWithTransparency)
  1243. end
  1244. else
  1245. begin
  1246. if Assigned(backScan) then
  1247. ADest.FillPolyAntialias(pts, backScan) else
  1248. ADest.FillPolyAntialias(pts, BackFill.SolidColor)
  1249. end;
  1250. backScan.Free;
  1251. end;
  1252. if GetPenVisible then
  1253. begin
  1254. if PenFill.FillType = vftSolid then penScan := nil
  1255. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  1256. pts := ComputeStroke(pts,true, AMatrix);
  1257. if ADraft and (PenWidth > 4) then
  1258. begin
  1259. if Assigned(penScan) then
  1260. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  1261. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency)
  1262. end
  1263. else
  1264. begin
  1265. if Assigned(penScan) then
  1266. ADest.FillPolyAntialias(pts, penScan) else
  1267. ADest.FillPolyAntialias(pts, PenColor);
  1268. end;
  1269. penScan.Free;
  1270. end;
  1271. end;
  1272. end;
  1273. function TEllipseShape.GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
  1274. var
  1275. xMargin, yMargin: single;
  1276. begin
  1277. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
  1278. result:= EmptyRectF
  1279. else
  1280. begin
  1281. result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
  1282. if GetPenVisible(rboAssumePenFill in AOptions) then
  1283. begin
  1284. xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
  1285. yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
  1286. result.Left -= xMargin;
  1287. result.Top -= yMargin;
  1288. result.Right += xMargin;
  1289. result.Bottom += yMargin;
  1290. end;
  1291. end;
  1292. end;
  1293. function TEllipseShape.PointInShape(APoint: TPointF): boolean;
  1294. var
  1295. pts: ArrayOfTPointF;
  1296. begin
  1297. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1298. if GetBackVisible and IsPointInPolygon(pts, APoint, true) then
  1299. result := true else
  1300. if GetPenVisible then
  1301. begin
  1302. pts := ComputeStroke(pts, true, AffineMatrixIdentity);
  1303. result:= IsPointInPolygon(pts, APoint, true);
  1304. end else
  1305. result := false;
  1306. end;
  1307. function TEllipseShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1308. var
  1309. pts: ArrayOfTPointF;
  1310. begin
  1311. if GetPenVisible or GetBackVisible then
  1312. begin
  1313. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1314. pts := ComputeStrokeEnvelope(pts, true, ARadius*2);
  1315. result:= IsPointInPolygon(pts, APoint, true);
  1316. end else
  1317. result := false;
  1318. end;
  1319. function TEllipseShape.PointInBack(APoint: TPointF): boolean;
  1320. var
  1321. pts: ArrayOfTPointF;
  1322. scan: TBGRACustomScanner;
  1323. begin
  1324. if GetBackVisible then
  1325. begin
  1326. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1327. result:= IsPointInPolygon(pts, APoint, true);
  1328. if result and (BackFill.FillType = vftTexture) then
  1329. begin
  1330. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1331. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1332. scan.Free;
  1333. end;
  1334. end else
  1335. result := false;
  1336. end;
  1337. function TEllipseShape.PointInPen(APoint: TPointF): boolean;
  1338. var
  1339. pts: ArrayOfTPointF;
  1340. begin
  1341. if GetPenVisible then
  1342. begin
  1343. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1344. pts := ComputeStroke(pts,true, AffineMatrixIdentity);
  1345. result:= IsPointInPolygon(pts, APoint, true);
  1346. end else
  1347. result := false;
  1348. end;
  1349. function TEllipseShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  1350. var
  1351. ab: TAffineBox;
  1352. backSurface, totalSurface, penSurface: Single;
  1353. begin
  1354. if not GetPenVisible and not GetBackVisible then
  1355. result := false
  1356. else
  1357. begin
  1358. ab := GetAffineBox(AMatrix, true);
  1359. backSurface := ab.Surface*Pi/4;
  1360. if GetPenVisible then
  1361. begin
  1362. penSurface := (ab.Width+ab.Height)*(Pi/2)*PenWidth;
  1363. if GetBackVisible then
  1364. totalSurface:= backSurface+penSurface/2
  1365. else
  1366. totalSurface := penSurface;
  1367. end else
  1368. totalSurface := backSurface;
  1369. result := (totalSurface > 640*480) or
  1370. ((backSurface > 320*240) and GetBackVisible and BackFill.IsSlow(AMatrix)) or
  1371. ((penSurface > 320*240) and GetPenVisible and PenFill.IsSlow(AMatrix));
  1372. end;
  1373. end;
  1374. class function TEllipseShape.StorageClassName: RawByteString;
  1375. begin
  1376. result := 'ellipse';
  1377. end;
  1378. { TPhongShape }
  1379. procedure TPhongShape.SetShapeKind(AValue: TPhongShapeKind);
  1380. begin
  1381. if FShapeKind=AValue then Exit;
  1382. BeginUpdate(TPhongShapeDiff);
  1383. FShapeKind:=AValue;
  1384. EndUpdate;
  1385. end;
  1386. procedure TPhongShape.OnMoveLightPos(ASender: TObject; APrevCoord,
  1387. ANewCoord: TPointF; AShift: TShiftState);
  1388. begin
  1389. LightPosition := ANewCoord;
  1390. end;
  1391. procedure TPhongShape.SetBorderSizePercent(AValue: single);
  1392. begin
  1393. if FBorderSizePercent=AValue then Exit;
  1394. BeginUpdate(TPhongShapeDiff);
  1395. FBorderSizePercent:=AValue;
  1396. EndUpdate;
  1397. end;
  1398. procedure TPhongShape.SetLightPosition(AValue: TPointF);
  1399. begin
  1400. if FLightPosition=AValue then Exit;
  1401. BeginUpdate(TPhongShapeDiff);
  1402. FLightPosition:=AValue;
  1403. EndUpdate;
  1404. end;
  1405. procedure TPhongShape.SetShapeAltitudePercent(AValue: single);
  1406. begin
  1407. if FShapeAltitudePercent=AValue then Exit;
  1408. BeginUpdate(TPhongShapeDiff);
  1409. FShapeAltitudePercent:=AValue;
  1410. EndUpdate;
  1411. end;
  1412. function TPhongShape.GetEnvelope: ArrayOfTPointF;
  1413. var
  1414. box: TAffineBox;
  1415. begin
  1416. case ShapeKind of
  1417. pskHalfSphere, pskConeTop: result := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1418. pskConeSide: result := PointsF([FOrigin - (FYAxis-FOrigin), FYAxis + (FXAxis-FOrigin), FYAxis - (FXAxis-FOrigin)]);
  1419. else
  1420. begin
  1421. box := GetAffineBox(AffineMatrixIdentity, true);
  1422. result := box.AsPolygon;
  1423. end;
  1424. end;
  1425. end;
  1426. function TPhongShape.AllowShearTransform: boolean;
  1427. begin
  1428. Result:= false;
  1429. end;
  1430. constructor TPhongShape.Create(AContainer: TVectorOriginal);
  1431. begin
  1432. inherited Create(AContainer);
  1433. FShapeKind:= pskRectangle;
  1434. FLightPosition := PointF(0,0);
  1435. FShapeAltitudePercent:= DefaultPhongShapeAltitudePercent;
  1436. FBorderSizePercent:= DefaultPhongBorderSizePercent;
  1437. end;
  1438. destructor TPhongShape.Destroy;
  1439. begin
  1440. inherited Destroy;
  1441. end;
  1442. function TPhongShape.GetCornerPositition: single;
  1443. begin
  1444. if ShapeKind in [pskHalfSphere,pskConeTop] then
  1445. result := sqrt(2)/2
  1446. else
  1447. result := 1;
  1448. end;
  1449. class function TPhongShape.Fields: TVectorShapeFields;
  1450. begin
  1451. Result:= [vsfBackFill];
  1452. end;
  1453. class function TPhongShape.PreferPixelCentered: boolean;
  1454. begin
  1455. Result:= false;
  1456. end;
  1457. function TPhongShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  1458. var
  1459. u, v: TPointF;
  1460. rx, ry: Single;
  1461. p: TBGRAPath;
  1462. begin
  1463. rx := Width; ry := Height;
  1464. case ShapeKind of
  1465. pskHalfSphere, pskConeTop:
  1466. if rx <> ry then
  1467. result := AContent.AppendEllipse(Origin, PointF(rx, ry))
  1468. else result := AContent.AppendCircle(Origin, rx);
  1469. pskConeSide: begin
  1470. p := TBGRAPath.Create;
  1471. p.moveTo(Origin.x, origin.y - ry);
  1472. p.lineTo(Origin.x + rx, Origin.y + ry);
  1473. p.lineTo(Origin.x - rx, Origin.y + ry);
  1474. result := AContent.AppendPath(p);
  1475. p.Free;
  1476. end
  1477. else {pskRectangle, pskRoundRectangle, pskHorizCylinder, pskVertCylinder}
  1478. result := AContent.AppendRect(Origin.x - rx, Origin.y - ry, rx*2, ry*2);
  1479. end;
  1480. if (XAxis.y <> 0) or (YAxis.x <> 0) then
  1481. begin
  1482. u := XAxis - Origin;
  1483. if rx > 0 then u *= (1/rx);
  1484. v := YAxis - Origin;
  1485. if ry > 0 then v *= (1/ry);
  1486. result.matrix[cuPixel] := AffineMatrixTranslation(Origin.X, Origin.Y) *
  1487. AffineMatrix(u, v, PointF(0, 0)) *
  1488. AffineMatrixTranslation(-Origin.X, -Origin.Y);
  1489. end;
  1490. result.strokeNone;
  1491. ApplyFillStyleToSVG(result, ADefs);
  1492. end;
  1493. function TPhongShape.GetAlignBounds(const ALayoutRect: TRect;
  1494. const AMatrix: TAffineMatrix): TRectF;
  1495. var
  1496. m: TAffineMatrix;
  1497. pts: ArrayOfTPointF;
  1498. i: Integer;
  1499. procedure IncludePoint(const APoint: TPointF);
  1500. begin
  1501. if APoint.x < result.Left then result.Left := APoint.x else
  1502. if APoint.x > result.Right then result.Right := APoint.x;
  1503. if APoint.y < result.Top then result.Top := APoint.y else
  1504. if APoint.y > result.Bottom then result.Bottom := APoint.y;
  1505. end;
  1506. begin
  1507. m:= AffineMatrixTranslation(0.5,0.5)*MatrixForPixelCentered(AMatrix);
  1508. if ShapeKind in[pskHalfSphere,pskConeTop] then
  1509. begin
  1510. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1511. if pts = nil then exit(EmptyRectF);
  1512. result.TopLeft := pts[0];
  1513. result.BottomRight := pts[0];
  1514. for i := 0 to high(pts) do IncludePoint(pts[i]);
  1515. IncludePoint(m*XAxis);
  1516. IncludePoint(m*YAxis);
  1517. IncludePoint(m*(Origin-(XAxis-Origin)));
  1518. IncludePoint(m*(Origin-(YAxis-Origin)));
  1519. end else
  1520. if ShapeKind = pskConeSide then
  1521. begin
  1522. result.TopLeft := m*Origin;
  1523. result.BottomRight := m*Origin;
  1524. IncludePoint(m*(XAxis+(YAxis-Origin)));
  1525. IncludePoint(m*(Origin-(XAxis-Origin)+(YAxis-Origin)));
  1526. IncludePoint(m*(Origin-(YAxis-Origin)));
  1527. end else
  1528. result := inherited GetAlignBounds(ALayoutRect,AMatrix);
  1529. end;
  1530. procedure TPhongShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
  1531. var
  1532. idxLight: Integer;
  1533. begin
  1534. inherited ConfigureCustomEditor(AEditor);
  1535. idxLight := AEditor.AddPoint(FLightPosition, @OnMoveLightPos, true);
  1536. if AEditor is TVectorOriginalEditor then
  1537. TVectorOriginalEditor(AEditor).AddLabel(idxLight, rsLightPosition, taCenter, tlTop);
  1538. end;
  1539. procedure TPhongShape.MouseDown(RightButton: boolean; ClickCount: integer; Shift: TShiftState; X,
  1540. Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  1541. begin
  1542. inherited MouseDown(RightButton, ClickCount, Shift, X, Y, ACursor, AHandled);
  1543. if not AHandled then
  1544. begin
  1545. if RightButton then
  1546. begin
  1547. LightPosition := PointF(x,y);
  1548. AHandled := true;
  1549. end;
  1550. end;
  1551. end;
  1552. procedure TPhongShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  1553. begin
  1554. BeginUpdate;
  1555. inherited LoadFromStorage(AStorage);
  1556. LightPosition := AStorage.PointF['light-pos'];
  1557. if isEmptyPointF(LightPosition) then LightPosition := PointF(0,0);
  1558. case AStorage.RawString['shape-kind'] of
  1559. 'round-rectangle': ShapeKind:= pskRoundRectangle;
  1560. 'half-sphere': ShapeKind := pskHalfSphere;
  1561. 'cone-top': ShapeKind := pskConeTop;
  1562. 'cone-side': ShapeKind := pskConeSide;
  1563. 'horizontal-cylinder': ShapeKind := pskHorizCylinder;
  1564. 'vertical-cylinder': ShapeKind := pskVertCylinder;
  1565. else
  1566. {'rectangle'} ShapeKind:= pskRectangle;
  1567. end;
  1568. ShapeAltitudePercent := AStorage.FloatDef['shape-altitude-percent', DefaultPhongShapeAltitudePercent];
  1569. if ShapeKind in[pskRectangle,pskRoundRectangle] then
  1570. BorderSizePercent := AStorage.FloatDef['border-size-percent', DefaultPhongBorderSizePercent]
  1571. else
  1572. BorderSizePercent := DefaultPhongBorderSizePercent;
  1573. EndUpdate;
  1574. end;
  1575. procedure TPhongShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  1576. begin
  1577. inherited SaveToStorage(AStorage);
  1578. AStorage.PointF['light-pos'] := LightPosition;
  1579. case ShapeKind of
  1580. pskRectangle: AStorage.RawString['shape-kind'] := 'rectangle';
  1581. pskRoundRectangle: AStorage.RawString['shape-kind'] := 'round-rectangle';
  1582. pskHalfSphere: AStorage.RawString['shape-kind'] := 'half-sphere';
  1583. pskConeTop: AStorage.RawString['shape-kind'] := 'cone-top';
  1584. pskConeSide: AStorage.RawString['shape-kind'] := 'cone-side';
  1585. pskHorizCylinder: AStorage.RawString['shape-kind'] := 'horizontal-cylinder';
  1586. pskVertCylinder: AStorage.RawString['shape-kind'] := 'vertical-cylinder';
  1587. end;
  1588. AStorage.Float['shape-altitude-percent'] := ShapeAltitudePercent;
  1589. if ShapeKind in[pskRectangle,pskRoundRectangle] then
  1590. AStorage.Float['border-size-percent'] := FBorderSizePercent;
  1591. end;
  1592. procedure TPhongShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  1593. ADraft: boolean);
  1594. var
  1595. ab,abRaster: TAffineBox;
  1596. mapWidth,mapHeight: integer;
  1597. shader: TPhongShading;
  1598. approxFactor,borderSize: single;
  1599. m,mInv: TAffineMatrix;
  1600. h, lightPosZ: single;
  1601. map,raster: TBGRABitmap;
  1602. u,v,lightPosF: TPointF;
  1603. scan: TBGRACustomScanner;
  1604. rectRenderF,rectRasterF: TRectF;
  1605. rectRender,rectRaster, prevClip: TRect;
  1606. begin
  1607. if not GetBackVisible then exit;
  1608. //determine final render bounds
  1609. rectRenderF := GetRenderBounds(InfiniteRect,AMatrix);
  1610. if IsEmptyRectF(rectRenderF) then exit;
  1611. rectRender := rect(floor(rectRenderF.Left),floor(rectRenderF.Top),ceil(rectRenderF.Right),ceil(rectRenderF.Bottom));
  1612. rectRender.Intersect(ADest.ClipRect);
  1613. if IsRectEmpty(rectRender) then exit;
  1614. //determine map size before transform
  1615. ab := GetAffineBox(AMatrix, false);
  1616. if (ab.Width = 0) or (ab.Height = 0) then exit;
  1617. if ab.Width > ab.Height then
  1618. begin
  1619. mapWidth := ceil(ab.Width);
  1620. mapHeight := ceil(ab.Surface/ab.Width);
  1621. end else
  1622. begin
  1623. mapWidth := ceil(ab.Surface/ab.Height);
  1624. mapHeight := ceil(ab.Height);
  1625. end;
  1626. approxFactor := 1;
  1627. if ADraft then
  1628. begin
  1629. if mapWidth > 100 then approxFactor:= min(approxFactor, 100/mapWidth);
  1630. if mapHeight > 100 then approxFactor:= min(approxFactor, 100/mapHeight);
  1631. end else
  1632. begin
  1633. if mapWidth > 800 then approxFactor:= min(approxFactor, 800/mapWidth);
  1634. if mapHeight > 800 then approxFactor:= min(approxFactor, 800/mapHeight);
  1635. end;
  1636. mapWidth:= ceil(mapWidth*approxFactor);
  1637. mapHeight:= ceil(mapHeight*approxFactor);
  1638. //determine map transform
  1639. u := (ab.TopRight-ab.TopLeft)*(1/ab.Width);
  1640. v := (ab.BottomLeft-ab.TopLeft)*(1/ab.Height);
  1641. m := AffineMatrix(u,v,ab.TopLeft)*AffineMatrixScale(ab.Width/mapWidth,ab.Height/mapHeight);
  1642. borderSize := FBorderSizePercent/200*min(ab.Width,ab.Height);
  1643. if not IsAffineMatrixInversible(m) then exit;
  1644. mInv := AffineMatrixInverse(m);
  1645. try
  1646. //create height map
  1647. map := nil;
  1648. case ShapeKind of
  1649. pskRoundRectangle: begin
  1650. map := CreateRoundRectanglePreciseMap(mapWidth,mapHeight,
  1651. round(borderSize*mapWidth/ab.Width),
  1652. round(borderSize*mapHeight/ab.Height),[]);
  1653. h := FShapeAltitudePercent*approxFactor;
  1654. end;
  1655. pskHalfSphere: begin
  1656. map := CreateSpherePreciseMap(mapWidth,mapHeight);
  1657. h := FShapeAltitudePercent/100*sqrt(mapWidth*mapHeight);
  1658. end;
  1659. pskConeTop: begin
  1660. map := CreateConePreciseMap(mapWidth,mapHeight);
  1661. h := FShapeAltitudePercent/100*sqrt(mapWidth*mapHeight);
  1662. end;
  1663. pskConeSide: begin
  1664. map := CreateVerticalConePreciseMap(mapWidth,mapHeight);
  1665. h := FShapeAltitudePercent/100*mapWidth;
  1666. end;
  1667. pskHorizCylinder: begin
  1668. map := CreateHorizontalCylinderPreciseMap(mapWidth,mapHeight);
  1669. h := FShapeAltitudePercent/100*mapHeight;
  1670. end;
  1671. pskVertCylinder: begin
  1672. map := CreateVerticalCylinderPreciseMap(mapWidth,mapHeight);
  1673. h := FShapeAltitudePercent/100*mapWidth;
  1674. end;
  1675. else
  1676. {pskRectangle: }begin
  1677. map := CreateRectanglePreciseMap(mapWidth,mapHeight,
  1678. round(borderSize*mapWidth/ab.Width),
  1679. round(borderSize*mapHeight/ab.Height),[]);
  1680. h := FShapeAltitudePercent*approxFactor;
  1681. end;
  1682. end;
  1683. abRaster := mInv*TAffineBox.AffineBox(rectRenderF);
  1684. rectRasterF := abRaster.RectBoundsF;
  1685. rectRaster := rect(floor(rectRasterF.Left),floor(rectRasterF.Top),ceil(rectRasterF.Right),ceil(rectRasterF.Bottom));
  1686. raster := nil;
  1687. shader := nil;
  1688. if IntersectRect(rectRaster, rectRaster, rect(0,0,mapWidth,mapHeight)) then
  1689. try
  1690. shader:= TPhongShading.Create;
  1691. shader.AmbientFactor := 0.5;
  1692. shader.NegativeDiffusionFactor := 0.15;
  1693. lightPosF := AffineMatrixTranslation(-rectRaster.Left,-rectRaster.Top)
  1694. *mInv*AMatrix*FLightPosition;
  1695. lightPosZ := 100*Power(approxFactor,1.1);
  1696. if h*3/2 > lightPosZ then lightposZ := h*3/2;
  1697. shader.LightPosition3D := Point3D(lightPosF.x,lightPosF.y,lightPosZ);
  1698. raster := TBGRABitmap.Create(rectRaster.Width,rectRaster.Height);
  1699. if BackFill.FillType = vftSolid then
  1700. shader.Draw(raster,map,h,-rectRaster.Left,-rectRaster.Top,BackFill.SolidColor)
  1701. else
  1702. begin
  1703. scan := BackFill.CreateScanner(AffineMatrixTranslation(-rectRaster.left,-rectRaster.top)*mInv*AMatrix,ADraft);
  1704. shader.DrawScan(raster,map,h,-rectRaster.Left,-rectRaster.Top,scan);
  1705. scan.Free;
  1706. end;
  1707. prevClip := ADest.ClipRect;
  1708. ADest.ClipRect := rectRender;
  1709. if ADraft then
  1710. ADest.PutImageAffine(m*AffineMatrixTranslation(rectRaster.Left,rectRaster.Top),raster,rfBox,dmDrawWithTransparency)
  1711. else
  1712. ADest.PutImageAffine(m*AffineMatrixTranslation(rectRaster.Left,rectRaster.Top),raster,rfHalfCosine,dmDrawWithTransparency);
  1713. ADest.ClipRect := prevClip;
  1714. finally
  1715. raster.Free;
  1716. shader.Free;
  1717. end;
  1718. finally
  1719. map.Free;
  1720. end;
  1721. end;
  1722. function TPhongShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix;
  1723. AOptions: TRenderBoundsOptions): TRectF;
  1724. begin
  1725. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) then
  1726. result:= EmptyRectF
  1727. else
  1728. result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
  1729. end;
  1730. function TPhongShape.PointInShape(APoint: TPointF): boolean;
  1731. var
  1732. pts: ArrayOfTPointF;
  1733. begin
  1734. if not GetBackVisible then exit(false);
  1735. pts := GetEnvelope;
  1736. result := IsPointInPolygon(pts, APoint, true);
  1737. end;
  1738. function TPhongShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1739. var
  1740. pts: ArrayOfTPointF;
  1741. begin
  1742. if GetBackVisible then
  1743. begin
  1744. pts := ComputeStrokeEnvelope(GetEnvelope, true, ARadius*2);
  1745. result:= IsPointInPolygon(pts, APoint, true);
  1746. end
  1747. else result := false;
  1748. end;
  1749. function TPhongShape.PointInBack(APoint: TPointF): boolean;
  1750. var
  1751. scan: TBGRACustomScanner;
  1752. begin
  1753. result := PointInShape(APoint);
  1754. if result and (BackFill.FillType = vftTexture) then
  1755. begin
  1756. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1757. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1758. scan.Free;
  1759. end;
  1760. end;
  1761. function TPhongShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  1762. var
  1763. ab: TAffineBox;
  1764. begin
  1765. if not GetBackVisible then exit(false);
  1766. ab := GetAffineBox(AMatrix, true);
  1767. result := ab.Surface > 320*240;
  1768. end;
  1769. function TPhongShape.GetGenericCost: integer;
  1770. begin
  1771. Result:= 10;
  1772. end;
  1773. procedure TPhongShape.Transform(const AMatrix: TAffineMatrix);
  1774. begin
  1775. BeginUpdate(TPhongShapeDiff);
  1776. LightPosition := AMatrix*LightPosition;
  1777. inherited Transform(AMatrix);
  1778. EndUpdate;
  1779. end;
  1780. class function TPhongShape.StorageClassName: RawByteString;
  1781. begin
  1782. result := 'phong';
  1783. end;
  1784. initialization
  1785. RegisterVectorShape(TRectShape);
  1786. RegisterVectorShape(TEllipseShape);
  1787. RegisterVectorShape(TPhongShape);
  1788. end.