def test_perpendicular(): v1 = np.array([1.0, 2.0, 3.0]) v2 = np.array([4.0, 5.0, 6.0]) expected = np.array([-3.0, 6.0, -3.0]) expected_magnitude = math.sqrt(9.0 + 36.0 + 9.0) np.testing.assert_array_almost_equal( vg.perpendicular(v1, v2, normalized=False), expected) np.testing.assert_array_almost_equal( vg.perpendicular(v1, v2, normalized=True), expected / expected_magnitude)
def test_perpendicular_mixed(): v1 = np.array([[1.0, 0.0, -1.0], [1.0, 2.0, 3.0]]) v2 = np.array([4.0, 5.0, 6.0]) expected = np.array([[5.0, -10.0, 5.0], [-3.0, 6.0, -3.0]]) expected_magnitude = np.array( [math.sqrt(25.0 + 100.0 + 25.0), math.sqrt(9.0 + 36.0 + 9.0)]) np.testing.assert_array_almost_equal( vg.perpendicular(v1, v2, normalized=False), expected) np.testing.assert_array_almost_equal( vg.perpendicular(v1, v2, normalized=True), expected / expected_magnitude.reshape(-1, 1), )
def normal(self, other: "Vector") -> "Vector": """Compute the vector normal to this vector & another vector Note: This is equivalent to computing the normalized cross product between this vector & the other vector, and so is anticommutative. Args: other: The vector with respect to which to compute the normal Raises: NoNormalForParallels: If the input vectors are parallel NoNormalForAntiparallels: If the input vectors are antiparallel NoNormalForNulls: If one of the vectors has 0 magnitude """ try: return self.from_components( vg.perpendicular(self.array, other.array)) except NaNVector: if self.magnitude and other.magnitude: if self.normalized == other.normalized: raise NoNormalForParallels(self, other) else: raise NoNormalForAntiparallels(self, other) else: raise NoNormalForNulls(min(self, other), max(self, other))
def test_project_to_line_stacked_both(): p1 = np.array([5.0, 5.0, 4.0]) p2 = np.array([10.0, 10.0, 6.0]) along_line = p2 - p1 common_kwargs = dict( reference_points_of_lines=np.array([p1, p1, p1]), vectors_along_lines=np.array([along_line, along_line, along_line]), ) other_point_on_line = np.array([0.0, 0.0, 2.0]) example_perpendicular_displacement = [ k * vg.perpendicular(vg.normalize(along_line), vg.basis.x) for k in [0.1, 0.5, -2.0] ] example_points = np.vstack([p1, p2, other_point_on_line]) expected_projected_points = np.vstack([p1, p2, other_point_on_line]) np.testing.assert_array_almost_equal( project_to_line(points=example_points, **common_kwargs), expected_projected_points, ) np.testing.assert_array_almost_equal( project_to_line(points=example_points + example_perpendicular_displacement, **common_kwargs), expected_projected_points, )
def test_project_to_line(): p1 = np.array([5.0, 5.0, 4.0]) p2 = np.array([10.0, 10.0, 6.0]) along_line = p2 - p1 common_kwargs = dict(reference_points_of_lines=p1, vectors_along_lines=along_line) np.testing.assert_array_almost_equal( project_to_line(points=p1, **common_kwargs), p1) np.testing.assert_array_almost_equal( project_to_line(points=p2, **common_kwargs), p2) other_point_on_line = np.array([0.0, 0.0, 2.0]) np.testing.assert_array_almost_equal( project_to_line(points=other_point_on_line, **common_kwargs), other_point_on_line, ) example_perpendicular_displacement = [ k * vg.perpendicular(vg.normalize(along_line), vg.basis.x) for k in [0.1, 0.5, -2.0] ] for point_on_line in [p1, p2, other_point_on_line]: for displacement in example_perpendicular_displacement: np.testing.assert_array_almost_equal( project_to_line(points=point_on_line + displacement, **common_kwargs), point_on_line, )
def test_perpendicular_error(): v1 = np.array([[1.0, 0.0, -1.0], [1.0, 2.0, 3.0]]) v2 = np.array([[4.0, 5.0, 6.0]]) with pytest.raises( ValueError, match="v2 must be an array with shape \\(2, 3\\); got \\(1, 3\\)"): vg.perpendicular(v1, v2, normalized=False) v1 = np.array([[1.0, 0.0, -1.0], [1.0, 2.0, 3.0]]) v2 = np.array([[[4.0, 5.0, 6.0]]]) with pytest.raises( ValueError, match="Not sure what to do with 2 dimensions and 3 dimensions"): vg.perpendicular(v1, v2, normalized=False)
def test_perpendicular_stacked(): v1 = np.array([[1.0, 0.0, -1.0], [1.0, 2.0, 3.0]]) v2 = np.array([[2.0, 2.0, 2.0], [4.0, 5.0, 6.0]]) expected = np.array([[2.0, -4.0, 2.0], [-3.0, 6.0, -3.0]]) expected_magnitude = np.array( [math.sqrt(4.0 + 16.0 + 4.0), math.sqrt(9.0 + 36.0 + 9.0)]) np.testing.assert_array_almost_equal( vg.perpendicular(v1, v2, normalized=False), expected) np.testing.assert_array_almost_equal( vg.perpendicular(v1, v2, normalized=True), expected / expected_magnitude[:, np.newaxis], )
def tilted(self, new_point, coplanar_point): """ Create a new plane, tilted so it passes through `new_point`. Also specify a `coplanar_point` which the old and new planes should have in common. Args: new_point (np.arraylike): A point on the desired plane, with shape `(3,)`. coplanar_point (np.arraylike): The `(3,)` point which the old and new planes have in common. Returns: Plane: The adjusted plane. """ vg.shape.check(locals(), "new_point", (3, )) vg.shape.check(locals(), "coplanar_point", (3, )) vector_along_old_plane = self.project_point(new_point) - coplanar_point vector_along_new_plane = new_point - coplanar_point axis_of_rotation = vg.perpendicular(vector_along_old_plane, self.normal) angle_between_vectors = vg.signed_angle( vector_along_old_plane, vector_along_new_plane, look=axis_of_rotation, units="rad", ) new_normal = vg.rotate( self.normal, around_axis=axis_of_rotation, angle=angle_between_vectors, units="rad", ) return Plane(point_on_plane=coplanar_point, unit_normal=new_normal)
def _rotmat(self, vector, points): """ Rotates a 3xn array of 3D coordinates from the +z normal to an arbitrary new normal vector. """ vector = vg.normalize(vector) axis = vg.perpendicular(vg.basis.z, vector) angle = vg.angle(vg.basis.z, vector, units='rad') a = np.hstack((axis, (angle, ))) R = matrix_from_axis_angle(a) r = Rot.from_matrix(R) rotmat = r.apply(points) return rotmat
def test_project(): p1 = np.array([5.0, 5.0, 4.0]) p2 = np.array([10.0, 10.0, 6.0]) line = Line.from_points(p1, p2) np.testing.assert_array_almost_equal(line.project(p1), p1) np.testing.assert_array_almost_equal(line.project(p2), p2) other_point_on_line = np.array([0.0, 0.0, 2.0]) np.testing.assert_array_almost_equal(line.project(other_point_on_line), other_point_on_line) example_perpendicular_displacement = [ k * vg.perpendicular(line.along, vg.basis.x) for k in [0.1, 0.5, -2.0] ] for p in [p1, p2, other_point_on_line]: for displacement in example_perpendicular_displacement: np.testing.assert_array_almost_equal( line.project(p + displacement), p)
def test_project_stacked(): p1 = np.array([5.0, 5.0, 4.0]) p2 = np.array([10.0, 10.0, 6.0]) line = Line.from_points(p1, p2) other_point_on_line = np.array([0.0, 0.0, 2.0]) example_perpendicular_displacement = [ k * vg.perpendicular(line.along, vg.basis.x) for k in [0.1, 0.5, -2.0] ] example_points = np.vstack([p1, p2, other_point_on_line]) expected_projected_points = np.vstack([p1, p2, other_point_on_line]) np.testing.assert_array_almost_equal(line.project(example_points), expected_projected_points) np.testing.assert_array_almost_equal( line.project(example_points + example_perpendicular_displacement), expected_projected_points, )