GLS.Utils.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLS.Utils;
  5. (* Miscellaneous support utilities & classes *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. System.Classes,
  10. System.SysUtils,
  11. System.UITypes,
  12. VCL.Graphics,
  13. VCL.Controls,
  14. VCL.Dialogs,
  15. VCL.ExtDlgs,
  16. GLCrossPlatform,
  17. GLS.Strings,
  18. GLVectorGeometry;
  19. type
  20. EGLUtilsException = class(Exception);
  21. TSqrt255Array = array[0..255] of Byte;
  22. PSqrt255Array = ^TSqrt255Array;
  23. // Copies the values of Source to Dest (converting word values to integer values)
  24. procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray; Count: Cardinal);
  25. // Round ups to the nearest power of two, value must be positive
  26. function RoundUpToPowerOf2(value: Integer): Integer;
  27. // Round down to the nearest power of two, value must be strictly positive
  28. function RoundDownToPowerOf2(value: Integer): Integer;
  29. // Returns True if value is a true power of two
  30. function IsPowerOf2(value: Integer): Boolean;
  31. // Read a CRLF terminated string from a stream. The CRLF is NOT in the returned string.
  32. function ReadCRLFString(aStream: TStream): String;
  33. // Write the string and a CRLF in the stream
  34. procedure WriteCRLFString(aStream: TStream; const aString: String);
  35. // Similar to SysUtils.TryStrToFloat, but ignores user's locale
  36. function TryStrToFloat(const strValue: string; var val: Extended): Boolean;
  37. // Similar to SysUtils.StrToFloatDef, but ignores user's locale
  38. function StrToFloatDef(const strValue: string; defValue: Extended = 0): Extended;
  39. // Converts a string into color
  40. function StringToColorAdvancedSafe(const Str: string; const Default: TColor): TColor;
  41. // Converts a string into color
  42. function TryStringToColorAdvanced(const Str: string; var OutColor: TColor): Boolean;
  43. // Converts a string into color
  44. function StringToColorAdvanced(const Str: string): TColor;
  45. (*Parses the next integer in the string.
  46. Initial non-numeric characters are skipper, p is altered, returns 0 if none
  47. found. '+' and '-' are acknowledged. *)
  48. function ParseInteger(var p: PChar): Integer;
  49. (* Parses the next integer in the string.
  50. Initial non-numeric characters are skipper, p is altered, returns 0 if none
  51. found. Both '.' and ',' are accepted as decimal separators. *)
  52. function ParseFloat(var p: PChar): Extended;
  53. //Saves ansistring "data" to "filename".
  54. procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
  55. //Returns the ansistring content of "filename".
  56. function LoadAnsiStringFromFile(const fileName: string): AnsiString;
  57. //Saves string "data" to "filename".
  58. procedure SaveStringToFile(const fileName: string; const data: String);
  59. //Returns the string content of "filename".
  60. function LoadStringFromFile(const fileName: string): String;
  61. //Saves component to a file.
  62. procedure SaveComponentToFile(const Component: TComponent; const FileName: string; const AsText: Boolean = True);
  63. // Loads component from a file.
  64. procedure LoadComponentFromFile(const Component: TComponent; const FileName: string; const AsText: Boolean = True);
  65. (* Returns the size of "filename".
  66. Returns 0 (zero) is file does not exists. *)
  67. function SizeOfFile(const fileName: string): Int64;
  68. // Returns a pointer to an array containing the results of "255*sqrt(i/255)".
  69. function GetSqrt255Array: PSqrt255Array;
  70. // Pops up a simple dialog with msg and an Ok button.
  71. procedure InformationDlg(const msg: string);
  72. (* Pops up a simple question dialog with msg and yes/no buttons.
  73. Returns True if answer was "yes". *)
  74. function QuestionDlg(const msg: string): Boolean;
  75. // Posp a simple dialog with a string input.
  76. function InputDlg(const aCaption, aPrompt, aDefault: string): string;
  77. // Pops up a simple save picture dialog.
  78. function SavePictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
  79. // Pops up a simple open picture dialog.
  80. function OpenPictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
  81. procedure SetGLSceneMediaDir();
  82. //------------------------------------------------------
  83. implementation
  84. //------------------------------------------------------
  85. var
  86. vSqrt255: TSqrt255Array;
  87. procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray; Count: Cardinal);
  88. var
  89. i: integer;
  90. begin
  91. for i := 0 to Count - 1 do
  92. Dest^[i] := Source^[i];
  93. end;
  94. function RoundUpToPowerOf2(value: Integer): Integer;
  95. begin
  96. Result := 1;
  97. while (Result < value) do
  98. Result := Result shl 1;
  99. end;
  100. function RoundDownToPowerOf2(value: Integer): Integer;
  101. begin
  102. if value > 0 then
  103. begin
  104. Result := 1 shl 30;
  105. while Result > value do
  106. Result := Result shr 1;
  107. end
  108. else
  109. Result := 1;
  110. end;
  111. function IsPowerOf2(value: Integer): Boolean;
  112. begin
  113. Result := (RoundUpToPowerOf2(value) = value);
  114. end;
  115. function ReadCRLFString(aStream: TStream): String;
  116. var
  117. c: Char;
  118. begin
  119. Result := '';
  120. while Copy(Result, Length(Result) - 1, 2) <> #13#10 do
  121. begin
  122. aStream.Read(c, 1);
  123. Result := Result + c;
  124. end;
  125. Result := Copy(Result, 1, Length(Result) - 2);
  126. end;
  127. procedure WriteCRLFString(aStream: TStream; const aString: String);
  128. const
  129. cCRLF: Integer = $0A0D;
  130. begin
  131. with aStream do
  132. begin
  133. Write(aString[1], Length(aString));
  134. Write(cCRLF, 2);
  135. end;
  136. end;
  137. function TryStrToFloat(const strValue: string; var val: Extended): Boolean;
  138. var
  139. i, j, divider, lLen, exponent: Integer;
  140. c: Char;
  141. v: Extended;
  142. begin
  143. if strValue = '' then
  144. begin
  145. Result := False;
  146. Exit;
  147. end
  148. else
  149. v := 0;
  150. lLen := Length(strValue);
  151. while (lLen > 0) and (strValue[lLen] = ' ') do
  152. Dec(lLen);
  153. divider := lLen + 1;
  154. exponent := 0;
  155. for i := 1 to lLen do
  156. begin
  157. c := strValue[i];
  158. case c of
  159. ' ': if v <> 0 then
  160. begin
  161. Result := False;
  162. Exit;
  163. end;
  164. '0'..'9': v := (v * 10) + Integer(c) - Integer('0');
  165. ',', '.':
  166. begin
  167. if (divider > lLen) then
  168. divider := i + 1
  169. else
  170. begin
  171. Result := False;
  172. Exit;
  173. end;
  174. end;
  175. '-', '+': if i > 1 then
  176. begin
  177. Result := False;
  178. Exit;
  179. end;
  180. 'e', 'E':
  181. begin
  182. if i + 1 > lLen then
  183. begin
  184. Result := False;
  185. Exit;
  186. end;
  187. for j := i + 1 to lLen do
  188. begin
  189. c := strValue[j];
  190. case c of
  191. '-', '+': if j <> i + 1 then
  192. begin
  193. Result := False;
  194. Exit;
  195. end;
  196. '0'..'9': exponent := (exponent * 10) + Integer(c) - Integer('0');
  197. else
  198. Result := False;
  199. Exit;
  200. end;
  201. end;
  202. if strValue[i + 1] <> '-' then
  203. exponent := -exponent;
  204. exponent := exponent - 1;
  205. lLen := i;
  206. if divider > lLen then
  207. divider := lLen;
  208. Break;
  209. end;
  210. else
  211. Result := False;
  212. Exit;
  213. end;
  214. end;
  215. divider := lLen - divider + exponent + 1;
  216. if strValue[1] = '-' then
  217. begin
  218. v := -v;
  219. end;
  220. if divider <> 0 then
  221. v := v * Exp(-divider * Ln(10));
  222. val := v;
  223. Result := True;
  224. end;
  225. function StrToFloatDef(const strValue: string; defValue: Extended = 0): Extended;
  226. begin
  227. if not TryStrToFloat(strValue, Result) then
  228. result := defValue;
  229. end;
  230. function StringToColorAdvancedSafe(const Str: string; const Default: TColor): TColor;
  231. begin
  232. if not TryStringToColorAdvanced(Str, Result) then
  233. Result := Default;
  234. end;
  235. function StringToColorAdvanced(const Str: string): TColor;
  236. begin
  237. if not TryStringToColorAdvanced(Str, Result) then
  238. raise EGLUtilsException.CreateResFmt(@strInvalidColor, [Str]);
  239. end;
  240. function TryStringToColorAdvanced(const Str: string; var OutColor: TColor): Boolean;
  241. var
  242. Code, I: Integer;
  243. Temp: string;
  244. begin
  245. Result := True;
  246. Temp := Str;
  247. Val(Temp, I, Code); //to see if it is a number
  248. if Code = 0 then
  249. OutColor := TColor(I) //Str = $0000FF
  250. else
  251. begin
  252. if not IdentToColor(Temp, Longint(OutColor)) then //Str = clRed
  253. begin
  254. if AnsiStartsText('clr', Temp) then //Str = clrRed
  255. begin
  256. Delete(Temp, 3, 1);
  257. if not IdentToColor(Temp, Longint(OutColor)) then
  258. Result := False;
  259. end
  260. else if not IdentToColor('cl' + Temp, Longint(OutColor)) then //Str = Red
  261. Result := False;
  262. end;
  263. end;
  264. end;
  265. function ParseInteger(var p: PChar): Integer;
  266. var
  267. neg: Boolean;
  268. c: Char;
  269. begin
  270. Result := 0;
  271. if p = nil then
  272. Exit;
  273. neg := False;
  274. // skip non-numerics
  275. while not CharInSet(p^, [#0, '0'..'9', '+', '-']) do
  276. Inc(p);
  277. c := p^;
  278. if c = '+' then
  279. Inc(p)
  280. else if c = '-' then
  281. begin
  282. neg := True;
  283. Inc(p);
  284. end;
  285. // Parse numerics
  286. while True do
  287. begin
  288. c := p^;
  289. if not CharInSet(c, ['0'..'9']) then
  290. Break;
  291. Result := Result * 10 + Integer(c) - Integer('0');
  292. Inc(p);
  293. end;
  294. if neg then
  295. Result := -Result;
  296. end;
  297. function ParseFloat(var p: PChar): Extended;
  298. var
  299. decimals, expSign, exponent: Integer;
  300. c: Char;
  301. neg: Boolean;
  302. begin
  303. Result := 0;
  304. if p = nil then
  305. Exit;
  306. // skip non-numerics
  307. while not CharInSet(p^, [#0, '0'..'9', '+', '-']) do
  308. Inc(p);
  309. c := p^;
  310. if c = '+' then
  311. begin
  312. neg := False;
  313. Inc(p);
  314. end
  315. else if c = '-' then
  316. begin
  317. neg := True;
  318. Inc(p);
  319. end
  320. else
  321. neg := False;
  322. // parse numbers
  323. while CharInSet(p^, ['0'..'9']) do
  324. begin
  325. Result := Result * 10 + (Integer(p^) - Integer('0'));
  326. Inc(p);
  327. end;
  328. // parse dot, then decimals, if any
  329. decimals := 0;
  330. if (p^ = '.') then
  331. begin
  332. Inc(p);
  333. while CharInSet(p^, ['0'..'9']) do
  334. begin
  335. Result := Result * 10 + (Integer(p^) - Integer('0'));
  336. Inc(p);
  337. Dec(decimals);
  338. end;
  339. end;
  340. // parse exponent, if any
  341. if CharInSet(p^, ['e', 'E']) then
  342. begin
  343. Inc(p);
  344. // parse exponent sign
  345. c := p^;
  346. if c = '-' then
  347. begin
  348. expSign := -1;
  349. Inc(p);
  350. end
  351. else if c = '+' then
  352. begin
  353. expSign := 1;
  354. Inc(p);
  355. end
  356. else
  357. expSign := 1;
  358. // parse exponent
  359. exponent := 0;
  360. while CharInSet(p^, ['0'..'9']) do
  361. begin
  362. exponent := exponent * 10 + (Integer(p^) - Integer('0'));
  363. Inc(p);
  364. end;
  365. decimals := decimals + expSign * exponent;
  366. end;
  367. if decimals <> 0 then
  368. Result := Result * Exp(decimals * Ln(10));
  369. if neg then
  370. Result := -Result;
  371. end;
  372. procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
  373. var
  374. n: Cardinal;
  375. fs: TStream;
  376. begin
  377. fs := TFileStream.Create(fileName, fmCreate);
  378. try
  379. n := Length(data);
  380. if n > 0 then
  381. fs.Write(data[1], n);
  382. finally
  383. fs.Free;
  384. end;
  385. end;
  386. function LoadAnsiStringFromFile(const fileName: string): AnsiString;
  387. var
  388. n: Cardinal;
  389. fs: TStream;
  390. begin
  391. if FileExists(fileName) then
  392. begin
  393. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  394. try
  395. n := fs.Size;
  396. SetLength(Result, n);
  397. if n > 0 then
  398. fs.Read(Result[1], n);
  399. finally
  400. fs.Free;
  401. end;
  402. end
  403. else
  404. Result := '';
  405. end;
  406. procedure SaveStringToFile(const fileName: string; const data: String);
  407. var
  408. n: Cardinal;
  409. fs: TStream;
  410. begin
  411. fs := TFileStream.Create(fileName, fmCreate);
  412. try
  413. n := Length(data);
  414. if n > 0 then
  415. fs.Write(data[1], n);
  416. finally
  417. fs.Free;
  418. end;
  419. end;
  420. function LoadStringFromFile(const fileName: string): String;
  421. var
  422. n: Cardinal;
  423. fs: TStream;
  424. begin
  425. if FileExists(fileName) then
  426. begin
  427. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  428. try
  429. n := fs.Size;
  430. SetLength(Result, n);
  431. if n > 0 then
  432. fs.Read(Result[1], n);
  433. finally
  434. fs.Free;
  435. end;
  436. end
  437. else
  438. Result := '';
  439. end;
  440. procedure SaveComponentToFile(const Component: TComponent; const FileName: string; const AsText: Boolean);
  441. var
  442. Stream: TStream;
  443. MemStream: TMemoryStream;
  444. begin
  445. Stream := TFileStream.Create(FileName, fmCreate);
  446. try
  447. if AsText then
  448. begin
  449. MemStream := TMemoryStream.Create;
  450. try
  451. MemStream.WriteComponent(Component);
  452. MemStream.Position := 0;
  453. ObjectBinaryToText(MemStream, Stream);
  454. finally
  455. MemStream.Free;
  456. end;
  457. end
  458. else
  459. Stream.WriteComponent(Component);
  460. finally
  461. Stream.Free;
  462. end;
  463. end;
  464. procedure LoadComponentFromFile(const Component: TComponent; const FileName: string; const AsText: Boolean = True);
  465. var
  466. Stream: TStream;
  467. MemStream: TMemoryStream;
  468. begin
  469. Stream := TFileStream.Create(FileName, fmOpenRead);
  470. try
  471. if AsText then
  472. begin
  473. MemStream := TMemoryStream.Create;
  474. try
  475. ObjectTextToBinary(Stream, MemStream);
  476. MemStream.Position := 0;
  477. MemStream.ReadComponent(Component);
  478. finally
  479. MemStream.Free;
  480. end;
  481. end
  482. else
  483. Stream.ReadComponent(Component);
  484. finally
  485. Stream.Free;
  486. end;
  487. end;
  488. function SizeOfFile(const fileName: string): Int64;
  489. var
  490. fs: TStream;
  491. begin
  492. if FileExists(fileName) then
  493. begin
  494. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  495. try
  496. Result := fs.Size;
  497. finally
  498. fs.Free;
  499. end;
  500. end
  501. else
  502. Result := 0;
  503. end;
  504. function GetSqrt255Array: PSqrt255Array;
  505. const
  506. cOneDiv255 = 1 / 255;
  507. var
  508. i: Integer;
  509. begin
  510. if vSqrt255[255] <> 255 then
  511. begin
  512. for i := 0 to 255 do
  513. vSqrt255[i] := Integer(Trunc(255 * Sqrt(i * cOneDiv255)));
  514. end;
  515. Result := @vSqrt255;
  516. end;
  517. procedure InformationDlg(const msg: string);
  518. begin
  519. ShowMessage(msg);
  520. end;
  521. function QuestionDlg(const msg: string): Boolean;
  522. begin
  523. Result := (MessageDlg(msg, mtConfirmation, [mbYes, mbNo], 0) = mrYes);
  524. end;
  525. function InputDlg(const aCaption, aPrompt, aDefault: string): string;
  526. begin
  527. Result := InputBox(aCaption, aPrompt, aDefault);
  528. end;
  529. function SavePictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
  530. var
  531. saveDialog: TSavePictureDialog;
  532. begin
  533. saveDialog := TSavePictureDialog.Create(nil);
  534. try
  535. with saveDialog do
  536. begin
  537. Options := [ofHideReadOnly, ofNoReadOnlyReturn];
  538. if aTitle <> '' then
  539. Title := aTitle;
  540. FileName := aFileName;
  541. Result := Execute;
  542. if Result then
  543. aFileName := FileName;
  544. end;
  545. finally
  546. saveDialog.Free;
  547. end;
  548. end;
  549. function OpenPictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
  550. var
  551. openDialog: TOpenPictureDialog;
  552. begin
  553. openDialog := TOpenPictureDialog.Create(nil);
  554. try
  555. with openDialog do
  556. begin
  557. Options := [ofHideReadOnly, ofNoReadOnlyReturn];
  558. if aTitle <> '' then
  559. Title := aTitle;
  560. FileName := aFileName;
  561. Result := Execute;
  562. if Result then
  563. aFileName := FileName;
  564. end;
  565. finally
  566. openDialog.Free;
  567. end;
  568. end;
  569. procedure SetGLSceneMediaDir();
  570. var
  571. path: String;
  572. p: Integer;
  573. begin
  574. path := ParamStr(0);
  575. path := LowerCase(ExtractFilePath(path));
  576. p := Pos('demos', path);
  577. Delete(path, p+5, Length(path));
  578. path := IncludeTrailingPathDelimiter(path) + 'media';
  579. SetCurrentDir(path);
  580. end;
  581. end.