Пример #1
0
    def __init__(self, point: array_like, radius: float):

        if radius <= 0:
            raise ValueError("The radius must be positive.")

        self.point = Point(point)
        self.radius = radius

        self.dimension = self.point.dimension
Пример #2
0
    def __init__(self, point_a: array_like, point_b: array_like,
                 point_c: array_like):

        self.point_a = Point(point_a)
        self.point_b = Point(point_b)
        self.point_c = Point(point_c)

        if not (self.point_a.dimension == self.point_b.dimension ==
                self.point_c.dimension):
            raise ValueError("The points must have the same dimension.")

        if Points([self.point_a, self.point_b, self.point_c]).are_collinear():
            raise ValueError("The points must not be collinear.")

        self.dimension = self.point_a.dimension
Пример #3
0
    def project_point(self, point: array_like) -> Point:
        """
        Project a point onto the plane.

        Parameters
        ----------
        point : array_like
            Input point.

        Returns
        -------
        Point
            Projection of the point onto the plane.

        Examples
        --------
        >>> from skspatial.objects import Plane

        >>> plane = Plane(point=[0, 0, 0], normal=[0, 0, 2])

        >>> plane.project_point([10, 2, 5])
        Point([10.,  2.,  0.])

        >>> plane.project_point([5, 9, -3])
        Point([5., 9., 0.])

        """
        # Vector from the point in space to the point on the plane.
        vector_to_plane = Vector.from_points(point, self.point)

        # Perpendicular vector from the point in space to the plane.
        vector_projected = self.normal.project_vector(vector_to_plane)

        return Point(point) + vector_projected
Пример #4
0
    def __init__(self, point: array_like, vector: array_like, **kwargs):

        self.point = Point(point)
        self.vector = Vector(vector)

        if self.point.dimension != self.vector.dimension:
            raise ValueError(
                "The point and vector must have the same dimension.")

        if self.vector.is_zero(**kwargs):
            raise ValueError("The vector must not be the zero vector.")

        self.dimension = self.point.dimension
    def __init__(self, point: array_like, vector: array_like, error = None):

        self.point = Point(point)
        self.vector = Vector(vector)
        self.error = error

        if self.point.dimension != self.vector.dimension:
            raise ValueError("The point and vector must have the same dimension.")

        if self.vector.is_zero(rel_tol=0, abs_tol=0):
            raise ValueError("The vector must not be the zero vector.")

        self.dimension = self.point.dimension
Пример #6
0
    def from_vectors(cls, point: array_like, vector_a: array_like,
                     vector_b: array_like, **kwargs) -> Plane:
        """
        Instantiate a plane from a point and two vectors.

        The two vectors span the plane.

        Parameters
        ----------
        point : array_like
            Point on the plane.
        vector_a, vector_b : array_like
            Input vectors.
        kwargs : dict, optional
            Additional keywords passed to :meth:`Vector.is_parallel`.

        Returns
        -------
        Plane
            Plane containing input point and spanned by the two input vectors.

        Raises
        ------
        ValueError
            If the vectors are parallel.

        Examples
        --------
        >>> from skspatial.objects import Plane

        >>> Plane.from_vectors([0, 0], [1, 0], [0, 1])
        Plane(point=Point([0, 0, 0]), normal=Vector([0, 0, 1]))

        >>> Plane.from_vectors([0, 0], [1, 0], [2, 0])
        Traceback (most recent call last):
        ...
        ValueError: The vectors must not be parallel.

        """
        vector_a = Vector(vector_a)

        if vector_a.is_parallel(vector_b, **kwargs):
            raise ValueError("The vectors must not be parallel.")

        # The cross product returns a 3D vector.
        vector_normal = vector_a.cross(vector_b)

        # Convert the point to 3D so that it matches the vector dimension.
        point = Point(point).set_dimension(3)

        return cls(point, vector_normal)
Пример #7
0
    def centroid(self) -> Point:
        """
        Return the centroid of the points.

        Returns
        -------
        Point
            Centroid of the points.

        Examples
        --------
        >>> from skspatial.objects import Points

        >>> Points([[1, 2, 3], [2, 2, 3]]).centroid()
        Point([1.5, 2. , 3. ])

        """
        return Point(self.mean(axis=0))
