def test_broadcast(self): """Test broadcasting.""" vec1 = np.array([1, 0, 0]) vec2 = np.array([0, 1, 0]) vec3 = np.array([0, 0, 1]) arr1 = np.stack((vec2, vec3), axis=0) output = np.array([ [0, np.sqrt(2) / 2, np.sqrt(2) / 2, 0], [0, np.sqrt(2) / 2, 0, np.sqrt(2) / 2], ]) # Test both directions of single array broadcasting quat = rowan.vector_vector_rotation(vec1, arr1) self.assertTrue(np.allclose(quat, output)) quat = rowan.vector_vector_rotation(arr1, vec1) self.assertTrue(np.allclose(quat, output)) # Matching sizes arr2 = np.stack((vec1, vec1), axis=0) quat = rowan.vector_vector_rotation(arr1, arr2) self.assertTrue(np.allclose(quat, output)) # Proper broadcasting arr1 = np.stack((vec2, vec3), axis=0)[:, np.newaxis, ...] arr2 = np.stack((vec1, vec1), axis=0)[np.newaxis, ...] bcast_output = output[:, np.newaxis, ...].repeat(2, axis=1) quat = rowan.vector_vector_rotation(arr1, arr2) self.assertTrue(np.allclose(quat, bcast_output))
def test_ap(self): """Test finding quaternion to rotate antiparallel vectors onto each other.""" # For this test, there are multiple quaternions that would effect the # correct rotation, so rather than checking for a specific one we check # that the appropriate rotation results from applying the quaternion vec1 = np.array([1, 0, 0]) vec2 = np.array([0, 1, 0]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose(rowan.rotate(quat, vec1), vec2 / np.linalg.norm(vec2, axis=-1))) vec1 = np.array([1, 0, 0]) vec2 = np.array([[0, 1, 0], [2, 0, 0], [-2, 0, 0]]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose( rowan.rotate(quat, vec1), vec2 / np.linalg.norm(vec2, axis=-1)[:, np.newaxis], )) vec1 = np.array([0, 1, 0]) vec2 = np.array([[0, 0, 1], [0, 2, 0], [0, -2, 0]]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose( rowan.rotate(quat, vec1), vec2 / np.linalg.norm(vec2, axis=-1)[:, np.newaxis], ))
def test_simple(self): """Test finding quaternion to rotate a vector onto another vector.""" vec1 = np.array([1, 0, 0]) vec2 = np.array([0, 1, 0]) vec3 = np.array([0, 0, 1]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose(quat, np.array([[0, np.sqrt(2) / 2, np.sqrt(2) / 2, 0]]))) quat = rowan.vector_vector_rotation(vec1, vec3) self.assertTrue( np.allclose(quat, np.array([[0, np.sqrt(2) / 2, 0, np.sqrt(2) / 2]])))
def field_scene(N=10, use='ellipsoids'): features = dict(ambient_light=.4) (positions, units) = example_vector_field(5) normalized_z = positions[:, 2].copy() normalized_z -= np.min(normalized_z) normalized_z /= np.max(normalized_z) colors = plato.cmap.cubehelix(normalized_z, h=1.4) colors[:, :3] = .5 * (colors[:, :3] + plato.cmap.cubeellipse( np.arctan2(positions[:, 1], positions[:, 0]))) if use == 'ellipsoids': orientations = rowan.vector_vector_rotation([(1, 0, 0)], units) prim = draw.Ellipsoids(positions=positions, orientations=orientations, colors=colors, a=.5, b=.125, c=.125) elif use == 'lines': features['ambient_light'] = 1 starts = positions - units / 2 ends = positions + units / 2 prim = draw.Lines(start_points=starts, end_points=ends, colors=colors, widths=np.ones(len(positions)) * .25) else: raise NotImplementedError('Unknown primitive {}'.format(use)) rotation = [0.8126942, 0.35465172, -0.43531808, 0.15571932] scene = draw.Scene(prim, zoom=4, features=features, rotation=rotation) return scene
def get_quaternions(n_views=20): """Get the quaternions for the specified number of views. The first (n_view - 3) views will be the views even distributed on a sphere, while the last three views will be the face-on, edge-on, and corner-on views, respectively. These quaternions are useful as input to `view_orientation` kwarg in `freud.diffraction.Diffractometer.compute`. Parameters ---------- n_views : int, default 20 The number of views to compute. Returns ------- list of numpy.ndarray Quaternions as (4,) arrays. """ if n_views <= 3 or not isinstance(n_views, int): raise ValueError("Please set n_views to an integer greater than 3.") # Calculate points for even distribution on a sphere ga = np.pi * (3 - 5**0.5) theta = ga * np.arange(n_views - 3) z = np.linspace(1 - 1 / (n_views - 3), 1 / (n_views - 3), n_views - 3) radius = np.sqrt(1 - z * z) points = np.zeros((n_views, 3)) points[:-3, 0] = radius * np.cos(theta) points[:-3, 1] = radius * np.sin(theta) points[:-3, 2] = z # face on points[-3] = np.array([0, 0, 1]) # edge on points[-2] = np.array([0, 1, 1]) # corner on points[-1] = np.array([1, 1, 1]) unit_z = np.array([0, 0, 1]) return [vector_vector_rotation(i, unit_z) for i in points]
def update_arrays(self): if not self._dirty_attributes: return # TODO make number of facets a configurable attribute thetas = np.linspace(0, 2 * np.pi, 10, endpoint=False) # to begin, pretend we are constructing a unit circle in the # xy plane; multiply xy by each line radius and z by each line # length before rotating and translating appropriately image = np.array( [np.cos(thetas), np.sin(thetas), np.zeros_like(thetas)]).T image = np.concatenate([image, image + (0, 0, 1)], axis=0) # indices to make all triangles for a given Line object image_indices = np.tile( np.arange(2 * len(thetas))[:, np.newaxis], (1, 3)) image_indices[:len(thetas), 1] += 1 image_indices[:len(thetas), 2] += len(thetas) image_indices[len(thetas):, 1] += 1 - len(thetas) image_indices[len(thetas):, 2] += 1 image_indices[len(thetas) - 1, 1] -= len(thetas) image_indices[-1, 1:] -= len(thetas) cap_indices = np.array( list( geometry.fanTriangleIndices( np.arange(len(thetas))[np.newaxis]))) image_indices = np.concatenate( [image_indices, cap_indices[:, ::-1], len(thetas) + cap_indices], axis=0) # normal vectors for each Line segment normals = self.end_points - self.start_points lengths = np.linalg.norm(normals, axis=-1, keepdims=True) normals /= lengths # find quaternion to rotate (0, 0, 1) into the direction each # Line is pointing quats = rowan.vector_vector_rotation( np.array([0, 0, 1.])[np.newaxis, :], normals) # Nlines*Nvertices*3 vertices = np.tile(image[np.newaxis], (len(quats), 1, 1)) # set xy according to line width vertices[:, :, :2] *= self.widths[:, np.newaxis, np.newaxis] * 0.5 # set z according to line length vertices[:, :, 2] *= lengths[:, np.newaxis, 0] # rotate appropriately vertices = rowan.rotate(quats[:, np.newaxis], vertices) # translation by start_points will happen in _finalize_primitive_arrays # these are incorrect normals, but this looks to be the most # straightforward way to have the normals get serialized normals = vertices - self.start_points[:, np.newaxis] colors = np.tile(self.colors[:, np.newaxis], (1, len(image), 1)) positions = np.tile(self.start_points[:, np.newaxis], (1, len(image), 1)) self._finalize_primitive_arrays(positions, None, colors, vertices, normals, image_indices)