lcvectorrectshapes.pas 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956
  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, vsfAliased];
  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. ApplyAliasingToSVG(result);
  892. end;
  893. end;
  894. procedure TRectShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  895. ADraft: boolean);
  896. const GradientDithering = false;
  897. var
  898. pts: Array of TPointF;
  899. orthoRect: TRectF;
  900. r: TRect;
  901. backScan, penScan: TBGRACustomScanner;
  902. temp: TBGRABitmap;
  903. i: Integer;
  904. begin
  905. pts := GetAffineBox(AMatrix, true).AsPolygon;
  906. If GetBackVisible and (Width <> 0) and (Height <> 0) then
  907. begin
  908. if (BackFill.FillType = vftSolid) then backScan := nil
  909. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  910. if GetOrthoRect(AMatrix, orthoRect) then
  911. begin
  912. if ADraft or Aliased then
  913. begin
  914. r:= rect(round(orthoRect.Left+0.5),round(orthoRect.Top+0.5),round(orthoRect.Right+0.5),round(orthoRect.Bottom+0.5));
  915. if Assigned(backScan) then
  916. ADest.FillRect(r, backScan, dmDrawWithTransparency) else
  917. ADest.FillRect(r, BackFill.SolidColor, dmDrawWithTransparency)
  918. end
  919. else
  920. begin
  921. if Assigned(backScan) then
  922. begin
  923. if (BackFill.FillType = vftGradient) and GradientDithering then
  924. begin
  925. with orthoRect do
  926. r := rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
  927. temp := TBGRABitmap.Create(0,0);
  928. temp.SetSize(r.Width,r.Height);
  929. temp.FillRect(0,0,r.Width,r.Height,backScan,dmSet,Point(r.Left,r.Top),daFloydSteinberg);
  930. temp.ScanOffset := Point(-r.Left,-r.Top);
  931. ADest.FillRectAntialias(orthoRect, temp);
  932. temp.Free;
  933. end else
  934. ADest.FillRectAntialias(orthoRect, backScan);
  935. end else
  936. ADest.FillRectAntialias(orthoRect, BackFill.SolidColor);
  937. end;
  938. end else
  939. begin
  940. if ADraft or Aliased then
  941. begin
  942. if Assigned(backScan) then
  943. ADest.FillPoly(pts, backScan, dmDrawWithTransparency) else
  944. ADest.FillPoly(pts, BackFill.SolidColor, dmDrawWithTransparency)
  945. end
  946. else
  947. begin
  948. if Assigned(backScan) then
  949. begin
  950. if BackFill.FillType = vftGradient then
  951. begin
  952. r := rect(floor(pts[0].x),floor(pts[0].y),ceil(pts[0].x),ceil(pts[0].y));
  953. for i := 1 to high(pts) do
  954. r.Union(rect(floor(pts[i].x),floor(pts[i].y),ceil(pts[i].x),ceil(pts[i].y)));
  955. temp := TBGRABitmap.Create(0,0);
  956. temp.SetSize(r.Width,r.Height);
  957. temp.FillRect(0,0,r.Width,r.Height,backScan,dmSet,Point(r.Left,r.Top),daFloydSteinberg);
  958. temp.ScanOffset := Point(-r.Left,-r.Top);
  959. ADest.FillPolyAntialias(pts, temp);
  960. temp.Free;
  961. end else
  962. ADest.FillPolyAntialias(pts, backScan);
  963. end else
  964. ADest.FillPolyAntialias(pts, BackFill.SolidColor);
  965. end;
  966. end;
  967. backScan.Free;
  968. end;
  969. if GetPenVisible then
  970. begin
  971. if (PenFill.FillType = vftSolid) then penScan := nil
  972. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  973. pts := ComputeStroke(pts,true, AMatrix);
  974. if (ADraft and (PenWidth > 4)) or Aliased then
  975. begin
  976. if Assigned(penScan) then
  977. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  978. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency)
  979. end
  980. else
  981. begin
  982. if Assigned(penScan) then
  983. ADest.FillPolyAntialias(pts, penScan) else
  984. ADest.FillPolyAntialias(pts, PenColor);
  985. end;
  986. penScan.Free;
  987. end;
  988. end;
  989. function TRectShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
  990. var
  991. i: Integer;
  992. pts: ArrayOfTPointF;
  993. xMargin, yMargin: single;
  994. begin
  995. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
  996. result:= EmptyRectF
  997. else
  998. begin
  999. result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
  1000. if GetPenVisible(rboAssumePenFill in AOptions) then
  1001. begin
  1002. if (JoinStyle <> pjsMiter) or (Stroker.MiterLimit <= 1) then
  1003. begin
  1004. xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
  1005. yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
  1006. result.Left -= xMargin;
  1007. result.Top -= yMargin;
  1008. result.Right += xMargin;
  1009. result.Bottom += yMargin;
  1010. end else
  1011. begin
  1012. pts := ComputeStroke(GetAffineBox(AMatrix, false).AsPolygon, true, AMatrix);
  1013. for i := 0 to high(pts) do
  1014. if not IsEmptyPointF(pts[i]) then
  1015. begin
  1016. if pts[i].x < result.Left then result.Left := pts[i].x;
  1017. if pts[i].x > result.Right then result.Right := pts[i].x;
  1018. if pts[i].y < result.Top then result.Top := pts[i].y;
  1019. if pts[i].y > result.Bottom then result.Bottom := pts[i].y;
  1020. end;
  1021. end;
  1022. end;
  1023. end;
  1024. end;
  1025. function TRectShape.PointInShape(APoint: TPointF): boolean;
  1026. var
  1027. pts: ArrayOfTPointF;
  1028. box: TAffineBox;
  1029. begin
  1030. box := GetAffineBox(AffineMatrixIdentity, true);
  1031. if GetBackVisible and box.Contains(APoint) then
  1032. result := true else
  1033. if GetPenVisible then
  1034. begin
  1035. pts := ComputeStroke(box.AsPolygon, true, AffineMatrixIdentity);
  1036. result:= IsPointInPolygon(pts, APoint, true);
  1037. end else
  1038. result := false;
  1039. end;
  1040. function TRectShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1041. var
  1042. pts: ArrayOfTPointF;
  1043. box: TAffineBox;
  1044. begin
  1045. if GetPenVisible or GetBackVisible then
  1046. begin
  1047. box := GetAffineBox(AffineMatrixIdentity, true);
  1048. pts := ComputeStrokeEnvelope(box.AsPolygon, true, ARadius*2);
  1049. result:= IsPointInPolygon(pts, APoint, true);
  1050. end
  1051. else result := false;
  1052. end;
  1053. function TRectShape.PointInBack(APoint: TPointF): boolean;
  1054. var
  1055. box: TAffineBox;
  1056. scan: TBGRACustomScanner;
  1057. begin
  1058. if GetBackVisible then
  1059. begin
  1060. box := GetAffineBox(AffineMatrixIdentity, true);
  1061. result := box.Contains(APoint);
  1062. if result and (BackFill.FillType = vftTexture) then
  1063. begin
  1064. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1065. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1066. scan.Free;
  1067. end;
  1068. end else
  1069. result := false;
  1070. end;
  1071. function TRectShape.PointInPen(APoint: TPointF): boolean;
  1072. var
  1073. pts: ArrayOfTPointF;
  1074. begin
  1075. if GetPenVisible then
  1076. begin
  1077. pts := GetAffineBox(AffineMatrixIdentity, true).AsPolygon;
  1078. pts := ComputeStroke(pts,true, AffineMatrixIdentity);
  1079. result:= IsPointInPolygon(pts, APoint, true);
  1080. end else
  1081. result := false;
  1082. end;
  1083. class function TRectShape.StorageClassName: RawByteString;
  1084. begin
  1085. result := 'rect';
  1086. end;
  1087. { TEllipseShape }
  1088. function TEllipseShape.GetCornerPositition: single;
  1089. begin
  1090. result := sqrt(2)/2;
  1091. end;
  1092. constructor TEllipseShape.Create(AContainer: TVectorOriginal);
  1093. begin
  1094. inherited Create(AContainer);
  1095. inherited SetJoinStyle(pjsRound);
  1096. end;
  1097. class function TEllipseShape.Fields: TVectorShapeFields;
  1098. begin
  1099. Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfBackFill, vsfAliased];
  1100. end;
  1101. function TEllipseShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  1102. var
  1103. u, v: TPointF;
  1104. rx, ry: Single;
  1105. begin
  1106. rx := Width; ry := Height;
  1107. if rx <> ry then
  1108. result := AContent.AppendEllipse(Origin, PointF(rx, ry))
  1109. else result := AContent.AppendCircle(Origin, rx);
  1110. if (XAxis.y <> 0) or (YAxis.x <> 0) then
  1111. begin
  1112. u := XAxis - Origin;
  1113. if rx > 0 then u *= (1/rx);
  1114. v := YAxis - Origin;
  1115. if ry > 0 then v *= (1/ry);
  1116. result.matrix[cuPixel] := AffineMatrixTranslation(Origin.X, Origin.Y) *
  1117. AffineMatrix(u, v, PointF(0, 0)) *
  1118. AffineMatrixTranslation(-Origin.X, -Origin.Y);
  1119. end;
  1120. ApplyStrokeStyleToSVG(result, ADefs);
  1121. ApplyFillStyleToSVG(result, ADefs);
  1122. ApplyAliasingToSVG(result);
  1123. end;
  1124. function TEllipseShape.GetAlignBounds(const ALayoutRect: TRect;
  1125. const AMatrix: TAffineMatrix): TRectF;
  1126. var
  1127. m: TAffineMatrix;
  1128. pts: ArrayOfTPointF;
  1129. i: Integer;
  1130. zoom: Single;
  1131. procedure IncludePoint(const APoint: TPointF);
  1132. begin
  1133. if APoint.x < result.Left then result.Left := APoint.x else
  1134. if APoint.x > result.Right then result.Right := APoint.x;
  1135. if APoint.y < result.Top then result.Top := APoint.y else
  1136. if APoint.y > result.Bottom then result.Bottom := APoint.y;
  1137. end;
  1138. begin
  1139. m:= AffineMatrixTranslation(0.5,0.5)*MatrixForPixelCentered(AMatrix);
  1140. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1141. if pts = nil then exit(EmptyRectF);
  1142. result.TopLeft := pts[0];
  1143. result.BottomRight := pts[0];
  1144. for i := 0 to high(pts) do IncludePoint(pts[i]);
  1145. IncludePoint(m*XAxis);
  1146. IncludePoint(m*YAxis);
  1147. IncludePoint(m*(Origin-(XAxis-Origin)));
  1148. IncludePoint(m*(Origin-(YAxis-Origin)));
  1149. if GetPenVisible then
  1150. begin
  1151. zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
  1152. result.Left -= zoom*PenWidth/2;
  1153. result.Right += zoom*PenWidth/2;
  1154. result.Top -= zoom*PenWidth/2;
  1155. result.Bottom += zoom*PenWidth/2;
  1156. end;
  1157. end;
  1158. procedure TEllipseShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  1159. ADraft: boolean);
  1160. var
  1161. pts: Array of TPointF;
  1162. orthoRect: TRectF;
  1163. center, radius: TPointF;
  1164. aliasedPen, isOrtho: Boolean;
  1165. r: TRect;
  1166. backScan, penScan: TBGRACustomScanner;
  1167. penZoom: Single;
  1168. m: TAffineMatrix;
  1169. begin
  1170. isOrtho := GetOrthoRect(AMatrix, orthoRect);
  1171. if isOrtho then
  1172. begin
  1173. center := (orthoRect.TopLeft+orthoRect.BottomRight)*0.5;
  1174. radius := (orthoRect.BottomRight-orthoRect.TopLeft)*0.5;
  1175. If GetBackVisible then
  1176. begin
  1177. if BackFill.FillType = vftSolid then backScan := nil
  1178. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  1179. if ADraft or Aliased then
  1180. begin
  1181. r := rect(round(orthoRect.Left),round(orthoRect.Top),round(orthoRect.Right),round(orthoRect.Bottom));
  1182. if Assigned(backScan) then
  1183. ADest.FillEllipseInRect(r, backScan, dmDrawWithTransparency) else
  1184. ADest.FillEllipseInRect(r, BackFill.SolidColor, dmDrawWithTransparency)
  1185. end
  1186. else
  1187. begin
  1188. if Assigned(backScan) then
  1189. ADest.FillEllipseAntialias(center.x, center.y, radius.x, radius.y, backScan) else
  1190. ADest.FillEllipseAntialias(center.x, center.y, radius.x, radius.y, BackFill.SolidColor);
  1191. end;
  1192. backScan.Free;
  1193. end;
  1194. if GetPenVisible then
  1195. begin
  1196. if PenFill.FillType = vftSolid then penScan := nil
  1197. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  1198. aliasedPen := (ADraft and (PenWidth > 4)) or Aliased;
  1199. if IsAffineMatrixScaledRotation(AMatrix) and not (aliasedPen and Assigned(penScan)) then
  1200. begin
  1201. penZoom := VectLen(AMatrix[1,1],AMatrix[2,1]);
  1202. ADest.CustomPenStyle := PenStyle;
  1203. if aliasedPen then
  1204. ADest.Ellipse(center.x, center.y, radius.x, radius.y, PenColor, PenWidth*penZoom, dmDrawWithTransparency)
  1205. else
  1206. begin
  1207. if Assigned(penScan) then
  1208. ADest.EllipseAntialias(center.x, center.y, radius.x, radius.y, penScan, PenWidth*penZoom) else
  1209. ADest.EllipseAntialias(center.x, center.y, radius.x, radius.y, PenColor, PenWidth*penZoom);
  1210. end;
  1211. ADest.PenStyle := psSolid;
  1212. end else
  1213. begin
  1214. m:= MatrixForPixelCentered(AMatrix);
  1215. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1216. pts := ComputeStroke(pts,true, AMatrix);
  1217. if aliasedPen then
  1218. begin
  1219. if Assigned(penScan) then
  1220. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  1221. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency)
  1222. end
  1223. else
  1224. begin
  1225. if Assigned(penScan) then
  1226. ADest.FillPolyAntialias(pts, penScan) else
  1227. ADest.FillPolyAntialias(pts, PenColor);
  1228. end;
  1229. end;
  1230. penScan.Free;
  1231. end;
  1232. end else
  1233. begin
  1234. m:= MatrixForPixelCentered(AMatrix);
  1235. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1236. If GetBackVisible then
  1237. begin
  1238. if BackFill.FillType = vftSolid then backScan := nil
  1239. else backScan := BackFill.CreateScanner(AMatrix, ADraft);
  1240. if ADraft or Aliased then
  1241. begin
  1242. if Assigned(backScan) then
  1243. ADest.FillPoly(pts, backScan, dmDrawWithTransparency) else
  1244. ADest.FillPoly(pts, BackFill.SolidColor, dmDrawWithTransparency)
  1245. end
  1246. else
  1247. begin
  1248. if Assigned(backScan) then
  1249. ADest.FillPolyAntialias(pts, backScan) else
  1250. ADest.FillPolyAntialias(pts, BackFill.SolidColor)
  1251. end;
  1252. backScan.Free;
  1253. end;
  1254. if GetPenVisible then
  1255. begin
  1256. if PenFill.FillType = vftSolid then penScan := nil
  1257. else penScan := PenFill.CreateScanner(AMatrix, ADraft);
  1258. pts := ComputeStroke(pts,true, AMatrix);
  1259. if (ADraft and (PenWidth > 4)) or Aliased then
  1260. begin
  1261. if Assigned(penScan) then
  1262. ADest.FillPoly(pts, penScan, dmDrawWithTransparency) else
  1263. ADest.FillPoly(pts, PenColor, dmDrawWithTransparency)
  1264. end
  1265. else
  1266. begin
  1267. if Assigned(penScan) then
  1268. ADest.FillPolyAntialias(pts, penScan) else
  1269. ADest.FillPolyAntialias(pts, PenColor);
  1270. end;
  1271. penScan.Free;
  1272. end;
  1273. end;
  1274. end;
  1275. function TEllipseShape.GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
  1276. var
  1277. xMargin, yMargin: single;
  1278. begin
  1279. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
  1280. result:= EmptyRectF
  1281. else
  1282. begin
  1283. result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
  1284. if GetPenVisible(rboAssumePenFill in AOptions) then
  1285. begin
  1286. xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
  1287. yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
  1288. result.Left -= xMargin;
  1289. result.Top -= yMargin;
  1290. result.Right += xMargin;
  1291. result.Bottom += yMargin;
  1292. end;
  1293. end;
  1294. end;
  1295. function TEllipseShape.PointInShape(APoint: TPointF): boolean;
  1296. var
  1297. pts: ArrayOfTPointF;
  1298. begin
  1299. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1300. if GetBackVisible and IsPointInPolygon(pts, APoint, true) then
  1301. result := true else
  1302. if GetPenVisible then
  1303. begin
  1304. pts := ComputeStroke(pts, true, AffineMatrixIdentity);
  1305. result:= IsPointInPolygon(pts, APoint, true);
  1306. end else
  1307. result := false;
  1308. end;
  1309. function TEllipseShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1310. var
  1311. pts: ArrayOfTPointF;
  1312. begin
  1313. if GetPenVisible or GetBackVisible then
  1314. begin
  1315. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1316. pts := ComputeStrokeEnvelope(pts, true, ARadius*2);
  1317. result:= IsPointInPolygon(pts, APoint, true);
  1318. end else
  1319. result := false;
  1320. end;
  1321. function TEllipseShape.PointInBack(APoint: TPointF): boolean;
  1322. var
  1323. pts: ArrayOfTPointF;
  1324. scan: TBGRACustomScanner;
  1325. begin
  1326. if GetBackVisible then
  1327. begin
  1328. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1329. result:= IsPointInPolygon(pts, APoint, true);
  1330. if result and (BackFill.FillType = vftTexture) then
  1331. begin
  1332. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1333. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1334. scan.Free;
  1335. end;
  1336. end else
  1337. result := false;
  1338. end;
  1339. function TEllipseShape.PointInPen(APoint: TPointF): boolean;
  1340. var
  1341. pts: ArrayOfTPointF;
  1342. begin
  1343. if GetPenVisible then
  1344. begin
  1345. pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1346. pts := ComputeStroke(pts,true, AffineMatrixIdentity);
  1347. result:= IsPointInPolygon(pts, APoint, true);
  1348. end else
  1349. result := false;
  1350. end;
  1351. function TEllipseShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  1352. var
  1353. ab: TAffineBox;
  1354. backSurface, totalSurface, penSurface: Single;
  1355. begin
  1356. if not GetPenVisible and not GetBackVisible then
  1357. result := false
  1358. else
  1359. begin
  1360. ab := GetAffineBox(AMatrix, true);
  1361. backSurface := ab.Surface*Pi/4;
  1362. if GetPenVisible then
  1363. begin
  1364. penSurface := (ab.Width+ab.Height)*(Pi/2)*PenWidth;
  1365. if GetBackVisible then
  1366. totalSurface:= backSurface+penSurface/2
  1367. else
  1368. totalSurface := penSurface;
  1369. end else
  1370. totalSurface := backSurface;
  1371. result := (totalSurface > 640*480) or
  1372. ((backSurface > 320*240) and GetBackVisible and BackFill.IsSlow(AMatrix)) or
  1373. ((penSurface > 320*240) and GetPenVisible and PenFill.IsSlow(AMatrix));
  1374. end;
  1375. end;
  1376. class function TEllipseShape.StorageClassName: RawByteString;
  1377. begin
  1378. result := 'ellipse';
  1379. end;
  1380. { TPhongShape }
  1381. procedure TPhongShape.SetShapeKind(AValue: TPhongShapeKind);
  1382. begin
  1383. if FShapeKind=AValue then Exit;
  1384. BeginUpdate(TPhongShapeDiff);
  1385. FShapeKind:=AValue;
  1386. EndUpdate;
  1387. end;
  1388. procedure TPhongShape.OnMoveLightPos(ASender: TObject; APrevCoord,
  1389. ANewCoord: TPointF; AShift: TShiftState);
  1390. begin
  1391. LightPosition := ANewCoord;
  1392. end;
  1393. procedure TPhongShape.SetBorderSizePercent(AValue: single);
  1394. begin
  1395. if FBorderSizePercent=AValue then Exit;
  1396. BeginUpdate(TPhongShapeDiff);
  1397. FBorderSizePercent:=AValue;
  1398. EndUpdate;
  1399. end;
  1400. procedure TPhongShape.SetLightPosition(AValue: TPointF);
  1401. begin
  1402. if FLightPosition=AValue then Exit;
  1403. BeginUpdate(TPhongShapeDiff);
  1404. FLightPosition:=AValue;
  1405. EndUpdate;
  1406. end;
  1407. procedure TPhongShape.SetShapeAltitudePercent(AValue: single);
  1408. begin
  1409. if FShapeAltitudePercent=AValue then Exit;
  1410. BeginUpdate(TPhongShapeDiff);
  1411. FShapeAltitudePercent:=AValue;
  1412. EndUpdate;
  1413. end;
  1414. function TPhongShape.GetEnvelope: ArrayOfTPointF;
  1415. var
  1416. box: TAffineBox;
  1417. begin
  1418. case ShapeKind of
  1419. pskHalfSphere, pskConeTop: result := ComputeEllipse(FOrigin, FXAxis, FYAxis);
  1420. pskConeSide: result := PointsF([FOrigin - (FYAxis-FOrigin), FYAxis + (FXAxis-FOrigin), FYAxis - (FXAxis-FOrigin)]);
  1421. else
  1422. begin
  1423. box := GetAffineBox(AffineMatrixIdentity, true);
  1424. result := box.AsPolygon;
  1425. end;
  1426. end;
  1427. end;
  1428. function TPhongShape.AllowShearTransform: boolean;
  1429. begin
  1430. Result:= false;
  1431. end;
  1432. constructor TPhongShape.Create(AContainer: TVectorOriginal);
  1433. begin
  1434. inherited Create(AContainer);
  1435. FShapeKind:= pskRectangle;
  1436. FLightPosition := PointF(0,0);
  1437. FShapeAltitudePercent:= DefaultPhongShapeAltitudePercent;
  1438. FBorderSizePercent:= DefaultPhongBorderSizePercent;
  1439. end;
  1440. destructor TPhongShape.Destroy;
  1441. begin
  1442. inherited Destroy;
  1443. end;
  1444. function TPhongShape.GetCornerPositition: single;
  1445. begin
  1446. if ShapeKind in [pskHalfSphere,pskConeTop] then
  1447. result := sqrt(2)/2
  1448. else
  1449. result := 1;
  1450. end;
  1451. class function TPhongShape.Fields: TVectorShapeFields;
  1452. begin
  1453. Result:= [vsfBackFill];
  1454. end;
  1455. class function TPhongShape.PreferPixelCentered: boolean;
  1456. begin
  1457. Result:= false;
  1458. end;
  1459. function TPhongShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  1460. var
  1461. u, v: TPointF;
  1462. rx, ry: Single;
  1463. p: TBGRAPath;
  1464. begin
  1465. rx := Width; ry := Height;
  1466. case ShapeKind of
  1467. pskHalfSphere, pskConeTop:
  1468. if rx <> ry then
  1469. result := AContent.AppendEllipse(Origin, PointF(rx, ry))
  1470. else result := AContent.AppendCircle(Origin, rx);
  1471. pskConeSide: begin
  1472. p := TBGRAPath.Create;
  1473. p.moveTo(Origin.x, origin.y - ry);
  1474. p.lineTo(Origin.x + rx, Origin.y + ry);
  1475. p.lineTo(Origin.x - rx, Origin.y + ry);
  1476. result := AContent.AppendPath(p);
  1477. p.Free;
  1478. end
  1479. else {pskRectangle, pskRoundRectangle, pskHorizCylinder, pskVertCylinder}
  1480. result := AContent.AppendRect(Origin.x - rx, Origin.y - ry, rx*2, ry*2);
  1481. end;
  1482. if (XAxis.y <> 0) or (YAxis.x <> 0) then
  1483. begin
  1484. u := XAxis - Origin;
  1485. if rx > 0 then u *= (1/rx);
  1486. v := YAxis - Origin;
  1487. if ry > 0 then v *= (1/ry);
  1488. result.matrix[cuPixel] := AffineMatrixTranslation(Origin.X, Origin.Y) *
  1489. AffineMatrix(u, v, PointF(0, 0)) *
  1490. AffineMatrixTranslation(-Origin.X, -Origin.Y);
  1491. end;
  1492. result.strokeNone;
  1493. ApplyFillStyleToSVG(result, ADefs);
  1494. end;
  1495. function TPhongShape.GetAlignBounds(const ALayoutRect: TRect;
  1496. const AMatrix: TAffineMatrix): TRectF;
  1497. var
  1498. m: TAffineMatrix;
  1499. pts: ArrayOfTPointF;
  1500. i: Integer;
  1501. procedure IncludePoint(const APoint: TPointF);
  1502. begin
  1503. if APoint.x < result.Left then result.Left := APoint.x else
  1504. if APoint.x > result.Right then result.Right := APoint.x;
  1505. if APoint.y < result.Top then result.Top := APoint.y else
  1506. if APoint.y > result.Bottom then result.Bottom := APoint.y;
  1507. end;
  1508. begin
  1509. m:= AffineMatrixTranslation(0.5,0.5)*MatrixForPixelCentered(AMatrix);
  1510. if ShapeKind in[pskHalfSphere,pskConeTop] then
  1511. begin
  1512. pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
  1513. if pts = nil then exit(EmptyRectF);
  1514. result.TopLeft := pts[0];
  1515. result.BottomRight := pts[0];
  1516. for i := 0 to high(pts) do IncludePoint(pts[i]);
  1517. IncludePoint(m*XAxis);
  1518. IncludePoint(m*YAxis);
  1519. IncludePoint(m*(Origin-(XAxis-Origin)));
  1520. IncludePoint(m*(Origin-(YAxis-Origin)));
  1521. end else
  1522. if ShapeKind = pskConeSide then
  1523. begin
  1524. result.TopLeft := m*Origin;
  1525. result.BottomRight := m*Origin;
  1526. IncludePoint(m*(XAxis+(YAxis-Origin)));
  1527. IncludePoint(m*(Origin-(XAxis-Origin)+(YAxis-Origin)));
  1528. IncludePoint(m*(Origin-(YAxis-Origin)));
  1529. end else
  1530. result := inherited GetAlignBounds(ALayoutRect,AMatrix);
  1531. end;
  1532. procedure TPhongShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
  1533. var
  1534. idxLight: Integer;
  1535. begin
  1536. inherited ConfigureCustomEditor(AEditor);
  1537. idxLight := AEditor.AddPoint(FLightPosition, @OnMoveLightPos, true);
  1538. if AEditor is TVectorOriginalEditor then
  1539. TVectorOriginalEditor(AEditor).AddLabel(idxLight, rsLightPosition, taCenter, tlTop);
  1540. end;
  1541. procedure TPhongShape.MouseDown(RightButton: boolean; ClickCount: integer; Shift: TShiftState; X,
  1542. Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  1543. begin
  1544. inherited MouseDown(RightButton, ClickCount, Shift, X, Y, ACursor, AHandled);
  1545. if not AHandled then
  1546. begin
  1547. if RightButton then
  1548. begin
  1549. LightPosition := PointF(x,y);
  1550. AHandled := true;
  1551. end;
  1552. end;
  1553. end;
  1554. procedure TPhongShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  1555. begin
  1556. BeginUpdate;
  1557. inherited LoadFromStorage(AStorage);
  1558. LightPosition := AStorage.PointF['light-pos'];
  1559. if isEmptyPointF(LightPosition) then LightPosition := PointF(0,0);
  1560. case AStorage.RawString['shape-kind'] of
  1561. 'round-rectangle': ShapeKind:= pskRoundRectangle;
  1562. 'half-sphere': ShapeKind := pskHalfSphere;
  1563. 'cone-top': ShapeKind := pskConeTop;
  1564. 'cone-side': ShapeKind := pskConeSide;
  1565. 'horizontal-cylinder': ShapeKind := pskHorizCylinder;
  1566. 'vertical-cylinder': ShapeKind := pskVertCylinder;
  1567. else
  1568. {'rectangle'} ShapeKind:= pskRectangle;
  1569. end;
  1570. ShapeAltitudePercent := AStorage.FloatDef['shape-altitude-percent', DefaultPhongShapeAltitudePercent];
  1571. if ShapeKind in[pskRectangle,pskRoundRectangle] then
  1572. BorderSizePercent := AStorage.FloatDef['border-size-percent', DefaultPhongBorderSizePercent]
  1573. else
  1574. BorderSizePercent := DefaultPhongBorderSizePercent;
  1575. EndUpdate;
  1576. end;
  1577. procedure TPhongShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  1578. begin
  1579. inherited SaveToStorage(AStorage);
  1580. AStorage.PointF['light-pos'] := LightPosition;
  1581. case ShapeKind of
  1582. pskRectangle: AStorage.RawString['shape-kind'] := 'rectangle';
  1583. pskRoundRectangle: AStorage.RawString['shape-kind'] := 'round-rectangle';
  1584. pskHalfSphere: AStorage.RawString['shape-kind'] := 'half-sphere';
  1585. pskConeTop: AStorage.RawString['shape-kind'] := 'cone-top';
  1586. pskConeSide: AStorage.RawString['shape-kind'] := 'cone-side';
  1587. pskHorizCylinder: AStorage.RawString['shape-kind'] := 'horizontal-cylinder';
  1588. pskVertCylinder: AStorage.RawString['shape-kind'] := 'vertical-cylinder';
  1589. end;
  1590. AStorage.Float['shape-altitude-percent'] := ShapeAltitudePercent;
  1591. if ShapeKind in[pskRectangle,pskRoundRectangle] then
  1592. AStorage.Float['border-size-percent'] := FBorderSizePercent;
  1593. end;
  1594. procedure TPhongShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
  1595. ADraft: boolean);
  1596. var
  1597. ab,abRaster: TAffineBox;
  1598. mapWidth,mapHeight: integer;
  1599. shader: TPhongShading;
  1600. approxFactor,borderSize: single;
  1601. m,mInv: TAffineMatrix;
  1602. h, lightPosZ: single;
  1603. map,raster: TBGRABitmap;
  1604. u,v,lightPosF: TPointF;
  1605. scan: TBGRACustomScanner;
  1606. rectRenderF,rectRasterF: TRectF;
  1607. rectRender,rectRaster, prevClip: TRect;
  1608. begin
  1609. if not GetBackVisible then exit;
  1610. //determine final render bounds
  1611. rectRenderF := GetRenderBounds(InfiniteRect,AMatrix);
  1612. if IsEmptyRectF(rectRenderF) then exit;
  1613. rectRender := rect(floor(rectRenderF.Left),floor(rectRenderF.Top),ceil(rectRenderF.Right),ceil(rectRenderF.Bottom));
  1614. rectRender.Intersect(ADest.ClipRect);
  1615. if IsRectEmpty(rectRender) then exit;
  1616. //determine map size before transform
  1617. ab := GetAffineBox(AMatrix, false);
  1618. if (ab.Width = 0) or (ab.Height = 0) then exit;
  1619. if ab.Width > ab.Height then
  1620. begin
  1621. mapWidth := ceil(ab.Width);
  1622. mapHeight := ceil(ab.Surface/ab.Width);
  1623. end else
  1624. begin
  1625. mapWidth := ceil(ab.Surface/ab.Height);
  1626. mapHeight := ceil(ab.Height);
  1627. end;
  1628. approxFactor := 1;
  1629. if ADraft then
  1630. begin
  1631. if mapWidth > 100 then approxFactor:= min(approxFactor, 100/mapWidth);
  1632. if mapHeight > 100 then approxFactor:= min(approxFactor, 100/mapHeight);
  1633. end else
  1634. begin
  1635. if mapWidth > 800 then approxFactor:= min(approxFactor, 800/mapWidth);
  1636. if mapHeight > 800 then approxFactor:= min(approxFactor, 800/mapHeight);
  1637. end;
  1638. mapWidth:= ceil(mapWidth*approxFactor);
  1639. mapHeight:= ceil(mapHeight*approxFactor);
  1640. //determine map transform
  1641. u := (ab.TopRight-ab.TopLeft)*(1/ab.Width);
  1642. v := (ab.BottomLeft-ab.TopLeft)*(1/ab.Height);
  1643. m := AffineMatrix(u,v,ab.TopLeft)*AffineMatrixScale(ab.Width/mapWidth,ab.Height/mapHeight);
  1644. borderSize := FBorderSizePercent/200*min(ab.Width,ab.Height);
  1645. if not IsAffineMatrixInversible(m) then exit;
  1646. mInv := AffineMatrixInverse(m);
  1647. try
  1648. //create height map
  1649. map := nil;
  1650. case ShapeKind of
  1651. pskRoundRectangle: begin
  1652. map := CreateRoundRectanglePreciseMap(mapWidth,mapHeight,
  1653. round(borderSize*mapWidth/ab.Width),
  1654. round(borderSize*mapHeight/ab.Height),[]);
  1655. h := FShapeAltitudePercent*approxFactor;
  1656. end;
  1657. pskHalfSphere: begin
  1658. map := CreateSpherePreciseMap(mapWidth,mapHeight);
  1659. h := FShapeAltitudePercent/100*sqrt(mapWidth*mapHeight);
  1660. end;
  1661. pskConeTop: begin
  1662. map := CreateConePreciseMap(mapWidth,mapHeight);
  1663. h := FShapeAltitudePercent/100*sqrt(mapWidth*mapHeight);
  1664. end;
  1665. pskConeSide: begin
  1666. map := CreateVerticalConePreciseMap(mapWidth,mapHeight);
  1667. h := FShapeAltitudePercent/100*mapWidth;
  1668. end;
  1669. pskHorizCylinder: begin
  1670. map := CreateHorizontalCylinderPreciseMap(mapWidth,mapHeight);
  1671. h := FShapeAltitudePercent/100*mapHeight;
  1672. end;
  1673. pskVertCylinder: begin
  1674. map := CreateVerticalCylinderPreciseMap(mapWidth,mapHeight);
  1675. h := FShapeAltitudePercent/100*mapWidth;
  1676. end;
  1677. else
  1678. {pskRectangle: }begin
  1679. map := CreateRectanglePreciseMap(mapWidth,mapHeight,
  1680. round(borderSize*mapWidth/ab.Width),
  1681. round(borderSize*mapHeight/ab.Height),[]);
  1682. h := FShapeAltitudePercent*approxFactor;
  1683. end;
  1684. end;
  1685. abRaster := mInv*TAffineBox.AffineBox(rectRenderF);
  1686. rectRasterF := abRaster.RectBoundsF;
  1687. rectRaster := rect(floor(rectRasterF.Left),floor(rectRasterF.Top),ceil(rectRasterF.Right),ceil(rectRasterF.Bottom));
  1688. raster := nil;
  1689. shader := nil;
  1690. if IntersectRect(rectRaster, rectRaster, rect(0,0,mapWidth,mapHeight)) then
  1691. try
  1692. shader:= TPhongShading.Create;
  1693. shader.AmbientFactor := 0.5;
  1694. shader.NegativeDiffusionFactor := 0.15;
  1695. lightPosF := AffineMatrixTranslation(-rectRaster.Left,-rectRaster.Top)
  1696. *mInv*AMatrix*FLightPosition;
  1697. lightPosZ := 100*Power(approxFactor,1.1);
  1698. if h*3/2 > lightPosZ then lightposZ := h*3/2;
  1699. shader.LightPosition3D := Point3D(lightPosF.x,lightPosF.y,lightPosZ);
  1700. raster := TBGRABitmap.Create(rectRaster.Width,rectRaster.Height);
  1701. if BackFill.FillType = vftSolid then
  1702. shader.Draw(raster,map,h,-rectRaster.Left,-rectRaster.Top,BackFill.SolidColor)
  1703. else
  1704. begin
  1705. scan := BackFill.CreateScanner(AffineMatrixTranslation(-rectRaster.left,-rectRaster.top)*mInv*AMatrix,ADraft);
  1706. shader.DrawScan(raster,map,h,-rectRaster.Left,-rectRaster.Top,scan);
  1707. scan.Free;
  1708. end;
  1709. prevClip := ADest.ClipRect;
  1710. ADest.ClipRect := rectRender;
  1711. if ADraft then
  1712. ADest.PutImageAffine(m*AffineMatrixTranslation(rectRaster.Left,rectRaster.Top),raster,rfBox,dmDrawWithTransparency)
  1713. else
  1714. ADest.PutImageAffine(m*AffineMatrixTranslation(rectRaster.Left,rectRaster.Top),raster,rfHalfCosine,dmDrawWithTransparency);
  1715. ADest.ClipRect := prevClip;
  1716. finally
  1717. raster.Free;
  1718. shader.Free;
  1719. end;
  1720. finally
  1721. map.Free;
  1722. end;
  1723. end;
  1724. function TPhongShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix;
  1725. AOptions: TRenderBoundsOptions): TRectF;
  1726. begin
  1727. if not (GetBackVisible or (rboAssumeBackFill in AOptions)) then
  1728. result:= EmptyRectF
  1729. else
  1730. result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
  1731. end;
  1732. function TPhongShape.PointInShape(APoint: TPointF): boolean;
  1733. var
  1734. pts: ArrayOfTPointF;
  1735. begin
  1736. if not GetBackVisible then exit(false);
  1737. pts := GetEnvelope;
  1738. result := IsPointInPolygon(pts, APoint, true);
  1739. end;
  1740. function TPhongShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1741. var
  1742. pts: ArrayOfTPointF;
  1743. begin
  1744. if GetBackVisible then
  1745. begin
  1746. pts := ComputeStrokeEnvelope(GetEnvelope, true, ARadius*2);
  1747. result:= IsPointInPolygon(pts, APoint, true);
  1748. end
  1749. else result := false;
  1750. end;
  1751. function TPhongShape.PointInBack(APoint: TPointF): boolean;
  1752. var
  1753. scan: TBGRACustomScanner;
  1754. begin
  1755. result := PointInShape(APoint);
  1756. if result and (BackFill.FillType = vftTexture) then
  1757. begin
  1758. scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
  1759. if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
  1760. scan.Free;
  1761. end;
  1762. end;
  1763. function TPhongShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  1764. var
  1765. ab: TAffineBox;
  1766. begin
  1767. if not GetBackVisible then exit(false);
  1768. ab := GetAffineBox(AMatrix, true);
  1769. result := ab.Surface > 320*240;
  1770. end;
  1771. function TPhongShape.GetGenericCost: integer;
  1772. begin
  1773. Result:= 10;
  1774. end;
  1775. procedure TPhongShape.Transform(const AMatrix: TAffineMatrix);
  1776. begin
  1777. BeginUpdate(TPhongShapeDiff);
  1778. LightPosition := AMatrix*LightPosition;
  1779. inherited Transform(AMatrix);
  1780. EndUpdate;
  1781. end;
  1782. class function TPhongShape.StorageClassName: RawByteString;
  1783. begin
  1784. result := 'phong';
  1785. end;
  1786. initialization
  1787. RegisterVectorShape(TRectShape);
  1788. RegisterVectorShape(TEllipseShape);
  1789. RegisterVectorShape(TPhongShape);
  1790. end.