Пример #8
0
    def __init__(self, point: array_like, vector: array_like, radius: float):

        self.point = Point(point)
        self.vector = Vector(vector)

        if self.point.dimension != 3:
            raise ValueError("The point must be 3D.")

        if self.vector.dimension != 3:
            raise ValueError("The vector must be 3D.")

        if self.vector.is_zero():
            raise ValueError("The vector must not be the zero vector.")

        if not radius > 0:
            raise ValueError("The radius must be positive.")

        self.radius = radius

        self.dimension = self.point.dimension
Пример #9
0
    def intersect_plane(self, other: Plane) -> Line:
        """
        Intersect the plane with another.

        The planes must not be parallel.

        Parameters
        ----------
        other : Plane
            Other plane.

        Returns
        -------
        Line
            The line of intersection.

        Raises
        ------
        ValueError
            If the planes are parallel.

        References
        ----------
        http://tbirdal.blogspot.com/2016/10/a-better-approach-to-plane-intersection.html

        Examples
        --------
        >>> from skspatial.objects import Plane

        >>> plane_a = Plane([0, 0, 0], [0, 0, 1])
        >>> plane_b = Plane([0, 0, 0], [1, 0, 0])

        >>> plane_a.intersect_plane(plane_b)
        Line(point=Point([0., 0., 0.]), direction=Vector([0, 1, 0]))

        >>> plane_b = Plane([5, 16, -94], [1, 0, 0])
        >>> plane_a.intersect_plane(plane_b)
        Line(point=Point([5., 0., 0.]), direction=Vector([0, 1, 0]))

        >>> plane_b = Plane([0, 0, 1], [1, 0, 1])
        >>> plane_a.intersect_plane(plane_b)
        Line(point=Point([1., 0., 0.]), direction=Vector([0, 1, 0]))

        >>> plane_b = Plane([0, 0, 5], [0, 0, -8])
        >>> plane_a.intersect_plane(plane_b)
        Traceback (most recent call last):
        ...
        ValueError: The planes must not be parallel.

        """
        if self.normal.is_parallel(other.normal, rel_tol=0, abs_tol=0):
            raise ValueError("The planes must not be parallel.")

        array_normals_stacked = np.vstack((self.normal, other.normal))

        # Construct a matrix for a linear system.
        array_00 = 2 * np.eye(3)
        array_01 = array_normals_stacked.T
        array_10 = array_normals_stacked
        array_11 = np.zeros((2, 2))
        matrix = np.block([[array_00, array_01], [array_10, array_11]])

        dot_a = np.dot(self.point, self.normal)
        dot_b = np.dot(other.point, other.normal)
        array_y = np.array([0, 0, 0, dot_a, dot_b])

        # Solve the linear system.
        solution = np.linalg.solve(matrix, array_y)

        point_line = Point(solution[:3])
        direction_line = self.normal.cross(other.normal)

        return Line(point_line, direction_line)
