diff --git a/spatialmath/base/argcheck.py b/spatialmath/base/argcheck.py index 742598aa..90c57e5f 100644 --- a/spatialmath/base/argcheck.py +++ b/spatialmath/base/argcheck.py @@ -186,7 +186,8 @@ def getmatrix( :raises ValueError: if value is not of the specified shape ``getmatrix(m, shape)`` is a 2D matrix with shape ``shape`` formed from - ``m`` which can be a 2D array, 1D array-like or a scalar. + ``m`` which can be a 2D array, a list or tuple of rows, a 1D array-like + (including a ``range``) or a scalar. .. runblock:: pycon @@ -197,6 +198,7 @@ def getmatrix( >>> getmatrix([3,4], (2, 1)) >>> getmatrix([3,4,5,6], (2,2)) >>> getmatrix(np.r_[3,4,5,6], (2,2)) + >>> getmatrix([[0,1,2], [0,1,0]], (2,None)) # list of rows .. note:: @@ -213,6 +215,13 @@ def getmatrix( :seealso: :func:`ismatrix`, :func:`verifymatrix` :SymPy: supported """ + if isinstance(m, range): + m = list(m) + if isinstance(m, (list, tuple)) and not isvector(m): + # passed a nested sequence, such as a list of rows: convert it and + # check its shape below + m = np.array(m) + if isinstance(m, np.ndarray) and len(m.shape) == 2: # passed a 2D array mshape = m.shape @@ -341,7 +350,7 @@ def getvector( where ``vec`` is any of: - a Python native int or float, a 1-vector - - Python native list or tuple + - Python native list, tuple or range - numPy real 1D array, ie. shape=(N,) - numPy real 2D array with a singleton dimension, ie. shape=(1,N) or (N,1) @@ -386,6 +395,8 @@ def getvector( """ dt = dtype + if isinstance(v, range): # a range is a sequence of numbers + v = list(v) if isinstance(v, _scalartypes): # handle scalar case v = [v] # type: ignore if isinstance(v, (list, tuple)): diff --git a/tests/base/test_argcheck.py b/tests/base/test_argcheck.py index bdf603e2..6d190223 100755 --- a/tests/base/test_argcheck.py +++ b/tests/base/test_argcheck.py @@ -110,6 +110,31 @@ def test_getmatrix(self): with self.assertRaises(ValueError): m = getmatrix(a, (2, None)) + def test_getmatrix_nested_sequence(self): + # a list of rows, as for a 2xN path of points (issue #255) + m = getmatrix([[0, 1, 2], [0, 1, 0]], (2, None)) + self.assertIsInstance(m, np.ndarray) + nt.assert_array_equal(m, np.array([[0, 1, 2], [0, 1, 0]])) + self.assertEqual(m.dtype, np.float64) + + # a list of tuples, Nx2 + m = getmatrix([(0, 0), (1, 1), (2, 0)], (None, 2)) + nt.assert_array_equal(m, np.array([[0, 0], [1, 1], [2, 0]])) + + # a tuple of lists, fully specified shape + m = getmatrix(([1, 2], [3, 4]), (2, 2)) + nt.assert_array_equal(m, np.array([[1, 2], [3, 4]])) + + # the shape is checked, not reshaped + with self.assertRaises(ValueError): + getmatrix([[0, 1, 2], [0, 1, 0]], (3, None)) + with self.assertRaises(ValueError): + getmatrix([[0, 0], [1, 1], [2, 0]], (2, None)) + + # a range is a 1D sequence of numbers + m = getmatrix(range(4), (2, 2)) + nt.assert_array_equal(m, np.array([[0, 1], [2, 3]])) + def test_verifymatrix(self): with self.assertRaises(TypeError): assertmatrix(3) @@ -237,6 +262,20 @@ def test_homogeneous_conversion_unsupported_array_shapes(self): with self.assertRaises(ValueError): convert(np.ones(shape)) + def test_getvector_range(self): + # a range is a sequence of numbers (issue #255) + v = getvector(range(1, 7)) + self.assertIsInstance(v, np.ndarray) + nt.assert_array_equal(v, np.r_[1, 2, 3, 4, 5, 6]) + + v = getvector(range(3), 3, out="list") + self.assertEqual(v, [0, 1, 2]) + + v = getvector(range(3), out="col") + nt.assert_equal(v.shape, (3, 1)) + + nt.assert_raises(ValueError, getvector, range(3), 4) + def test_getvector(self): l = [1, 2, 3] t = (1, 2, 3)