lcvectorrectshapes.pas 60 KB

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