Skip to content

Commit

Permalink
Merge pull request #255 from ajhynes7/improve_unit_tests
Browse files Browse the repository at this point in the history
Improve unit tests
  • Loading branch information
ajhynes7 committed Mar 7, 2021
2 parents 6fba81c + cee72aa commit 05b9843
Show file tree
Hide file tree
Showing 39 changed files with 1,656 additions and 1,397 deletions.
7 changes: 3 additions & 4 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[run]
source = skspatial
omit =
skspatial/plotting.py
skspatial/tests/*
*/plotting.py

[report]
exclude_lines=def *plot*
exclude_lines =
def *plot*
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ repos:
- flake8-builtins==1.5.3
- flake8-comprehensions==3.3.0
- flake8-eradicate==1.0.0
- flake8-pytest-style==1.3.0
- flake8-rst-docstrings==0.0.14
- flake8-unused-arguments==0.0.6
- repo: https://github.com/PyCQA/pydocstyle
Expand Down
1 change: 1 addition & 0 deletions requirements/pre-commit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pre-commit==2.10.1
11 changes: 10 additions & 1 deletion src/skspatial/objects/_base_array.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Private base classes for arrays."""
import warnings
from typing import cast
from typing import Type
from typing import TypeVar
Expand All @@ -9,7 +10,6 @@
from skspatial.objects._base_spatial import _BaseSpatial
from skspatial.typing import array_like


# Create generic variables that can be 'Parent' or any subclass.
Array = TypeVar('Array', bound='_BaseArray')

Expand All @@ -23,6 +23,15 @@ class _BaseArray(np.ndarray, _BaseSpatial):

def __new__(cls: Type[Array], array: array_like) -> Array:

try:
warnings.filterwarnings("error")
np.array(array)

except np.VisibleDeprecationWarning as error:

if str(error).startswith("Creating an ndarray from ragged nested sequences"):
raise ValueError("The array must not contain sequences with different lengths.")

if np.size(array) == 0:
raise ValueError("The array must not be empty.")

Expand Down
4 changes: 2 additions & 2 deletions src/skspatial/objects/_base_line_plane.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
class _BaseLinePlane(_BaseSpatial):
"""Private parent class for Line and Plane."""

def __init__(self, point: array_like, vector: array_like):
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(rel_tol=0, abs_tol=0):
if self.vector.is_zero(**kwargs):
raise ValueError("The vector must not be the zero vector.")

self.dimension = self.point.dimension
Expand Down
16 changes: 8 additions & 8 deletions src/skspatial/objects/circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ def intersect_line(self, line: Line) -> Tuple[Point, 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
point_translated_1 = point_1 - self.point
point_translated_2 = point_2 - self.point

x_1, y_1 = point_1
x_2, y_2 = point_2
x_1, y_1 = point_translated_1
x_2, y_2 = point_translated_2

d_x = x_2 - x_1
d_y = y_2 - y_1
Expand All @@ -225,12 +225,12 @@ def intersect_line(self, line: Line) -> Tuple[Point, Point]:
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]])
point_translated_a = Point([coords_x[0], coords_y[0]])
point_translated_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
point_a = point_translated_a + self.point
point_b = point_translated_b + self.point

return point_a, point_b

Expand Down
2 changes: 1 addition & 1 deletion src/skspatial/objects/cylinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __repr__(self) -> str:
return f"Cylinder(point={repr_point}, vector={repr_vector}, radius={self.radius})"

@classmethod
def from_points(cls, point_a: array_like, point_b: array_like, radius: float) -> 'Cylinder':
def from_points(cls, point_a: array_like, point_b: array_like, radius: float) -> Cylinder:
"""
Instantiate a cylinder from two points and a radius.
Expand Down
56 changes: 43 additions & 13 deletions src/skspatial/objects/line.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Module for the Line class."""
from __future__ import annotations

import numpy as np
from matplotlib.axes import Axes
from mpl_toolkits.mplot3d import Axes3D
Expand All @@ -25,6 +27,9 @@ class Line(_BaseLinePlane):
Point on the line.
direction : array_like
Direction vector of the line.
kwargs : dict, optional
Additional keywords passed to :meth:`Vector.is_zero`.
This method is used to ensure that the direction vector is not the zero vector.
Attributes
----------
Expand Down Expand Up @@ -84,7 +89,7 @@ def __init__(self, point: array_like, direction: array_like):
self.direction = self.vector

@classmethod
def from_points(cls, point_a: array_like, point_b: array_like) -> 'Line':
def from_points(cls, point_a: array_like, point_b: array_like) -> Line:
"""
Instantiate a line from two points.
Expand Down Expand Up @@ -116,7 +121,7 @@ def from_points(cls, point_a: array_like, point_b: array_like) -> 'Line':
return cls(point_a, vector_ab)

