12
12
13
13
_numtypes = (int , np .int64 , float , np .float64 )
14
14
15
+
15
16
class BasePoseList (UserList , ABC ):
16
17
"""
17
18
List properties for spatial math classes
18
19
19
20
Each of the spatial math classes behaves like a regular Python object and
20
- an instance contains a value of a particular type, for example an SE(3)
21
+ an instance contains a value of a particular type, for example an SE(3)
21
22
matrix, a unit quaternion, a twist etc.
22
23
23
24
This class adds list-like capabilities to each of spatial math classes. This
24
- means that an instance is not limited to holding just a single value (a
25
- singleton instance), it can hold a list of values. That list can contain
25
+ means that an instance is not limited to holding just a single value (a
26
+ singleton instance), it can hold a list of values. That list can contain
26
27
zero or more items. This is helpful for:
27
-
28
+
28
29
- storing sequences (trajectories) where it is important to know that all
29
30
elements in the sequence are of the same time and have valid values
30
31
- arrays of the same type to enable C++ like programming patterns
@@ -86,7 +87,7 @@ def _import(self, x, check=True):
86
87
def Empty (cls ):
87
88
"""
88
89
Construct an empty instance (BasePoseList superclass method)
89
-
90
+
90
91
:return: pose instance with zero values
91
92
92
93
Example::
@@ -117,7 +118,7 @@ def Alloc(cls, n=1):
117
118
can be referenced ``X[i]`` or assigned to ``X[i] = ...``.
118
119
119
120
.. note:: The default value depends on the pose class and is the result
120
- of the empty constructor. For ``SO2``,
121
+ of the empty constructor. For ``SO2``,
121
122
``SE2``, ``SO3``, ``SE3`` it is an identity matrix, for a
122
123
twist class ``Twist2`` or ``Twist3`` it is a zero vector,
123
124
for a ``UnitQuaternion`` or ``Quaternion`` it is a zero
@@ -195,10 +196,16 @@ def arghandler(self, arg, convertfrom=(), check=True):
195
196
196
197
elif type (arg [0 ]) == type (self ):
197
198
# possibly a list of objects of same type
198
- assert all (map (lambda x : type (x ) == type (self ), arg )), 'elements of list are incorrect type'
199
+ assert all (
200
+ map (lambda x : type (x ) == type (self ), arg )
201
+ ), "elements of list are incorrect type"
199
202
self .data = [x .A for x in arg ]
200
203
201
- elif argcheck .isnumberlist (arg ) and len (self .shape ) == 1 and len (arg ) == self .shape [0 ]:
204
+ elif (
205
+ argcheck .isnumberlist (arg )
206
+ and len (self .shape ) == 1
207
+ and len (arg ) == self .shape [0 ]
208
+ ):
202
209
self .data = [np .array (arg )]
203
210
204
211
else :
@@ -215,7 +222,9 @@ def arghandler(self, arg, convertfrom=(), check=True):
215
222
# get method to convert from arg to self types
216
223
converter = getattr (arg .__class__ , type (self ).__name__ )
217
224
except AttributeError :
218
- raise ValueError ('argument has no conversion method to this type' ) from None
225
+ raise ValueError (
226
+ "argument has no conversion method to this type"
227
+ ) from None
219
228
self .data = [converter (arg ).A ]
220
229
221
230
else :
@@ -224,6 +233,15 @@ def arghandler(self, arg, convertfrom=(), check=True):
224
233
225
234
return True
226
235
236
+ @property
237
+ def __array_interface__ (self ):
238
+ """
239
+ Copies the numpy array interface from the first numpy array
240
+ so that C extenstions with this spatial math class have direct
241
+ access to the underlying numpy array
242
+ """
243
+ return self .data [0 ].__array_interface__
244
+
227
245
@property
228
246
def _A (self ):
229
247
"""
@@ -238,18 +256,18 @@ def _A(self):
238
256
return self .data
239
257
240
258
@property
241
- def A (self ):
259
+ def A (self ) -> np . ndarray :
242
260
"""
243
261
Array value of an instance (BasePoseList superclass method)
244
262
245
263
:return: NumPy array value of this instance
246
264
:rtype: ndarray
247
265
248
- - ``X.A`` is a NumPy array that represents the value of this instance,
266
+ - ``X.A`` is a NumPy array that represents the value of this instance,
249
267
and has a shape given by ``X.shape``.
250
268
251
269
.. note:: This assumes that ``len(X)`` == 1, ie. it is a single-valued
252
- instance.
270
+ instance.
253
271
"""
254
272
255
273
if len (self .data ) == 1 :
@@ -270,9 +288,9 @@ def __getitem__(self, i):
270
288
:raises IndexError: if the element is out of bounds
271
289
272
290
Note that only a single index is supported, slices are not.
273
-
291
+
274
292
Example::
275
-
293
+
276
294
>>> x = X.Alloc(10)
277
295
>>> len(x)
278
296
10
@@ -296,14 +314,19 @@ def __getitem__(self, i):
296
314
else :
297
315
# stop is positive, use it directly
298
316
end = i .stop
299
- return self .__class__ ([self .data [k ] for k in range (i .start or 0 , end , i .step or 1 )])
317
+ return self .__class__ (
318
+ [self .data [k ] for k in range (i .start or 0 , end , i .step or 1 )]
319
+ )
300
320
else :
301
- return self .__class__ (self .data [i ], check = False )
302
-
321
+ ret = self .__class__ (self .data [i ], check = False )
322
+ # ret.__array_interface__ = self.data[i].__array_interface__
323
+ return ret
324
+ # return self.__class__(self.data[i], check=False)
325
+
303
326
def __setitem__ (self , i , value ):
304
327
"""
305
328
Assign a value to an instance (BasePoseList superclass method)
306
-
329
+
307
330
:param i: index of element to assign to
308
331
:type i: int
309
332
:param value: the value to insert
@@ -312,7 +335,7 @@ def __setitem__(self, i, value):
312
335
313
336
Assign the argument to an element of the object's internal list of values.
314
337
This supports the assignement operator, for example::
315
-
338
+
316
339
>>> x = X.Alloc(10)
317
340
>>> len(x)
318
341
10
@@ -324,7 +347,9 @@ def __setitem__(self, i, value):
324
347
if not type (self ) == type (value ):
325
348
raise ValueError ("can't insert different type of object" )
326
349
if len (value ) > 1 :
327
- raise ValueError ("can't insert a multivalued element - must have len() == 1" )
350
+ raise ValueError (
351
+ "can't insert a multivalued element - must have len() == 1"
352
+ )
328
353
self .data [i ] = value .A
329
354
330
355
# flag these binary operators as being not supported
@@ -343,7 +368,7 @@ def __ge__(self, other):
343
368
def append (self , item ):
344
369
"""
345
370
Append a value to an instance (BasePoseList superclass method)
346
-
371
+
347
372
:param x: the value to append
348
373
:type x: Quaternion or UnitQuaternion instance
349
374
:raises ValueError: incorrect type of appended object
@@ -361,18 +386,17 @@ def append(self, item):
361
386
362
387
where ``X`` is any of the SMTB classes.
363
388
"""
364
- #print('in append method')
389
+ # print('in append method')
365
390
if not type (self ) == type (item ):
366
391
raise ValueError ("can't append different type of object" )
367
392
if len (item ) > 1 :
368
393
raise ValueError ("can't append a multivalued instance - use extend" )
369
394
super ().append (item .A )
370
-
371
395
372
396
def extend (self , iterable ):
373
397
"""
374
398
Extend sequence of values in an instance (BasePoseList superclass method)
375
-
399
+
376
400
:param x: the value to extend
377
401
:type x: instance of same type
378
402
:raises ValueError: incorrect type of appended object
@@ -390,7 +414,7 @@ def extend(self, iterable):
390
414
391
415
where ``X`` is any of the SMTB classes.
392
416
"""
393
- #print('in extend method')
417
+ # print('in extend method')
394
418
if not type (self ) == type (iterable ):
395
419
raise ValueError ("can't append different type of object" )
396
420
super ().extend (iterable ._A )
@@ -427,9 +451,11 @@ def insert(self, i, item):
427
451
if not type (self ) == type (item ):
428
452
raise ValueError ("can't insert different type of object" )
429
453
if len (item ) > 1 :
430
- raise ValueError ("can't insert a multivalued instance - must have len() == 1" )
454
+ raise ValueError (
455
+ "can't insert a multivalued instance - must have len() == 1"
456
+ )
431
457
super ().insert (i , item ._A )
432
-
458
+
433
459
def pop (self , i = - 1 ):
434
460
"""
435
461
Pop value from an instance (BasePoseList superclass method)
@@ -442,7 +468,7 @@ def pop(self, i=-1):
442
468
443
469
Removes a value from the value list and returns it. The original
444
470
instance is modified.
445
-
471
+
446
472
Example::
447
473
448
474
>>> x = X.Alloc(10)
@@ -462,7 +488,7 @@ def pop(self, i=-1):
462
488
def binop (self , right , op , op2 = None , list1 = True ):
463
489
"""
464
490
Perform binary operation
465
-
491
+
466
492
:param left: left operand
467
493
:type left: BasePoseList subclass
468
494
:param right: right operand
@@ -523,7 +549,7 @@ def binop(self, right, op, op2=None, list1=True):
523
549
524
550
# class * class
525
551
if len (left ) == 1 :
526
- # singleton *
552
+ # singleton *
527
553
if argcheck .isscalar (right ):
528
554
if list1 :
529
555
return [op (left ._A , right )]
@@ -539,7 +565,7 @@ def binop(self, right, op, op2=None, list1=True):
539
565
# singleton * non-singleton
540
566
return [op (left .A , x ) for x in right .A ]
541
567
else :
542
- # non-singleton *
568
+ # non-singleton *
543
569
if argcheck .isscalar (right ):
544
570
return [op (x , right ) for x in left .A ]
545
571
elif len (right ) == 1 :
@@ -549,12 +575,12 @@ def binop(self, right, op, op2=None, list1=True):
549
575
# non-singleton * non-singleton
550
576
return [op (x , y ) for (x , y ) in zip (left .A , right .A )]
551
577
else :
552
- raise ValueError (' length of lists to == must be same length' )
578
+ raise ValueError (" length of lists to == must be same length" )
553
579
554
580
# if isinstance(right, left.__class__):
555
581
# # class * class
556
582
# if len(left) == 1:
557
- # # singleton *
583
+ # # singleton *
558
584
# if len(right) == 1:
559
585
# # singleton * singleton
560
586
# if list1:
@@ -565,7 +591,7 @@ def binop(self, right, op, op2=None, list1=True):
565
591
# # singleton * non-singleton
566
592
# return [op(left.A, x) for x in right.A]
567
593
# else:
568
- # # non-singleton *
594
+ # # non-singleton *
569
595
# if len(right) == 1:
570
596
# # non-singleton * singleton
571
597
# return [op(x, right.A) for x in left.A]
@@ -587,7 +613,7 @@ def binop(self, right, op, op2=None, list1=True):
587
613
def unop (self , op , matrix = False ):
588
614
"""
589
615
Perform unary operation
590
-
616
+
591
617
:param self: operand
592
618
:type self: BasePoseList subclass
593
619
:param op: unnary operation
@@ -598,7 +624,7 @@ def unop(self, op, matrix=False):
598
624
:rtype: list or NumPy array
599
625
600
626
The is a helper method for implementing unary operations where the
601
- operand has multiple value. This method computes the value of
627
+ operand has multiple value. This method computes the value of
602
628
the operation for all input values and returns the result as either
603
629
a list or as a matrix which vertically stacks the results.
604
630
@@ -613,7 +639,7 @@ def unop(self, op, matrix=False):
613
639
========= ==== ===================================
614
640
615
641
The result is:
616
-
642
+
617
643
- a list of values if ``matrix==False``, or
618
644
- a 2D NumPy stack of values if ``matrix==True``, it is assumed
619
645
that the value is a 1D array.
@@ -623,4 +649,3 @@ def unop(self, op, matrix=False):
623
649
return np .vstack ([op (x ) for x in self .data ])
624
650
else :
625
651
return [op (x ) for x in self .data ]
626
-
0 commit comments