pyrender-maintained 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyrender/trackball.py ADDED
@@ -0,0 +1,216 @@
1
+ """Trackball class for 3D manipulation of viewpoints.
2
+ """
3
+ import numpy as np
4
+
5
+ import trimesh.transformations as transformations
6
+
7
+
8
+ class Trackball(object):
9
+ """A trackball class for creating camera transforms from mouse movements.
10
+ """
11
+ STATE_ROTATE = 0
12
+ STATE_PAN = 1
13
+ STATE_ROLL = 2
14
+ STATE_ZOOM = 3
15
+
16
+ def __init__(self, pose, size, scale,
17
+ target=np.array([0.0, 0.0, 0.0])):
18
+ """Initialize a trackball with an initial camera-to-world pose
19
+ and the given parameters.
20
+
21
+ Parameters
22
+ ----------
23
+ pose : [4,4]
24
+ An initial camera-to-world pose for the trackball.
25
+
26
+ size : (float, float)
27
+ The width and height of the camera image in pixels.
28
+
29
+ scale : float
30
+ The diagonal of the scene's bounding box --
31
+ used for ensuring translation motions are sufficiently
32
+ fast for differently-sized scenes.
33
+
34
+ target : (3,) float
35
+ The center of the scene in world coordinates.
36
+ The trackball will revolve around this point.
37
+ """
38
+ self._size = np.array(size)
39
+ self._scale = float(scale)
40
+
41
+ self._pose = pose
42
+ self._n_pose = pose
43
+
44
+ self._target = target
45
+ self._n_target = target
46
+
47
+ self._state = Trackball.STATE_ROTATE
48
+
49
+ @property
50
+ def pose(self):
51
+ """autolab_core.RigidTransform : The current camera-to-world pose.
52
+ """
53
+ return self._n_pose
54
+
55
+ def set_state(self, state):
56
+ """Set the state of the trackball in order to change the effect of
57
+ dragging motions.
58
+
59
+ Parameters
60
+ ----------
61
+ state : int
62
+ One of Trackball.STATE_ROTATE, Trackball.STATE_PAN,
63
+ Trackball.STATE_ROLL, and Trackball.STATE_ZOOM.
64
+ """
65
+ self._state = state
66
+
67
+ def resize(self, size):
68
+ """Resize the window.
69
+
70
+ Parameters
71
+ ----------
72
+ size : (float, float)
73
+ The new width and height of the camera image in pixels.
74
+ """
75
+ self._size = np.array(size)
76
+
77
+ def down(self, point):
78
+ """Record an initial mouse press at a given point.
79
+
80
+ Parameters
81
+ ----------
82
+ point : (2,) int
83
+ The x and y pixel coordinates of the mouse press.
84
+ """
85
+ self._pdown = np.array(point, dtype=np.float32)
86
+ self._pose = self._n_pose
87
+ self._target = self._n_target
88
+
89
+ def drag(self, point):
90
+ """Update the tracball during a drag.
91
+
92
+ Parameters
93
+ ----------
94
+ point : (2,) int
95
+ The current x and y pixel coordinates of the mouse during a drag.
96
+ This will compute a movement for the trackball with the relative
97
+ motion between this point and the one marked by down().
98
+ """
99
+ point = np.array(point, dtype=np.float32)
100
+ dx, dy = point - self._pdown
101
+ mindim = 0.3 * np.min(self._size)
102
+
103
+ target = self._target
104
+ x_axis = self._pose[:3,0].flatten()
105
+ y_axis = self._pose[:3,1].flatten()
106
+ z_axis = self._pose[:3,2].flatten()
107
+ eye = self._pose[:3,3].flatten()
108
+
109
+ # Interpret drag as a rotation
110
+ if self._state == Trackball.STATE_ROTATE:
111
+ x_angle = -dx / mindim
112
+ x_rot_mat = transformations.rotation_matrix(
113
+ x_angle, y_axis, target
114
+ )
115
+
116
+ y_angle = dy / mindim
117
+ y_rot_mat = transformations.rotation_matrix(
118
+ y_angle, x_axis, target
119
+ )
120
+
121
+ self._n_pose = y_rot_mat.dot(x_rot_mat.dot(self._pose))
122
+
123
+ # Interpret drag as a roll about the camera axis
124
+ elif self._state == Trackball.STATE_ROLL:
125
+ center = self._size / 2.0
126
+ v_init = self._pdown - center
127
+ v_curr = point - center
128
+ v_init = v_init / np.linalg.norm(v_init)
129
+ v_curr = v_curr / np.linalg.norm(v_curr)
130
+
131
+ theta = (-np.arctan2(v_curr[1], v_curr[0]) +
132
+ np.arctan2(v_init[1], v_init[0]))
133
+
134
+ rot_mat = transformations.rotation_matrix(theta, z_axis, target)
135
+
136
+ self._n_pose = rot_mat.dot(self._pose)
137
+
138
+ # Interpret drag as a camera pan in view plane
139
+ elif self._state == Trackball.STATE_PAN:
140
+ dx = -dx / (5.0 * mindim) * self._scale
141
+ dy = -dy / (5.0 * mindim) * self._scale
142
+
143
+ translation = dx * x_axis + dy * y_axis
144
+ self._n_target = self._target + translation
145
+ t_tf = np.eye(4)
146
+ t_tf[:3,3] = translation
147
+ self._n_pose = t_tf.dot(self._pose)
148
+
149
+ # Interpret drag as a zoom motion
150
+ elif self._state == Trackball.STATE_ZOOM:
151
+ radius = np.linalg.norm(eye - target)
152
+ ratio = 0.0
153
+ if dy > 0:
154
+ ratio = np.exp(abs(dy) / (0.5 * self._size[1])) - 1.0
155
+ elif dy < 0:
156
+ ratio = 1.0 - np.exp(dy / (0.5 * (self._size[1])))
157
+ translation = -np.sign(dy) * ratio * radius * z_axis
158
+ t_tf = np.eye(4)
159
+ t_tf[:3,3] = translation
160
+ self._n_pose = t_tf.dot(self._pose)
161
+
162
+ def scroll(self, clicks):
163
+ """Zoom using a mouse scroll wheel motion.
164
+
165
+ Parameters
166
+ ----------
167
+ clicks : int
168
+ The number of clicks. Positive numbers indicate forward wheel
169
+ movement.
170
+ """
171
+ target = self._target
172
+ ratio = 0.90
173
+
174
+ mult = 1.0
175
+ if clicks > 0:
176
+ mult = ratio**clicks
177
+ elif clicks < 0:
178
+ mult = (1.0 / ratio)**abs(clicks)
179
+
180
+ z_axis = self._n_pose[:3,2].flatten()
181
+ eye = self._n_pose[:3,3].flatten()
182
+ radius = np.linalg.norm(eye - target)
183
+ translation = (mult * radius - radius) * z_axis
184
+ t_tf = np.eye(4)
185
+ t_tf[:3,3] = translation
186
+ self._n_pose = t_tf.dot(self._n_pose)
187
+
188
+ z_axis = self._pose[:3,2].flatten()
189
+ eye = self._pose[:3,3].flatten()
190
+ radius = np.linalg.norm(eye - target)
191
+ translation = (mult * radius - radius) * z_axis
192
+ t_tf = np.eye(4)
193
+ t_tf[:3,3] = translation
194
+ self._pose = t_tf.dot(self._pose)
195
+
196
+ def rotate(self, azimuth, axis=None):
197
+ """Rotate the trackball about the "Up" axis by azimuth radians.
198
+
199
+ Parameters
200
+ ----------
201
+ azimuth : float
202
+ The number of radians to rotate.
203
+ """
204
+ target = self._target
205
+
206
+ y_axis = self._n_pose[:3,1].flatten()
207
+ if axis is not None:
208
+ y_axis = axis
209
+ x_rot_mat = transformations.rotation_matrix(azimuth, y_axis, target)
210
+ self._n_pose = x_rot_mat.dot(self._n_pose)
211
+
212
+ y_axis = self._pose[:3,1].flatten()
213
+ if axis is not None:
214
+ y_axis = axis
215
+ x_rot_mat = transformations.rotation_matrix(azimuth, y_axis, target)
216
+ self._pose = x_rot_mat.dot(self._pose)
pyrender/utils.py ADDED
@@ -0,0 +1,115 @@
1
+ import numpy as np
2
+ from PIL import Image
3
+
4
+
5
+ def format_color_vector(value, length):
6
+ """Format a color vector.
7
+ """
8
+ if isinstance(value, int):
9
+ value = value / 255.0
10
+ if isinstance(value, float):
11
+ value = np.repeat(value, length)
12
+ if isinstance(value, list) or isinstance(value, tuple):
13
+ value = np.array(value)
14
+ if isinstance(value, np.ndarray):
15
+ value = value.squeeze()
16
+ if np.issubdtype(value.dtype, np.integer):
17
+ value = (value / 255.0).astype(np.float32)
18
+ if value.ndim != 1:
19
+ raise ValueError('Format vector takes only 1-D vectors')
20
+ if length > value.shape[0]:
21
+ value = np.hstack((value, np.ones(length - value.shape[0])))
22
+ elif length < value.shape[0]:
23
+ value = value[:length]
24
+ else:
25
+ raise ValueError('Invalid vector data type')
26
+
27
+ return value.squeeze().astype(np.float32)
28
+
29
+
30
+ def format_color_array(value, shape):
31
+ """Format an array of colors.
32
+ """
33
+ # Convert uint8 to floating
34
+ value = np.asanyarray(value)
35
+ if np.issubdtype(value.dtype, np.integer):
36
+ value = (value / 255.0).astype(np.float32)
37
+
38
+ # Match up shapes
39
+ if value.ndim == 1:
40
+ value = np.tile(value, (shape[0],1))
41
+ if value.shape[1] < shape[1]:
42
+ nc = shape[1] - value.shape[1]
43
+ value = np.column_stack((value, np.ones((value.shape[0], nc))))
44
+ elif value.shape[1] > shape[1]:
45
+ value = value[:,:shape[1]]
46
+ return value.astype(np.float32)
47
+
48
+
49
+ def format_texture_source(texture, target_channels='RGB'):
50
+ """Format a texture as a float32 np array.
51
+ """
52
+
53
+ # Pass through None
54
+ if texture is None:
55
+ return None
56
+
57
+ # Convert PIL images into numpy arrays
58
+ if isinstance(texture, Image.Image):
59
+ if texture.mode == 'P' and target_channels in ('RGB', 'RGBA'):
60
+ texture = np.array(texture.convert(target_channels))
61
+ else:
62
+ texture = np.array(texture)
63
+
64
+ # Format numpy arrays
65
+ if isinstance(texture, np.ndarray):
66
+ if np.issubdtype(texture.dtype, np.floating):
67
+ texture = np.array(texture * 255.0, dtype=np.uint8)
68
+ elif np.issubdtype(texture.dtype, np.integer):
69
+ texture = texture.astype(np.uint8)
70
+ else:
71
+ raise TypeError('Invalid type {} for texture'.format(
72
+ type(texture)
73
+ ))
74
+
75
+ # Format array by picking out correct texture channels or padding
76
+ if texture.ndim == 2:
77
+ texture = texture[:,:,np.newaxis]
78
+ if target_channels == 'R':
79
+ texture = texture[:,:,0]
80
+ texture = texture.squeeze()
81
+ elif target_channels == 'RG':
82
+ if texture.shape[2] == 1:
83
+ texture = np.repeat(texture, 2, axis=2)
84
+ else:
85
+ texture = texture[:,:,(0,1)]
86
+ elif target_channels == 'GB':
87
+ if texture.shape[2] == 1:
88
+ texture = np.repeat(texture, 2, axis=2)
89
+ elif texture.shape[2] > 2:
90
+ texture = texture[:,:,(1,2)]
91
+ elif target_channels == 'RGB':
92
+ if texture.shape[2] == 1:
93
+ texture = np.repeat(texture, 3, axis=2)
94
+ elif texture.shape[2] == 2:
95
+ raise ValueError('Cannot reformat 2-channel texture into RGB')
96
+ else:
97
+ texture = texture[:,:,(0,1,2)]
98
+ elif target_channels == 'RGBA':
99
+ if texture.shape[2] == 1:
100
+ texture = np.repeat(texture, 4, axis=2)
101
+ texture[:,:,3] = 255
102
+ elif texture.shape[2] == 2:
103
+ raise ValueError('Cannot reformat 2-channel texture into RGBA')
104
+ elif texture.shape[2] == 3:
105
+ tx = np.empty((texture.shape[0], texture.shape[1], 4), dtype=np.uint8)
106
+ tx[:,:,:3] = texture
107
+ tx[:,:,3] = 255
108
+ texture = tx
109
+ else:
110
+ raise ValueError('Invalid texture channel specification: {}'
111
+ .format(target_channels))
112
+ else:
113
+ raise TypeError('Invalid type {} for texture'.format(type(texture)))
114
+
115
+ return texture
pyrender/version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = '1.0.0'