@classmethod
def from_slope(cls, slope: float, y_intercept: float) -> 'Line':
def from_slope(cls, slope: float, y_intercept: float) -> Line:
r"""
Instantiate a 2D line from a slope and Y-intercept.
Expand Down Expand Up @@ -437,7 +442,7 @@ def distance_line(self, other: 'Line') -> np.float64:

return distance

def intersect_line(self, other: 'Line') -> Point:
def intersect_line(self, other: 'Line', **kwargs) -> Point:
"""
Intersect the line with another.
Expand All @@ -447,6 +452,8 @@ def intersect_line(self, other: 'Line') -> Point:
----------
other : Line
Other line.
kwargs : dict, optional
Additional keywords passed to :meth:`Vector.is_parallel`.
Returns
-------
Expand All @@ -468,10 +475,32 @@ def intersect_line(self, other: 'Line') -> Point:
>>> line_a = Line([0, 0], [1, 0])
>>> line_b = Line([5, 5], [0, 1])
>>> line_a.intersect_line(line_b)
Point([5., 0.])
>>> line_a = Line([0, 0, 0], [1, 1, 1])
>>> line_b = Line([5, 5, 0], [0, 0, -8])
>>> line_a.intersect_line(line_b)
Point([5., 5., 5.])
>>> line_a = Line([0, 0, 0], [1, 0, 0])
>>> line_b = Line([0, 0], [1, 1])
>>> line_a.intersect_line(line_b)
Traceback (most recent call last):
...
ValueError: The lines must have the same dimension.
>>> line_a = Line(4 * [0], [1, 0, 0, 0])
>>> line_b = Line(4 * [0], [0, 0, 0, 1])
>>> line_a.intersect_line(line_b)
Traceback (most recent call last):
...
ValueError: The line dimension cannot be greater than 3.
>>> line_a = Line([0, 0], [0, 1])
>>> line_b = Line([0, 1], [0, 1])
>>> line_a = Line([0, 0], [1, 0])
>>> line_b = Line([0, 1], [2, 0])
>>> line_a.intersect_line(line_b)
Traceback (most recent call last):
Expand All @@ -480,20 +509,19 @@ def intersect_line(self, other: 'Line') -> Point:
>>> line_a = Line([1, 2, 3], [-4, 1, 1])
>>> line_b = Line([4, 5, 6], [3, 1, 5])
>>> line_a.intersect_line(line_b)
Traceback (most recent call last):
...
ValueError: The lines must be coplanar.
>>> line_a = Line([0, 0, 0], [1, 1, 1])
>>> line_b = Line([5, 5, 0], [0, 0, -8])
"""
if self.dimension != other.dimension:
raise ValueError("The lines must have the same dimension.")

>>> line_a.intersect_line(line_b)
Point([5., 5., 5.])
if self.dimension > 3 or other.dimension > 3:
raise ValueError("The line dimension cannot be greater than 3.")

"""
if self.direction.is_parallel(other.direction, rel_tol=0, abs_tol=0):
if self.direction.is_parallel(other.direction, **kwargs):
raise ValueError("The lines must not be parallel.")

if not self.is_coplanar(other):
Expand All @@ -514,14 +542,16 @@ def intersect_line(self, other: 'Line') -> Point:
return self.point + vector_a_scaled

@classmethod
def best_fit(cls, points: array_like, **kwargs) -> 'Line':
def best_fit(cls, points: array_like, tol: float | None = None, **kwargs) -> Line:
"""
Return the line of best fit for a set of points.
Parameters
----------
points : array_like
Input points.
tol : float | None, optional
Keyword passed to :meth:`Points.are_collinear` (default None).
kwargs : dict, optional
Additional keywords passed to :func:`numpy.linalg.svd`
Expand Down Expand Up @@ -555,7 +585,7 @@ def best_fit(cls, points: array_like, **kwargs) -> 'Line':
"""
points_spatial = Points(points)

if points_spatial.are_concurrent(tol=0):
if points_spatial.are_concurrent(tol=tol):
raise ValueError("The points must not be concurrent.")

points_centered, centroid = points_spatial.mean_center(return_centroid=True)
Expand Down

0 comments on commit 05b9843

Please sign in to comment.