AGfxMatrix.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. (* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
  2. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
  3. MODULE GfxMatrix; (** portable *) (* eos *)
  4. (** AUTHOR "eos"; PURPOSE "Affine transformations in 2D"; *)
  5. (*
  6. 21.02.2000 - bugfix in Scaled: didn't recognize downscaling
  7. 15.04.2000 - added Atan2
  8. *)
  9. IMPORT
  10. Streams, Math;
  11. CONST
  12. Eps = 1.0E-5;
  13. TYPE
  14. (**
  15. Transformation matrix
  16. 3x2_matrices can represent any combination of affine transformations, i.e. of translation, rotation, scaling and
  17. shearing.
  18. Translate by tx, ty:
  19. [ 1 0 ]
  20. [ 0 1 ]
  21. [ tx ty ]
  22. Scale by sx, sy:
  23. [ sx 0 ]
  24. [ 0 sy ]
  25. [ 0 0 ]
  26. Rotate counter-clockwise by angle phi:
  27. [ cos(phi) sin(phi) ]
  28. [ -sin(phi) cos(phi) ]
  29. [ 0 0 ]
  30. Shear along x_axis by factor f:
  31. [ 1 0 ]
  32. [ f 1 ]
  33. [ 0 0 ]
  34. **)
  35. Matrix* = ARRAY 3, 2 OF REAL;
  36. VAR
  37. Identity*: Matrix; (** identity matrix (read_only) **)
  38. (**--- Matrix Computation ---**)
  39. (** initialize matrix with given values **)
  40. PROCEDURE Init* (VAR m: Matrix; m00, m01, m10, m11, m20, m21: REAL);
  41. BEGIN
  42. m[0, 0] := m00; m[0, 1] := m01;
  43. m[1, 0] := m10; m[1, 1] := m11;
  44. m[2, 0] := m20; m[2, 1] := m21
  45. END Init;
  46. (**
  47. Procedures Get3PointTransform, Get2PointTransform and Invert may not be able to find a solution. In that case,
  48. they return a singular matrix with all elements set to zero.
  49. **)
  50. (** calculate matrix that maps p0 to p1, q0 to q1, and r0 to r1 **)
  51. PROCEDURE Get3PointTransform* (px0, py0, px1, py1, qx0, qy0, qx1, qy1, rx0, ry0, rx1, ry1: REAL; VAR res: Matrix);
  52. VAR m: ARRAY 6, 7 OF REAL; i, j, k: LONGINT; max, t: REAL; v: ARRAY 6 OF REAL;
  53. BEGIN
  54. (* initialize set of linear equations for matrix coefficients *)
  55. m[0, 0] := px0; m[0, 1] := py0; m[0, 2] := 1.0; m[0, 3] := 0.0; m[0, 4] := 0.0; m[0, 5] := 0.0; m[0, 6] := px1;
  56. m[1, 0] := qx0; m[1, 1] := qy0; m[1, 2] := 1.0; m[1, 3] := 0.0; m[1, 4] := 0.0; m[1, 5] := 0.0; m[1, 6] := qx1;
  57. m[2, 0] := rx0; m[2, 1] := ry0; m[2, 2] := 1.0; m[2, 3] := 0.0; m[2, 4] := 0.0; m[2, 5] := 0.0; m[2, 6] := rx1;
  58. m[3, 0] := 0.0; m[3, 1] := 0.0; m[3, 2] := 0.0; m[3, 3] := px0; m[3, 4] := py0; m[3, 5] := 1.0; m[3, 6] := py1;
  59. m[4, 0] := 0.0; m[4, 1] := 0.0; m[4, 2] := 0.0; m[4, 3] := qx0; m[4, 4] := qy0; m[4, 5] := 1.0; m[4, 6] := qy1;
  60. m[5, 0] := 0.0; m[5, 1] := 0.0; m[5, 2] := 0.0; m[5, 3] := rx0; m[5, 4] := ry0; m[5, 5] := 1.0; m[5, 6] := ry1;
  61. (* Gaussian elimination with pivoting *)
  62. FOR i := 0 TO 5 DO
  63. k := i; max := ABS(m[i, i]);
  64. FOR j := i+1 TO 5 DO
  65. IF ABS(m[j, i]) > max THEN
  66. k := j; max := ABS(m[j, i])
  67. END
  68. END;
  69. IF max < Eps THEN (* matrix is singular *)
  70. Init(res, 0, 0, 0, 0, 0, 0);
  71. RETURN
  72. END;
  73. IF k # i THEN (* swap rows to bring largest element up *)
  74. FOR j := i TO 6 DO
  75. t := m[i, j]; m[i, j] := m[k, j]; m[k, j] := t
  76. END
  77. END;
  78. FOR k := i+1 TO 5 DO
  79. t := m[k, i]/m[i, i];
  80. FOR j := i+1 TO 6 DO
  81. m[k, j] := m[k, j] - t * m[i, j]
  82. END
  83. END
  84. END;
  85. (* solve equations *)
  86. FOR i := 5 TO 0 BY -1 DO
  87. t := m[i, 6];
  88. FOR j := i+1 TO 5 DO
  89. t := t - v[j] * m[i, j]
  90. END;
  91. v[i] := t/m[i, i]
  92. END;
  93. Init(res, v[0], v[3], v[1], v[4], v[2], v[5])
  94. END Get3PointTransform;
  95. (** calculate matrix that maps p0 to p1 and q0 to q1 **)
  96. PROCEDURE Get2PointTransform* (px0, py0, px1, py1, qx0, qy0, qx1, qy1: REAL; VAR res: Matrix);
  97. VAR rx0, ry0, rx1, ry1: REAL;
  98. BEGIN
  99. rx0 := px0 + py0 - qy0; ry0 := py0 + qx0 - px0;
  100. rx1 := px1 + py1 - qy1; ry1 := py1 + qx1 - px1;
  101. Get3PointTransform(px0, py0, px1, py1, qx0, qy0, qx1, qy1, rx0, ry0, rx1, ry1, res)
  102. END Get2PointTransform;
  103. (** calculate inverse of matrix **)
  104. PROCEDURE Invert* (m: Matrix; VAR res: Matrix);
  105. VAR det, inv: REAL;
  106. BEGIN
  107. det := m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0];
  108. IF ABS(det) >= Eps THEN (* matrix can be inverted; use Cramer's rule *)
  109. inv := 1/det;
  110. res[0, 0] := +inv * m[1, 1];
  111. res[0, 1] := -inv * m[0, 1];
  112. res[1, 0] := -inv * m[1, 0];
  113. res[1, 1] := +inv * m[0, 0];
  114. res[2, 0] := +inv * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0]);
  115. res[2, 1] := +inv * (m[0, 1] * m[2, 0] - m[0, 0] * m[2, 1])
  116. ELSE
  117. Init(res, 0, 0, 0, 0, 0, 0)
  118. END
  119. END Invert;
  120. (**--- Detection of Special Cases ---**)
  121. (** return determinant of matrix **)
  122. PROCEDURE Det* (VAR m: Matrix): REAL;
  123. BEGIN
  124. RETURN m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]
  125. END Det;
  126. (** return whether matrix is singular **)
  127. PROCEDURE Singular* (VAR m: Matrix): BOOLEAN;
  128. BEGIN
  129. RETURN ABS(m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]) < Eps
  130. END Singular;
  131. (** return whether matrix changes vector lengths **)
  132. PROCEDURE Scaled* (VAR m: Matrix): BOOLEAN;
  133. BEGIN
  134. RETURN ABS(ABS(m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]) - 1) > Eps
  135. END Scaled;
  136. (** return whether matrix includes rotation, shear, or mirror transformation **)
  137. PROCEDURE Rotated* (VAR m: Matrix): BOOLEAN;
  138. BEGIN
  139. RETURN (m[0, 0] < -Eps) OR (m[1, 1] < -Eps) OR (ABS(m[0, 1]) > Eps) OR (ABS(m[1, 0]) > Eps)
  140. END Rotated;
  141. (** return whether matrices should be considered equal **)
  142. PROCEDURE Equal* (VAR m, n: Matrix): BOOLEAN;
  143. BEGIN
  144. RETURN
  145. (ABS(m[0, 0] - n[0, 0]) < Eps) & (ABS(m[0, 1] - n[0, 1]) < Eps) &
  146. (ABS(m[1, 0] - n[1, 0]) < Eps) & (ABS(m[1, 1] - n[1, 1]) < Eps) &
  147. (ABS(m[2, 0] - n[2, 0]) < Eps) & (ABS(m[2, 1] - n[2, 1]) < Eps)
  148. END Equal;
  149. (**--- Matrix Concatenation ---**)
  150. (**
  151. Combinations of single transformations are evaluated from left to right. Executing Translate, Rotate or Scale
  152. pre-concatenates a corresponding matrix to the left of the given matrix parameter. This has the effect that
  153. the new transformation is applied before all previously accumulated transformations. Every transformation is
  154. therefore executed in the context of the coordinate system defined by the concatenation of all transformations
  155. to its right.
  156. **)
  157. (** translation by (dx, dy) **)
  158. PROCEDURE Translate* (m: Matrix; dx, dy: REAL; VAR res: Matrix);
  159. BEGIN
  160. res[0, 0] := m[0, 0]; res[0, 1] := m[0, 1];
  161. res[1, 0] := m[1, 0]; res[1, 1] := m[1, 1];
  162. res[2, 0] := m[2, 0] + dx * m[0, 0] + dy * m[1, 0];
  163. res[2, 1] := m[2, 1] + dx * m[0, 1] + dy * m[1, 1]
  164. END Translate;
  165. (** scale by (sx, sy) **)
  166. PROCEDURE Scale* (m: Matrix; sx, sy: REAL; VAR res: Matrix);
  167. BEGIN
  168. res[0, 0] := sx * m[0, 0]; res[0, 1] := sx * m[0, 1];
  169. res[1, 0] := sy * m[1, 0]; res[1, 1] := sy * m[1, 1];
  170. res[2, 0] := m[2, 0]; res[2, 1] := m[2, 1]
  171. END Scale;
  172. (** scale at (ox, oy) by (sx, sy) **)
  173. PROCEDURE ScaleAt* (m: Matrix; ox, oy, sx, sy: REAL; VAR res: Matrix);
  174. VAR tx, ty: REAL;
  175. BEGIN
  176. res[0, 0] := sx * m[0, 0]; res[0, 1] := sx * m[0, 1];
  177. res[1, 0] := sy * m[1, 0]; res[1, 1] := sy * m[1, 1];
  178. tx := ox * (1-sx); ty := oy * (1-sy);
  179. res[2, 0] := tx * m[0, 0] + ty * m[1, 0] + m[2, 0];
  180. res[2, 1] := tx * m[0, 1] + ty * m[1, 1] + m[2, 1]
  181. END ScaleAt;
  182. (** rotate counter-clockwise by angle specified by its sine and cosine **)
  183. PROCEDURE Rotate* (m: Matrix; sin, cos: REAL; VAR res: Matrix);
  184. BEGIN
  185. res[0, 0] := cos * m[0, 0] + sin * m[1, 0]; res[0, 1] := cos * m[0, 1] + sin * m[1, 1];
  186. res[1, 0] := -sin * m[0, 0] + cos * m[1, 0]; res[1, 1] := -sin * m[0, 1] + cos * m[1, 1];
  187. res[2, 0] := m[2, 0]; res[2, 1] := m[2, 1]
  188. END Rotate;
  189. (** rotate counter-clockwise around (ox, oy) by angle specified by its sine and cosine **)
  190. PROCEDURE RotateAt* (m: Matrix; ox, oy, sin, cos: REAL; VAR res: Matrix);
  191. VAR tx, ty: REAL;
  192. BEGIN
  193. res[0, 0] := cos * m[0, 0] + sin * m[1, 0]; res[0, 1] := cos * m[0, 1] + sin * m[1, 1];
  194. res[1, 0] := -sin * m[0, 0] + cos * m[1, 0]; res[1, 1] := -sin * m[0, 1] + cos * m[1, 1];
  195. tx := ox * (1-cos) + oy * sin; ty := oy * (1-cos) - ox * sin;
  196. res[2, 0] := tx * m[0, 0] + ty * m[1, 0] + m[2, 0];
  197. res[2, 1] := tx * m[0, 1] + ty * m[1, 1] + m[2, 1]
  198. END RotateAt;
  199. (** concatenate matrices **)
  200. PROCEDURE Concat* (m, n: Matrix; VAR res: Matrix);
  201. BEGIN
  202. res[0, 0] := m[0, 0] * n[0, 0] + m[0, 1] * n[1, 0];
  203. res[0, 1] := m[0, 0] * n[0, 1] + m[0, 1] * n[1, 1];
  204. res[1, 0] := m[1, 0] * n[0, 0] + m[1, 1] * n[1, 0];
  205. res[1, 1] := m[1, 0] * n[0, 1] + m[1, 1] * n[1, 1];
  206. res[2, 0] := m[2, 0] * n[0, 0] + m[2, 1] * n[1, 0] + n[2, 0];
  207. res[2, 1] := m[2, 0] * n[0, 1] + m[2, 1] * n[1, 1] + n[2, 1]
  208. END Concat;
  209. (**--- Arctan of Vector ---**)
  210. PROCEDURE Atan2* (x, y: REAL): REAL;
  211. VAR phi: REAL;
  212. BEGIN
  213. IF (ABS(x) < 1.0) & (ABS(y) >= ABS(x * MAX(REAL))) THEN (* y/x would overflow *)
  214. IF y >= 0.0 THEN phi := Math.pi/2
  215. ELSE phi := -Math.pi/2
  216. END
  217. ELSIF x > 0.0 THEN (* 1st or 4th quadrant *)
  218. phi := Math.arctan(y/x)
  219. ELSIF x < 0.0 THEN (* 2nd or 3rd quadrant *)
  220. phi := Math.arctan(y/x) + Math.pi
  221. END;
  222. RETURN phi
  223. END Atan2;
  224. (**--- Matrix Application ---**)
  225. (** apply transformation matrix to point **)
  226. PROCEDURE Apply* (VAR m: Matrix; xin, yin: REAL; VAR xout, yout: REAL);
  227. BEGIN
  228. xout := xin * m[0, 0] + yin * m[1, 0] + m[2, 0];
  229. yout := xin * m[0, 1] + yin * m[1, 1] + m[2, 1]
  230. END Apply;
  231. (** apply transformation matrix to vector (ignoring translation) **)
  232. PROCEDURE ApplyToVector* (VAR m: Matrix; xin, yin: REAL; VAR xout, yout: REAL);
  233. BEGIN
  234. xout := xin * m[0, 0] + yin * m[1, 0];
  235. yout := xin * m[0, 1] + yin * m[1, 1]
  236. END ApplyToVector;
  237. (** apply transformation matrix to distance **)
  238. PROCEDURE ApplyToDist* (VAR m: Matrix; din: REAL; VAR dout: REAL);
  239. VAR x, y: REAL;
  240. BEGIN
  241. x := din * m[0, 0]; y := din * m[0, 1];
  242. IF ABS(y) < 1.0E-3 THEN dout := x
  243. ELSE dout := Math.sqrt(x * x + y * y)
  244. END
  245. END ApplyToDist;
  246. (** apply transformation matrix to axis-aligned rectangle; result is enclosing axis-aligned rectangle **)
  247. PROCEDURE ApplyToRect* (VAR m: Matrix; ilx, ily, irx, iuy: REAL; VAR olx, oly, orx, ouy: REAL);
  248. VAR l, h: REAL;
  249. BEGIN
  250. olx := m[2, 0]; orx := m[2, 0];
  251. l := ilx * m[0, 0]; h := irx * m[0, 0];
  252. IF l <= h THEN olx := olx + l; orx := orx + h ELSE olx := olx + h; orx := orx + l END;
  253. l := ily * m[1, 0]; h := iuy * m[1, 0];
  254. IF l <= h THEN olx := olx + l; orx := orx + h ELSE olx := olx + h; orx := orx + l END;
  255. oly := m[2, 1]; ouy := m[2, 1];
  256. l := ilx * m[0, 1]; h := irx * m[0, 1];
  257. IF l <= h THEN oly := oly + l; ouy := ouy + h ELSE oly := oly + h; ouy := ouy + l END;
  258. l := ily * m[1, 1]; h := iuy * m[1, 1];
  259. IF l <= h THEN oly := oly + l; ouy := ouy + h ELSE oly := oly + h; ouy := ouy + l END
  260. END ApplyToRect;
  261. (** apply inverse of matrix to point **)
  262. PROCEDURE Solve* (VAR m: Matrix; u, v: REAL; VAR x, y: REAL);
  263. VAR det: REAL;
  264. BEGIN
  265. det := m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0];
  266. IF ABS(det) >= Eps THEN (* matrix can be inverted *)
  267. u := u - m[2, 0]; v := v - m[2, 1];
  268. x := (m[1, 1] * u - m[1, 0] * v)/det;
  269. y := (m[0, 0] * v - m[0, 1] * u)/det
  270. END
  271. END Solve;
  272. (**--- Matrix I/O ---**)
  273. PROCEDURE Write* (VAR w: Streams.Writer; VAR m: Matrix);
  274. VAR i: LONGINT;
  275. BEGIN
  276. FOR i := 0 TO 2 DO
  277. w.RawReal(m[i, 0]); w.RawReal(m[i, 1])
  278. END
  279. END Write;
  280. PROCEDURE Read* (VAR r: Streams.Reader; VAR m: Matrix);
  281. VAR i: LONGINT;
  282. BEGIN
  283. FOR i := 0 TO 2 DO
  284. r.RawReal(m[i, 0]); r.RawReal(m[i, 1])
  285. END
  286. END Read;
  287. BEGIN
  288. Init(Identity, 1, 0, 0, 1, 0, 0)
  289. END GfxMatrix.