GLCoordinates.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLCoordinates;
  5. (* Coordinate related classes *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. System.Classes,
  10. System.SysUtils,
  11. GLVectorGeometry,
  12. GLVectorTypes,
  13. OpenGLTokens,
  14. GLBaseClasses;
  15. type
  16. (* Identifie le type de données stockées au sein d'un TGLCustomCoordinates.
  17. csPoint2D : a simple 2D point (Z=0, W=0)
  18. csPoint : un point (W=1)
  19. csVector : un vecteur (W=0)
  20. csUnknown : aucune contrainte *)
  21. TGLCoordinatesStyle = (csPoint2D, csPoint, csVector, csUnknown);
  22. (* Stores and homogeneous vector.
  23. This class is basicly a container for a TVector, allowing proper use of
  24. delphi property editors and editing in the IDE. Vector/Coordinates
  25. manipulation methods are only minimal.
  26. Handles dynamic default values to save resource file space. *)
  27. TGLCustomCoordinates = class(TGLUpdateAbleObject)
  28. private
  29. FCoords: TVector;
  30. FStyle: TGLCoordinatesStyle; // NOT Persistent
  31. FPDefaultCoords: PVector;
  32. procedure SetAsPoint2D(const Value: TVector2f);
  33. procedure SetAsVector(const Value: TVector);
  34. procedure SetAsAffineVector(const Value: TAffineVector);
  35. function GetAsAffineVector: TAffineVector; inline;
  36. function GetAsPoint2D: TVector2f;
  37. function GetAsString: String;
  38. function GetCoordinate(const AIndex: Integer): TGLFloat; inline;
  39. procedure SetCoordinate(const AIndex: Integer; const AValue: TGLFloat); inline;
  40. function GetDirectCoordinate(const Index: Integer): TGLFloat; inline;
  41. procedure SetDirectCoordinate(const Index: Integer; const AValue: TGLFloat);
  42. protected
  43. procedure SetDirectVector(const V: TVector); inline;
  44. procedure DefineProperties(Filer: TFiler); override;
  45. procedure ReadData(Stream: TStream);
  46. procedure WriteData(Stream: TStream);
  47. public
  48. constructor CreateInitialized(AOwner: TPersistent; const AValue: TVector;
  49. const AStyle: TGLCoordinatesStyle = CsUnknown);
  50. destructor Destroy; override;
  51. procedure Assign(Source: TPersistent); override;
  52. procedure WriteToFiler(Writer: TWriter);
  53. procedure ReadFromFiler(Reader: TReader);
  54. procedure Initialize(const Value: TVector);
  55. procedure NotifyChange(Sender: TObject); override;
  56. (* Identifies the coordinates styles.
  57. The property is NOT persistent, csUnknown by default, and should be
  58. managed by owner object only (internally).
  59. It is used by the TGLCustomCoordinates for internal "assertion" checks
  60. to detect "misuses" or "misunderstandings" of what the homogeneous
  61. coordinates system implies. *)
  62. property Style: TGLCoordinatesStyle read FStyle write FStyle;
  63. procedure Translate(const TranslationVector: TVector); overload;
  64. procedure Translate(const TranslationVector: TAffineVector); overload;
  65. procedure AddScaledVector(const Factor: Single; const TranslationVector: TVector); overload;
  66. procedure AddScaledVector(const Factor: Single; const TranslationVector: TAffineVector); overload;
  67. procedure Rotate(const AnAxis: TAffineVector; AnAngle: Single); overload;
  68. procedure Rotate(const AnAxis: TVector; AnAngle: Single); overload;
  69. procedure Normalize; inline;
  70. procedure Invert;
  71. procedure Scale(Factor: Single);
  72. function VectorLength: TGLFloat;
  73. function VectorNorm: TGLFloat;
  74. function MaxXYZ: Single;
  75. function Equals(const AVector: TVector): Boolean; reintroduce;
  76. procedure SetVector(const X, Y: Single; Z: Single = 0); overload;
  77. procedure SetVector(const X, Y, Z, W: Single); overload;
  78. procedure SetVector(const V: TAffineVector); overload;
  79. procedure SetVector(const V: TVector); overload;
  80. procedure SetPoint(const X, Y, Z: Single); overload;
  81. procedure SetPoint(const V: TAffineVector); overload;
  82. procedure SetPoint(const V: TVector); overload;
  83. procedure SetPoint2D(const X, Y: Single); overload;
  84. procedure SetPoint2D(const Vector: TAffineVector); overload;
  85. procedure SetPoint2D(const Vector: TVector); overload;
  86. procedure SetPoint2D(const Vector: TVector2f); overload;
  87. procedure SetToZero;
  88. function AsAddress: PGLFloat; inline;
  89. (* The coordinates viewed as a vector.
  90. Assigning a value to this property will trigger notification events,
  91. if you don't want so, use DirectVector instead. *)
  92. property AsVector: TVector read FCoords write SetAsVector;
  93. (* The coordinates viewed as an affine vector.
  94. Assigning a value to this property will trigger notification events,
  95. if you don't want so, use DirectVector instead.
  96. The W component is automatically adjustes depending on style. *)
  97. property AsAffineVector: TAffineVector read GetAsAffineVector write SetAsAffineVector;
  98. (* The coordinates viewed as a 2D point.
  99. Assigning a value to this property will trigger notification events,
  100. if you don't want so, use DirectVector instead. *)
  101. property AsPoint2D: TVector2f read GetAsPoint2D write SetAsPoint2D;
  102. property X: TGLFloat index 0 read GetCoordinate write SetCoordinate;
  103. property Y: TGLFloat index 1 read GetCoordinate write SetCoordinate;
  104. property Z: TGLFloat index 2 read GetCoordinate write SetCoordinate;
  105. property W: TGLFloat index 3 read GetCoordinate write SetCoordinate;
  106. property Coordinate[const AIndex: Integer]: TGLFloat read GetCoordinate write SetCoordinate; default;
  107. // The coordinates, in-between brackets, separated by semi-colons.
  108. property AsString: String read GetAsString;
  109. // Similar to AsVector but does not trigger notification events
  110. property DirectVector: TVector read FCoords write SetDirectVector;
  111. property DirectX: TGLFloat index 0 read GetDirectCoordinate write SetDirectCoordinate;
  112. property DirectY: TGLFloat index 1 read GetDirectCoordinate write SetDirectCoordinate;
  113. property DirectZ: TGLFloat index 2 read GetDirectCoordinate write SetDirectCoordinate;
  114. property DirectW: TGLFloat index 3 read GetDirectCoordinate write SetDirectCoordinate;
  115. end;
  116. // A TGLCustomCoordinates that publishes X, Y properties.
  117. TGLCoordinates2 = class(TGLCustomCoordinates)
  118. published
  119. property X stored False;
  120. property Y stored False;
  121. end;
  122. // A TGLCustomCoordinates that publishes X, Y, Z properties.
  123. TGLCoordinates3 = class(TGLCustomCoordinates)
  124. published
  125. property X stored False;
  126. property Y stored False;
  127. property Z stored False;
  128. end;
  129. // A TGLCustomCoordinates that publishes X, Y, Z, W properties.
  130. TGLCoordinates4 = class(TGLCustomCoordinates)
  131. published
  132. property X stored False;
  133. property Y stored False;
  134. property Z stored False;
  135. property W stored False;
  136. end;
  137. TGLCoordinates = TGLCoordinates3;
  138. (* Actually Sender should be TGLCustomCoordinates, but that would require
  139. changes in a some other GLScene units and some other projects that use
  140. TGLCoordinatesUpdateAbleComponent *)
  141. IGLCoordinatesUpdateAble = interface(IInterface)
  142. ['{ACB98D20-8905-43A7-AFA5-225CF5FA6FF5}']
  143. procedure CoordinateChanged(Sender: TGLCustomCoordinates);
  144. end;
  145. TGLCoordinatesUpdateAbleComponent = class(TGLUpdateAbleComponent, IGLCoordinatesUpdateAble)
  146. public
  147. procedure CoordinateChanged(Sender: TGLCustomCoordinates); virtual; abstract;
  148. end;
  149. var
  150. (* Specifies if TGLCustomCoordinates should allocate memory for
  151. their default values (ie. design-time) or not (run-time) *)
  152. VUseDefaultCoordinateSets: Boolean = False;
  153. //==================================================================
  154. implementation
  155. //==================================================================
  156. const
  157. csVectorHelp = 'If you are getting assertions here, consider using the SetPoint procedure';
  158. csPointHelp = 'If you are getting assertions here, consider using the SetVector procedure';
  159. csPoint2DHelp = 'If you are getting assertions here, consider using one of the SetVector or SetPoint procedures';
  160. // ------------------
  161. // ------------------ TGLCustomCoordinates ------------------
  162. // ------------------
  163. constructor TGLCustomCoordinates.CreateInitialized(AOwner: TPersistent;
  164. const AValue: TVector; const AStyle: TGLCoordinatesStyle = CsUnknown);
  165. begin
  166. Create(AOwner);
  167. Initialize(AValue);
  168. FStyle := AStyle;
  169. end;
  170. destructor TGLCustomCoordinates.Destroy;
  171. begin
  172. if Assigned(FPDefaultCoords) then
  173. Dispose(FPDefaultCoords);
  174. inherited;
  175. end;
  176. procedure TGLCustomCoordinates.Initialize(const Value: TVector);
  177. begin
  178. FCoords := Value;
  179. if VUseDefaultCoordinateSets then
  180. begin
  181. if not Assigned(FPDefaultCoords) then
  182. New(FPDefaultCoords);
  183. FPDefaultCoords^ := Value;
  184. end;
  185. end;
  186. procedure TGLCustomCoordinates.Assign(Source: TPersistent);
  187. begin
  188. if Source is TGLCustomCoordinates then
  189. FCoords := TGLCustomCoordinates(Source).FCoords
  190. else
  191. inherited;
  192. end;
  193. procedure TGLCustomCoordinates.WriteToFiler(Writer: TWriter);
  194. var
  195. WriteCoords: Boolean;
  196. begin
  197. with Writer do
  198. begin
  199. WriteInteger(0); // Archive Version 0
  200. if VUseDefaultCoordinateSets then
  201. WriteCoords := not VectorEquals(FPDefaultCoords^, FCoords)
  202. else
  203. WriteCoords := True;
  204. WriteBoolean(WriteCoords);
  205. if WriteCoords then
  206. Write(FCoords.X, SizeOf(FCoords));
  207. end;
  208. end;
  209. procedure TGLCustomCoordinates.ReadFromFiler(Reader: TReader);
  210. var
  211. N: Integer;
  212. begin
  213. with Reader do
  214. begin
  215. ReadInteger; // Ignore ArchiveVersion
  216. if ReadBoolean then
  217. begin
  218. N := SizeOf(FCoords);
  219. Assert(N = 4 * SizeOf(Single));
  220. Read(FCoords.X, N);
  221. end
  222. else if Assigned(FPDefaultCoords) then
  223. FCoords := FPDefaultCoords^;
  224. end;
  225. end;
  226. procedure TGLCustomCoordinates.DefineProperties(Filer: TFiler);
  227. begin
  228. inherited;
  229. Filer.DefineBinaryProperty('Coordinates', ReadData, WriteData,
  230. not(Assigned(FPDefaultCoords) and VectorEquals(FPDefaultCoords^, FCoords)));
  231. end;
  232. procedure TGLCustomCoordinates.ReadData(Stream: TStream);
  233. begin
  234. Stream.Read(FCoords, SizeOf(FCoords));
  235. end;
  236. procedure TGLCustomCoordinates.WriteData(Stream: TStream);
  237. begin
  238. Stream.Write(FCoords, SizeOf(FCoords));
  239. end;
  240. procedure TGLCustomCoordinates.NotifyChange(Sender: TObject);
  241. var
  242. Int: IGLCoordinatesUpdateAble;
  243. begin
  244. if Supports(Owner, IGLCoordinatesUpdateAble, Int) then
  245. Int.CoordinateChanged(TGLCoordinates(Self));
  246. inherited NotifyChange(Sender);
  247. end;
  248. procedure TGLCustomCoordinates.Translate(const TranslationVector: TVector);
  249. begin
  250. FCoords.X := FCoords.X + TranslationVector.X;
  251. FCoords.Y := FCoords.Y + TranslationVector.Y;
  252. FCoords.Z := FCoords.Z + TranslationVector.Z;
  253. NotifyChange(Self);
  254. end;
  255. procedure TGLCustomCoordinates.Translate(const TranslationVector
  256. : TAffineVector);
  257. begin
  258. FCoords.X := FCoords.X + TranslationVector.X;
  259. FCoords.Y := FCoords.Y + TranslationVector.Y;
  260. FCoords.Z := FCoords.Z + TranslationVector.Z;
  261. NotifyChange(Self);
  262. end;
  263. procedure TGLCustomCoordinates.AddScaledVector(const Factor: Single;
  264. const TranslationVector: TVector);
  265. var
  266. F: Single;
  267. begin
  268. F := Factor;
  269. CombineVector(FCoords, TranslationVector, F);
  270. NotifyChange(Self);
  271. end;
  272. procedure TGLCustomCoordinates.AddScaledVector(const Factor: Single;
  273. const TranslationVector: TAffineVector);
  274. var
  275. F: Single;
  276. begin
  277. F := Factor;
  278. CombineVector(FCoords, TranslationVector, F);
  279. NotifyChange(Self);
  280. end;
  281. procedure TGLCustomCoordinates.Rotate(const AnAxis: TAffineVector;
  282. AnAngle: Single);
  283. begin
  284. RotateVector(FCoords, AnAxis, AnAngle);
  285. NotifyChange(Self);
  286. end;
  287. procedure TGLCustomCoordinates.Rotate(const AnAxis: TVector; AnAngle: Single);
  288. begin
  289. RotateVector(FCoords, AnAxis, AnAngle);
  290. NotifyChange(Self);
  291. end;
  292. procedure TGLCustomCoordinates.Normalize;
  293. begin
  294. NormalizeVector(FCoords);
  295. NotifyChange(Self);
  296. end;
  297. procedure TGLCustomCoordinates.Invert;
  298. begin
  299. NegateVector(FCoords);
  300. NotifyChange(Self);
  301. end;
  302. procedure TGLCustomCoordinates.Scale(Factor: Single);
  303. begin
  304. ScaleVector(PAffineVector(@FCoords)^, Factor);
  305. NotifyChange(Self);
  306. end;
  307. function TGLCustomCoordinates.VectorLength: TGLFloat;
  308. begin
  309. Result := GLVectorGeometry.VectorLength(FCoords);
  310. end;
  311. function TGLCustomCoordinates.VectorNorm: TGLFloat;
  312. begin
  313. Result := GLVectorGeometry.VectorNorm(FCoords);
  314. end;
  315. function TGLCustomCoordinates.MaxXYZ: Single;
  316. begin
  317. Result := MaxXYZComponent(FCoords);
  318. end;
  319. function TGLCustomCoordinates.Equals(const AVector: TVector): Boolean;
  320. begin
  321. Result := VectorEquals(FCoords, AVector);
  322. end;
  323. procedure TGLCustomCoordinates.SetVector(const X, Y: Single; Z: Single = 0);
  324. begin
  325. Assert(FStyle = csVector, csVectorHelp);
  326. GLVectorGeometry.SetVector(FCoords, X, Y, Z);
  327. NotifyChange(Self);
  328. end;
  329. procedure TGLCustomCoordinates.SetVector(const V: TAffineVector);
  330. begin
  331. Assert(FStyle = csVector, csVectorHelp);
  332. GLVectorGeometry.SetVector(FCoords, V);
  333. NotifyChange(Self);
  334. end;
  335. procedure TGLCustomCoordinates.SetVector(const V: TVector);
  336. begin
  337. Assert(FStyle = csVector, csVectorHelp);
  338. GLVectorGeometry.SetVector(FCoords, V);
  339. NotifyChange(Self);
  340. end;
  341. procedure TGLCustomCoordinates.SetVector(const X, Y, Z, W: Single);
  342. begin
  343. Assert(FStyle = csVector, csVectorHelp);
  344. GLVectorGeometry.SetVector(FCoords, X, Y, Z, W);
  345. NotifyChange(Self);
  346. end;
  347. procedure TGLCustomCoordinates.SetDirectCoordinate(const Index: Integer;
  348. const AValue: TGLFloat);
  349. begin
  350. FCoords.V[index] := AValue;
  351. end;
  352. procedure TGLCustomCoordinates.SetDirectVector(const V: TVector);
  353. begin
  354. FCoords.X := V.X;
  355. FCoords.Y := V.Y;
  356. FCoords.Z := V.Z;
  357. FCoords.W := V.W;
  358. end;
  359. procedure TGLCustomCoordinates.SetToZero;
  360. begin
  361. FCoords.X := 0;
  362. FCoords.Y := 0;
  363. FCoords.Z := 0;
  364. if FStyle = CsPoint then
  365. FCoords.W := 1
  366. else
  367. FCoords.W := 0;
  368. NotifyChange(Self);
  369. end;
  370. procedure TGLCustomCoordinates.SetPoint(const X, Y, Z: Single);
  371. begin
  372. Assert(FStyle = CsPoint, CsPointHelp);
  373. MakePoint(FCoords, X, Y, Z);
  374. NotifyChange(Self);
  375. end;
  376. procedure TGLCustomCoordinates.SetPoint(const V: TAffineVector);
  377. begin
  378. Assert(FStyle = CsPoint, CsPointHelp);
  379. MakePoint(FCoords, V);
  380. NotifyChange(Self);
  381. end;
  382. procedure TGLCustomCoordinates.SetPoint(const V: TVector);
  383. begin
  384. Assert(FStyle = CsPoint, CsPointHelp);
  385. MakePoint(FCoords, V);
  386. NotifyChange(Self);
  387. end;
  388. procedure TGLCustomCoordinates.SetPoint2D(const X, Y: Single);
  389. begin
  390. Assert(FStyle = CsPoint2D, CsPoint2DHelp);
  391. GLVectorGeometry.MakeVector(FCoords, X, Y, 0);
  392. NotifyChange(Self);
  393. end;
  394. procedure TGLCustomCoordinates.SetPoint2D(const Vector: TAffineVector);
  395. begin
  396. Assert(FStyle = CsPoint2D, CsPoint2DHelp);
  397. MakeVector(FCoords, Vector);
  398. NotifyChange(Self);
  399. end;
  400. procedure TGLCustomCoordinates.SetPoint2D(const Vector: TVector);
  401. begin
  402. Assert(FStyle = CsPoint2D, CsPoint2DHelp);
  403. MakeVector(FCoords, Vector);
  404. NotifyChange(Self);
  405. end;
  406. procedure TGLCustomCoordinates.SetPoint2D(const Vector: TVector2f);
  407. begin
  408. Assert(FStyle = CsPoint2D, CsPoint2DHelp);
  409. MakeVector(FCoords, Vector.X, Vector.Y, 0);
  410. NotifyChange(Self);
  411. end;
  412. function TGLCustomCoordinates.AsAddress: PGLFloat;
  413. begin
  414. Result := @FCoords;
  415. end;
  416. procedure TGLCustomCoordinates.SetAsVector(const Value: TVector);
  417. begin
  418. FCoords := Value;
  419. case FStyle of
  420. CsPoint2D:
  421. begin
  422. FCoords.Z := 0;
  423. FCoords.W := 0;
  424. end;
  425. CsPoint:
  426. FCoords.W := 1;
  427. CsVector:
  428. FCoords.W := 0;
  429. else
  430. Assert(False);
  431. end;
  432. NotifyChange(Self);
  433. end;
  434. procedure TGLCustomCoordinates.SetAsAffineVector(const Value: TAffineVector);
  435. begin
  436. case FStyle of
  437. CsPoint2D:
  438. MakeVector(FCoords, Value);
  439. CsPoint:
  440. MakePoint(FCoords, Value);
  441. CsVector:
  442. MakeVector(FCoords, Value);
  443. else
  444. Assert(False);
  445. end;
  446. NotifyChange(Self);
  447. end;
  448. procedure TGLCustomCoordinates.SetAsPoint2D(const Value: TVector2f);
  449. begin
  450. case FStyle of
  451. CsPoint2D, CsPoint, CsVector:
  452. begin
  453. FCoords.X := Value.X;
  454. FCoords.Y := Value.Y;
  455. FCoords.Z := 0;
  456. FCoords.W := 0;
  457. end;
  458. else
  459. Assert(False);
  460. end;
  461. NotifyChange(Self);
  462. end;
  463. function TGLCustomCoordinates.GetAsAffineVector: TAffineVector;
  464. begin
  465. GLVectorGeometry.SetVector(Result, FCoords);
  466. end;
  467. function TGLCustomCoordinates.GetAsPoint2D: TVector2f;
  468. begin
  469. Result.X := FCoords.X;
  470. Result.Y := FCoords.Y;
  471. end;
  472. procedure TGLCustomCoordinates.SetCoordinate(const AIndex: Integer;
  473. const AValue: TGLFloat);
  474. begin
  475. FCoords.V[AIndex] := AValue;
  476. NotifyChange(Self);
  477. end;
  478. function TGLCustomCoordinates.GetCoordinate(const AIndex: Integer): TGLFloat;
  479. begin
  480. Result := FCoords.V[AIndex];
  481. end;
  482. function TGLCustomCoordinates.GetDirectCoordinate(
  483. const Index: Integer): TGLFloat;
  484. begin
  485. Result := FCoords.V[index]
  486. end;
  487. function TGLCustomCoordinates.GetAsString: String;
  488. begin
  489. case Style of
  490. CsPoint2D:
  491. Result := Format('(%g; %g)', [FCoords.X, FCoords.Y]);
  492. CsPoint:
  493. Result := Format('(%g; %g; %g)', [FCoords.X, FCoords.Y, FCoords.Z]);
  494. CsVector:
  495. Result := Format('(%g; %g; %g; %g)', [FCoords.X, FCoords.Y, FCoords.Z,
  496. FCoords.W]);
  497. else
  498. Assert(False);
  499. end;
  500. end;
  501. //=====================================================================
  502. initialization
  503. //=====================================================================
  504. RegisterClasses([TGLCoordinates2, TGLCoordinates3, TGLCoordinates4]);
  505. end.