1"""
2Various transforms used for by the 3D code
3"""
4
5import numpy as np
6
7from matplotlib import _api
8
9
10def world_transformation(xmin, xmax,
11 ymin, ymax,
12 zmin, zmax, pb_aspect=None):
13 """
14 Produce a matrix that scales homogeneous coords in the specified ranges
15 to [0, 1], or [0, pb_aspect[i]] if the plotbox aspect ratio is specified.
16 """
17 dx = xmax - xmin
18 dy = ymax - ymin
19 dz = zmax - zmin
20 if pb_aspect is not None:
21 ax, ay, az = pb_aspect
22 dx /= ax
23 dy /= ay
24 dz /= az
25
26 return np.array([[1/dx, 0, 0, -xmin/dx],
27 [0, 1/dy, 0, -ymin/dy],
28 [0, 0, 1/dz, -zmin/dz],
29 [0, 0, 0, 1]])
30
31
32@_api.deprecated("3.8")
33def rotation_about_vector(v, angle):
34 """
35 Produce a rotation matrix for an angle in radians about a vector.
36 """
37 return _rotation_about_vector(v, angle)
38
39
40def _rotation_about_vector(v, angle):
41 """
42 Produce a rotation matrix for an angle in radians about a vector.
43 """
44 vx, vy, vz = v / np.linalg.norm(v)
45 s = np.sin(angle)
46 c = np.cos(angle)
47 t = 2*np.sin(angle/2)**2 # more numerically stable than t = 1-c
48
49 R = np.array([
50 [t*vx*vx + c, t*vx*vy - vz*s, t*vx*vz + vy*s],
51 [t*vy*vx + vz*s, t*vy*vy + c, t*vy*vz - vx*s],
52 [t*vz*vx - vy*s, t*vz*vy + vx*s, t*vz*vz + c]])
53
54 return R
55
56
57def _view_axes(E, R, V, roll):
58 """
59 Get the unit viewing axes in data coordinates.
60
61 Parameters
62 ----------
63 E : 3-element numpy array
64 The coordinates of the eye/camera.
65 R : 3-element numpy array
66 The coordinates of the center of the view box.
67 V : 3-element numpy array
68 Unit vector in the direction of the vertical axis.
69 roll : float
70 The roll angle in radians.
71
72 Returns
73 -------
74 u : 3-element numpy array
75 Unit vector pointing towards the right of the screen.
76 v : 3-element numpy array
77 Unit vector pointing towards the top of the screen.
78 w : 3-element numpy array
79 Unit vector pointing out of the screen.
80 """
81 w = (E - R)
82 w = w/np.linalg.norm(w)
83 u = np.cross(V, w)
84 u = u/np.linalg.norm(u)
85 v = np.cross(w, u) # Will be a unit vector
86
87 # Save some computation for the default roll=0
88 if roll != 0:
89 # A positive rotation of the camera is a negative rotation of the world
90 Rroll = _rotation_about_vector(w, -roll)
91 u = np.dot(Rroll, u)
92 v = np.dot(Rroll, v)
93 return u, v, w
94
95
96def _view_transformation_uvw(u, v, w, E):
97 """
98 Return the view transformation matrix.
99
100 Parameters
101 ----------
102 u : 3-element numpy array
103 Unit vector pointing towards the right of the screen.
104 v : 3-element numpy array
105 Unit vector pointing towards the top of the screen.
106 w : 3-element numpy array
107 Unit vector pointing out of the screen.
108 E : 3-element numpy array
109 The coordinates of the eye/camera.
110 """
111 Mr = np.eye(4)
112 Mt = np.eye(4)
113 Mr[:3, :3] = [u, v, w]
114 Mt[:3, -1] = -E
115 M = np.dot(Mr, Mt)
116 return M
117
118
119@_api.deprecated("3.8")
120def view_transformation(E, R, V, roll):
121 """
122 Return the view transformation matrix.
123
124 Parameters
125 ----------
126 E : 3-element numpy array
127 The coordinates of the eye/camera.
128 R : 3-element numpy array
129 The coordinates of the center of the view box.
130 V : 3-element numpy array
131 Unit vector in the direction of the vertical axis.
132 roll : float
133 The roll angle in radians.
134 """
135 u, v, w = _view_axes(E, R, V, roll)
136 M = _view_transformation_uvw(u, v, w, E)
137 return M
138
139
140@_api.deprecated("3.8")
141def persp_transformation(zfront, zback, focal_length):
142 return _persp_transformation(zfront, zback, focal_length)
143
144
145def _persp_transformation(zfront, zback, focal_length):
146 e = focal_length
147 a = 1 # aspect ratio
148 b = (zfront+zback)/(zfront-zback)
149 c = -2*(zfront*zback)/(zfront-zback)
150 proj_matrix = np.array([[e, 0, 0, 0],
151 [0, e/a, 0, 0],
152 [0, 0, b, c],
153 [0, 0, -1, 0]])
154 return proj_matrix
155
156
157@_api.deprecated("3.8")
158def ortho_transformation(zfront, zback):
159 return _ortho_transformation(zfront, zback)
160
161
162def _ortho_transformation(zfront, zback):
163 # note: w component in the resulting vector will be (zback-zfront), not 1
164 a = -(zfront + zback)
165 b = -(zfront - zback)
166 proj_matrix = np.array([[2, 0, 0, 0],
167 [0, 2, 0, 0],
168 [0, 0, -2, 0],
169 [0, 0, a, b]])
170 return proj_matrix
171
172
173def _proj_transform_vec(vec, M):
174 vecw = np.dot(M, vec)
175 w = vecw[3]
176 # clip here..
177 txs, tys, tzs = vecw[0]/w, vecw[1]/w, vecw[2]/w
178 return txs, tys, tzs
179
180
181def _proj_transform_vec_clip(vec, M):
182 vecw = np.dot(M, vec)
183 w = vecw[3]
184 # clip here.
185 txs, tys, tzs = vecw[0] / w, vecw[1] / w, vecw[2] / w
186 tis = (0 <= vecw[0]) & (vecw[0] <= 1) & (0 <= vecw[1]) & (vecw[1] <= 1)
187 if np.any(tis):
188 tis = vecw[1] < 1
189 return txs, tys, tzs, tis
190
191
192def inv_transform(xs, ys, zs, invM):
193 """
194 Transform the points by the inverse of the projection matrix, *invM*.
195 """
196 vec = _vec_pad_ones(xs, ys, zs)
197 vecr = np.dot(invM, vec)
198 if vecr.shape == (4,):
199 vecr = vecr.reshape((4, 1))
200 for i in range(vecr.shape[1]):
201 if vecr[3][i] != 0:
202 vecr[:, i] = vecr[:, i] / vecr[3][i]
203 return vecr[0], vecr[1], vecr[2]
204
205
206def _vec_pad_ones(xs, ys, zs):
207 return np.array([xs, ys, zs, np.ones_like(xs)])
208
209
210def proj_transform(xs, ys, zs, M):
211 """
212 Transform the points by the projection matrix *M*.
213 """
214 vec = _vec_pad_ones(xs, ys, zs)
215 return _proj_transform_vec(vec, M)
216
217
218transform = _api.deprecated(
219 "3.8", obj_type="function", name="transform",
220 alternative="proj_transform")(proj_transform)
221
222
223def proj_transform_clip(xs, ys, zs, M):
224 """
225 Transform the points by the projection matrix
226 and return the clipping result
227 returns txs, tys, tzs, tis
228 """
229 vec = _vec_pad_ones(xs, ys, zs)
230 return _proj_transform_vec_clip(vec, M)
231
232
233@_api.deprecated("3.8")
234def proj_points(points, M):
235 return _proj_points(points, M)
236
237
238def _proj_points(points, M):
239 return np.column_stack(_proj_trans_points(points, M))
240
241
242@_api.deprecated("3.8")
243def proj_trans_points(points, M):
244 return _proj_trans_points(points, M)
245
246
247def _proj_trans_points(points, M):
248 xs, ys, zs = zip(*points)
249 return proj_transform(xs, ys, zs, M)
250
251
252@_api.deprecated("3.8")
253def rot_x(V, alpha):
254 cosa, sina = np.cos(alpha), np.sin(alpha)
255 M1 = np.array([[1, 0, 0, 0],
256 [0, cosa, -sina, 0],
257 [0, sina, cosa, 0],
258 [0, 0, 0, 1]])
259 return np.dot(M1, V)