Пример #10
0
    def intersect_line(self, line: Line) -> Tuple[Point, Point]:
        """
        Intersect the circle with a line.

        A line intersects a circle at two points.

        Parameters
        ----------
        line : Line
            Input line.

        Returns
        -------
        point_a, point_b : Point
            The two points of intersection.

        Raises
        ------
        ValueError
            If the line does not intersect the circle.

        References
        ----------
        http://mathworld.wolfram.com/Circle-LineIntersection.html

        Examples
        --------
        >>> from skspatial.objects import Circle, Line

        >>> circle = Circle([0, 0], 1)

        >>> circle.intersect_line(Line(point=[0, 0], direction=[1, 0]))
        (Point([-1.,  0.]), Point([1., 0.]))

        >>> point_a, point_b = circle.intersect_line(Line(point=[0, 0], direction=[1, 1]))

        >>> point_a.round(3)
        Point([-0.707, -0.707])

        >>> point_b.round(3)
        Point([0.707, 0.707])

        >>> circle.intersect_line(Line(point=[1, 2], direction=[1, 1]))
        (Point([-1.,  0.]), Point([0., 1.]))

        If the line is tangent to the circle, the two intersection points are the same.

        >>> circle.intersect_line(Line(point=[1, 0], direction=[0, 1]))
        (Point([1., 0.]), Point([1., 0.]))

        The circle does not have to be centered on the origin.

        >>> point_a, point_b = Circle([2, 3], 5).intersect_line(Line([1, 1], [2, 3]))

        >>> point_a.round(3)
        Point([-0.538, -1.308])

        >>> point_b.round(3)
        Point([5., 7.])

        >>> circle.intersect_line(Line(point=[5, 0], direction=[1, 1]))
        Traceback (most recent call last):
        ...
        ValueError: The line does not intersect the circle.

        """
        # Two points on the line.
        # Copy the line point to avoid changing the line itself.
        point_1 = np.copy(line.point)
        point_2 = point_1 + line.direction.unit()

        # Translate the points on the line to mimic the circle being centered on the origin.
        point_1 -= self.point
        point_2 -= self.point

        x_1, y_1 = point_1
        x_2, y_2 = point_2

        d_x = x_2 - x_1
        d_y = y_2 - y_1

        # Pre-compute variables common to x and y equations.
        d_r_squared = d_x**2 + d_y**2
        determinant = x_1 * y_2 - x_2 * y_1
        discriminant = self.radius**2 * d_r_squared - determinant**2

        if discriminant < 0:
            raise ValueError("The line does not intersect the circle.")

        root = math.sqrt(discriminant)

        pm = np.array([-1, 1])  # Array to compute plus/minus.
        sign = -1 if d_y < 0 else 1

        coords_x = (determinant * d_y + pm * sign * d_x * root) / d_r_squared
        coords_y = (-determinant * d_x + pm * abs(d_y) * root) / d_r_squared

        point_a = Point([coords_x[0], coords_y[0]])
        point_b = Point([coords_x[1], coords_y[1]])

        # Translate the intersection points back from origin circle to real circle.
        point_a += self.point
        point_b += self.point

        return point_a, point_b
Пример #11
0
class _BaseSphere:
    """Private parent class for Circle and Sphere."""
    def __init__(self, point: array_like, radius: float):

        if radius <= 0:
            raise ValueError("The radius must be positive.")

        self.point = Point(point)
        self.radius = radius

        self.dimension = self.point.dimension

    def __repr__(self) -> str:

        name_class = type(self).__name__

        repr_point = np.array_repr(self.point)

        return f"{name_class}(point={repr_point}, radius={self.radius})"

    def distance_point(self, point: array_like) -> np.float64:
        """Return the distance from a point to the circle/sphere."""
        distance_to_center = self.point.distance_point(point)

        return abs(distance_to_center - self.radius)

    def contains_point(self, point: array_like, **kwargs: float) -> bool:
        """Check if the line/plane contains a point."""
        return _contains_point(self, point, **kwargs)

    def project_point(self, point: array_like) -> Point:
        """
        Project a point onto the circle or sphere.

        Parameters
        ----------
        point : array_like
            Input point.

        Returns
        -------
        Point
            Point projected onto the circle or sphere.

        Raises
        ------
        ValueError
            If the input point is the center of the circle or sphere.

        Examples
        --------
        >>> from skspatial.objects import Circle

        >>> circle = Circle([0, 0], 1)

        >>> circle.project_point([1, 1]).round(3)
        Point([0.707, 0.707])

        >>> circle.project_point([-6, 3]).round(3)
        Point([-0.894,  0.447])

        >>> circle.project_point([0, 0])
        Traceback (most recent call last):
        ...
        ValueError: The point must not be the center of the circle or sphere.

        >>> from skspatial.objects import Sphere

        >>> Sphere([0, 0, 0], 2).project_point([1, 2, 3]).round(3)
        Point([0.535, 1.069, 1.604])

        """
        if self.point.is_equal(point):
            raise ValueError(
                "The point must not be the center of the circle or sphere.")

        vector_to_point = Vector.from_points(self.point, point)

        return self.point + self.radius * vector_to_point.unit()

    def plotter(
            self, **kwargs
    ) -> Union[Callable[[Axes], None], Callable[[Axes3D], None]]:

        return _plotter(self, **kwargs)