fpvutils.pas 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. {
  2. fpvutils.pas
  3. Vector graphics document
  4. License: The same modified LGPL as the Free Pascal RTL
  5. See the file COPYING.modifiedLGPL for more details
  6. AUTHORS: Felipe Monteiro de Carvalho
  7. Pedro Sol Pegorini L de Lima
  8. }
  9. unit fpvutils;
  10. {$ifdef fpc}
  11. {$mode delphi}
  12. {$endif}
  13. interface
  14. uses
  15. Classes, SysUtils, Math,
  16. fpvectorial, fpimage;
  17. type
  18. T10Strings = array[0..9] of shortstring;
  19. // Color Conversion routines
  20. function FPColorToRGBHexString(AColor: TFPColor): string;
  21. function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline;
  22. // Other routine
  23. function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline;
  24. function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer;
  25. function SeparateString(AString: string; ASeparator: char): T10Strings;
  26. // Mathematical routines
  27. procedure EllipticalArcToBezier(Xc, Yc, Rx, Ry, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint);
  28. procedure CircularArcToBezier(Xc, Yc, R, startAngle, endAngle: Double; var P1, P2, P3, P4: T3DPoint);
  29. implementation
  30. {@@ This function is utilized by the SVG writer and some other places, so
  31. it shouldn't be changed.
  32. }
  33. function FPColorToRGBHexString(AColor: TFPColor): string;
  34. begin
  35. Result := Format('%.2x%.2x%.2x', [AColor.Red shr 8, AColor.Green shr 8, AColor.Blue shr 8]);
  36. end;
  37. function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline;
  38. begin
  39. Result.Red := (AR shl 8) + AR;
  40. Result.Green := (AG shl 8) + AG;
  41. Result.Blue := (AB shl 8) + AB;
  42. Result.Alpha := $FFFF;
  43. end;
  44. {@@ Converts the coordinate system from a TCanvas to FPVectorial
  45. The basic difference is that the Y axis is positioned differently and
  46. points upwards in FPVectorial and downwards in TCanvas.
  47. The X axis doesn't change. The fix is trivial and requires only the Height of
  48. the Canvas as extra info.
  49. @param AHeight Should receive TCanvas.Height
  50. }
  51. function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline;
  52. begin
  53. Result := AHeight - AY;
  54. end;
  55. {@@
  56. LCL Text is positioned based on the top-left corner of the text.
  57. Besides that, one also needs to take the general coordinate change into account too.
  58. @param ACanvasHeight Should receive TCanvas.Height
  59. @param ATextHeight Should receive TFont.Size
  60. }
  61. function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer;
  62. begin
  63. Result := CanvasCoordsToFPVectorial(AY, ACanvasHeight) - ATextHeight;
  64. end;
  65. {@@
  66. Reads a string and separates it in substring
  67. using ASeparator to delimite them.
  68. Limits:
  69. Number of substrings: 10 (indexed 0 to 9)
  70. Length of each substring: 255 (they are shortstrings)
  71. }
  72. function SeparateString(AString: string; ASeparator: char): T10Strings;
  73. var
  74. i, CurrentPart: integer;
  75. begin
  76. CurrentPart := 0;
  77. { Clears the result }
  78. for i := 0 to 9 do
  79. Result[i] := '';
  80. { Iterates througth the string, filling strings }
  81. for i := 1 to Length(AString) do
  82. begin
  83. if Copy(AString, i, 1) = ASeparator then
  84. begin
  85. Inc(CurrentPart);
  86. { Verifies if the string capacity wasn't exceeded }
  87. if CurrentPart > 9 then
  88. Exit;
  89. end
  90. else
  91. Result[CurrentPart] := Result[CurrentPart] + Copy(AString, i, 1);
  92. end;
  93. end;
  94. { Considering a counter-clockwise arc, elliptical and alligned to the axises
  95. An elliptical Arc can be converted to
  96. the following Cubic Bezier control points:
  97. P1 = E(startAngle) <- start point
  98. P2 = P1+alfa * dE(startAngle) <- control point
  99. P3 = P4−alfa * dE(endAngle) <- control point
  100. P4 = E(endAngle) <- end point
  101. source: http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
  102. The equation of an elliptical arc is:
  103. X(t) = Xc + Rx * cos(t)
  104. Y(t) = Yc + Ry * sin(t)
  105. dX(t)/dt = - Rx * sin(t)
  106. dY(t)/dt = + Ry * cos(t)
  107. }
  108. procedure EllipticalArcToBezier(Xc, Yc, Rx, Ry, startAngle, endAngle: Double;
  109. var P1, P2, P3, P4: T3DPoint);
  110. var
  111. halfLength, arcLength, alfa: Double;
  112. begin
  113. arcLength := endAngle - startAngle;
  114. halfLength := (endAngle - startAngle) / 2;
  115. alfa := sin(arcLength) * (Sqrt(4 + 3*sqr(tan(halfLength))) - 1) / 3;
  116. // Start point
  117. P1.X := Xc + Rx * cos(startAngle);
  118. P1.Y := Yc + Ry * sin(startAngle);
  119. // End point
  120. P4.X := Xc + Rx * cos(endAngle);
  121. P4.Y := Yc + Ry * sin(endAngle);
  122. // Control points
  123. P2.X := P1.X + alfa * -1 * Rx * sin(startAngle);
  124. P2.Y := P1.Y + alfa * Ry * cos(startAngle);
  125. P3.X := P4.X - alfa * -1 * Rx * sin(endAngle);
  126. P3.Y := P4.Y - alfa * Ry * cos(endAngle);
  127. end;
  128. procedure CircularArcToBezier(Xc, Yc, R, startAngle, endAngle: Double; var P1,
  129. P2, P3, P4: T3DPoint);
  130. begin
  131. EllipticalArcToBezier(Xc, Yc, R, R, startAngle, endAngle, P1, P2, P3, P4);
  132. end;
  133. end.