1
- #!/usr/bin/env python3
1
+ # Part of Spatial Math Toolbox for Python
2
+ # Copyright (c) 2000 Peter Corke
3
+ # MIT Licence, see details in top-level file: LICENCE
2
4
3
5
import numpy as np
4
6
import math
5
7
from collections import namedtuple
6
- from collections import UserList
7
-
8
- from spatialmath .base import argcheck as arg
9
- import spatialmath .base as base
10
8
import matplotlib .pyplot as plt
11
- from mpl_toolkits . mplot3d import Axes3D
9
+ import spatialmath . base as base
12
10
from spatialmath import SE3
13
11
from spatialmath .smuserlist import SMUserList
14
12
@@ -30,7 +28,7 @@ class Plane:
30
28
"""
31
29
def __init__ (self , c ):
32
30
33
- self .plane = arg .getvector (c , 4 )
31
+ self .plane = base .getvector (c , 4 )
34
32
35
33
# point and normal
36
34
@classmethod
@@ -46,8 +44,8 @@ def PN(cls, p, n):
46
44
:rtype: Plane
47
45
48
46
"""
49
- n = arg .getvector (n , 3 ) # normal to the plane
50
- p = arg .getvector (p , 3 ) # point on the plane
47
+ n = base .getvector (n , 3 ) # normal to the plane
48
+ p = base .getvector (p , 3 ) # point on the plane
51
49
return cls (np .r_ [n , - np .dot (n , p )])
52
50
53
51
# point and normal
@@ -62,7 +60,7 @@ def P3(cls, p):
62
60
:rtype: Plane
63
61
"""
64
62
65
- p = arg .ismatrix ((3 ,3 ))
63
+ p = base .ismatrix (p , (3 ,3 ))
66
64
v1 = p [:,0 ]
67
65
v2 = p [:,1 ]
68
66
v3 = p [:,2 ]
@@ -235,7 +233,7 @@ def __init__(self, v=None, w=None):
235
233
236
234
else :
237
235
# additional arguments
238
- assert arg .isvector (v , 3 ) and arg .isvector (w , 3 ), 'expecting two 3-vectors'
236
+ assert base .isvector (v , 3 ) and base .isvector (w , 3 ), 'expecting two 3-vectors'
239
237
self .data = [np .r_ [v , w ]]
240
238
241
239
# needed to allow __rmul__ to work if left multiplied by ndarray
@@ -271,8 +269,8 @@ def PQ(P=None, Q=None):
271
269
272
270
:seealso: Plucker, Plucker.Planes, Plucker.PointDir
273
271
"""
274
- P = arg .getvector (P , 3 )
275
- Q = arg .getvector (Q , 3 )
272
+ P = base .getvector (P , 3 )
273
+ Q = base .getvector (Q , 3 )
276
274
# compute direction and moment
277
275
w = P - Q
278
276
v = np .cross (P - Q , P )
@@ -300,9 +298,9 @@ def Planes(pi1, pi2):
300
298
"""
301
299
302
300
if not isinstance (pi1 , Plane ):
303
- pi1 = Plane (arg .getvector (pi1 , 4 ))
301
+ pi1 = Plane (base .getvector (pi1 , 4 ))
304
302
if not isinstance (pi2 , Plane ):
305
- pi2 = Plane (arg .getvector (pi2 , 4 ))
303
+ pi2 = Plane (base .getvector (pi2 , 4 ))
306
304
307
305
w = np .cross (pi1 .n , pi2 .n )
308
306
v = pi2 .d * pi1 .n - pi1 .d * pi2 .n
@@ -326,8 +324,8 @@ def PointDir(point, dir):
326
324
:seealso: Plucker, Plucker.Planes, Plucker.PQ
327
325
"""
328
326
329
- point = arg .getvector (point , 3 )
330
- dir = arg .getvector (dir , 3 )
327
+ point = base .getvector (point , 3 )
328
+ dir = base .getvector (dir , 3 )
331
329
332
330
return Plucker (np .r_ [np .cross (dir , point ), dir ])
333
331
@@ -486,7 +484,7 @@ def point(self, lam):
486
484
487
485
:seealso: Plucker.pp, Plucker.closest, Plucker.uw
488
486
"""
489
- lam = arg .getvector (lam , out = 'row' )
487
+ lam = base .getvector (lam , out = 'row' )
490
488
return self .pp .reshape ((3 ,1 )) + self .uw .reshape ((3 ,1 )) * lam
491
489
492
490
# ------------------------------------------------------------------------- #
@@ -511,15 +509,15 @@ def contains(self, x, tol=50*_eps):
511
509
If ``X`` is an array with 3 rows, the test is performed on every column and
512
510
an array of booleans is returned.
513
511
"""
514
- if arg .isvector (x , 3 ):
515
- x = arg .getvector (x )
512
+ if base .isvector (x , 3 ):
513
+ x = base .getvector (x )
516
514
return np .linalg .norm ( np .cross (x - self .pp , self .w ) ) < tol
517
- elif arg .ismatrix (x , (3 ,None )):
515
+ elif base .ismatrix (x , (3 ,None )):
518
516
return [np .linalg .norm (np .cross (_ - self .pp , self .w )) < tol for _ in x .T ]
519
517
else :
520
518
raise ValueError ('bad argument' )
521
519
522
- def __eq__ (l1 , l2 ): # pylint: disable=no-self-argument
520
+ def __eq__ (self , l2 ): # pylint: disable=no-self-argument
523
521
"""
524
522
Test if two lines are equivalent
525
523
@@ -535,9 +533,10 @@ def __eq__(l1, l2): # pylint: disable=no-self-argument
535
533
space. Note that because of the over parameterization, lines can be
536
534
equivalent even if their coordinate vectors are different.
537
535
"""
536
+ l1 = self
538
537
return abs ( 1 - np .dot (base .unitvec (l1 .vec ), base .unitvec (l2 .vec ))) < 10 * _eps
539
538
540
- def __ne__ (l1 , l2 ): # pylint: disable=no-self-argument
539
+ def __ne__ (self , l2 ): # pylint: disable=no-self-argument
541
540
"""
542
541
Test if two lines are not equivalent
543
542
@@ -552,10 +551,10 @@ def __ne__(l1, l2): # pylint: disable=no-self-argument
552
551
space. Note that because of the over parameterization, lines can be
553
552
equivalent even if their coordinate vectors are different.
554
553
"""
555
-
554
+ l1 = self
556
555
return not l1 .__eq__ (l2 )
557
556
558
- def isparallel (l1 , l2 , tol = 10 * _eps ): # pylint: disable=no-self-argument
557
+ def isparallel (self , l2 , tol = 10 * _eps ): # pylint: disable=no-self-argument
559
558
"""
560
559
Test if lines are parallel
561
560
@@ -572,11 +571,11 @@ def isparallel(l1, l2, tol=10*_eps): # pylint: disable=no-self-argument
572
571
573
572
:seealso: Plucker.or, Plucker.intersects
574
573
"""
575
-
574
+ l1 = self
576
575
return np .linalg .norm (np .cross (l1 .w , l2 .w ) ) < tol
577
576
578
577
579
- def __or__ (l1 , l2 ): # pylint: disable=no-self-argument
578
+ def __or__ (self , l2 ): # pylint: disable=no-self-argument
580
579
"""
581
580
Test if lines are parallel as a binary operator
582
581
@@ -591,10 +590,11 @@ def __or__(l1, l2): # pylint: disable=no-self-argument
591
590
592
591
:seealso: Plucker.isparallel, Plucker.__xor__
593
592
"""
593
+ l1 = self
594
594
return l1 .isparallel (l2 )
595
595
596
596
597
- def __xor__ (l1 , l2 ): # pylint: disable=no-self-argument
597
+ def __xor__ (self , l2 ): # pylint: disable=no-self-argument
598
598
599
599
"""
600
600
Test if lines intersect as a binary operator
@@ -615,14 +615,15 @@ def __xor__(l1, l2): # pylint: disable=no-self-argument
615
615
616
616
:seealso: Plucker.intersects, Plucker.parallel
617
617
"""
618
+ l1 = self
618
619
return not l1 .isparallel (l2 ) and (abs (l1 * l2 ) < 10 * _eps )
619
620
620
621
# ------------------------------------------------------------------------- #
621
622
# PLUCKER LINE DISTANCE AND INTERSECTION
622
623
# ------------------------------------------------------------------------- #
623
624
624
625
625
- def intersects (l1 , l2 ): # pylint: disable=no-self-argument
626
+ def intersects (self , l2 ): # pylint: disable=no-self-argument
626
627
"""
627
628
Intersection point of two lines
628
629
@@ -639,6 +640,7 @@ def intersects(l1, l2): # pylint: disable=no-self-argument
639
640
640
641
:seealso: Plucker.commonperp, Plucker.eq, Plucker.__xor__
641
642
"""
643
+ l1 = self
642
644
if l1 ^ l2 :
643
645
# lines do intersect
644
646
return - (np .dot (l1 .v , l2 .w ) * np .eye (3 , 3 ) + \
@@ -648,7 +650,7 @@ def intersects(l1, l2): # pylint: disable=no-self-argument
648
650
# lines don't intersect
649
651
return None
650
652
651
- def distance (l1 , l2 ): # pylint: disable=no-self-argument
653
+ def distance (self , l2 ): # pylint: disable=no-self-argument
652
654
"""
653
655
Minimum distance between lines
654
656
@@ -664,7 +666,8 @@ def distance(l1, l2): # pylint: disable=no-self-argument
664
666
Notes:
665
667
666
668
- Works for parallel, skew and intersecting lines.
667
- """
669
+ """
670
+ l1 = self
668
671
if l1 | l2 :
669
672
# lines are parallel
670
673
l = np .cross (l1 .w , l1 .v - l2 .v * np .dot (l1 .w , l2 .w ) / dot (l2 .w , l2 .w )) / np .linalg .norm (l1 .w )
@@ -706,7 +709,7 @@ def closest(self, x):
706
709
# http://www.ahinson.com/algorithms_general/Sections/Geometry/PluckerLine.pdf
707
710
# has different equation for moment, the negative
708
711
709
- x = arg .getvector (x , 3 )
712
+ x = base .getvector (x , 3 )
710
713
711
714
lam = np .dot (x - self .pp , self .uw )
712
715
p = self .point (lam ).flatten () # is the closest point on the line
@@ -715,7 +718,7 @@ def closest(self, x):
715
718
return namedtuple ('closest' , 'p d lam' )(p , d , lam )
716
719
717
720
718
- def commonperp (l1 , l2 ): # pylint: disable=no-self-argument
721
+ def commonperp (self , l2 ): # pylint: disable=no-self-argument
719
722
"""
720
723
Common perpendicular to two lines
721
724
@@ -731,7 +734,7 @@ def commonperp(l1, l2): # pylint: disable=no-self-argument
731
734
732
735
:seealso: Plucker.intersect
733
736
"""
734
-
737
+ l1 = self
735
738
if l1 | l2 :
736
739
# no common perpendicular if lines are parallel
737
740
return None
@@ -744,7 +747,7 @@ def commonperp(l1, l2): # pylint: disable=no-self-argument
744
747
return Plucker (v , w )
745
748
746
749
747
- def __mul__ (left , right ): # pylint: disable=no-self-argument
750
+ def __mul__ (self , right ): # pylint: disable=no-self-argument
748
751
r"""
749
752
Reciprocal product
750
753
@@ -764,13 +767,14 @@ def __mul__(left, right): # pylint: disable=no-self-argument
764
767
765
768
:seealso: Plucker.__rmul__
766
769
"""
770
+ left = self
767
771
if isinstance (right , Plucker ):
768
772
# reciprocal product
769
773
return np .dot (left .uw , right .v ) + np .dot (right .uw , left .v )
770
774
else :
771
775
raise ValueError ('bad arguments' )
772
776
773
- def __rmul__ (right , left ): # pylint: disable=no-self-argument
777
+ def __rmul__ (self , left ): # pylint: disable=no-self-argument
774
778
"""
775
779
Line transformation
776
780
@@ -786,6 +790,7 @@ def __rmul__(right, left): # pylint: disable=no-self-argument
786
790
787
791
:seealso: Plucker.__mul__
788
792
"""
793
+ right = self
789
794
if isinstance (left , SE3 ):
790
795
A = np .r_ [ np .c_ [left .R , base .skew (- left .t ) @ left .R ],
791
796
np .c_ [np .zeros ((3 ,3 )), left .R ]
@@ -799,7 +804,7 @@ def __rmul__(right, left): # pylint: disable=no-self-argument
799
804
# ------------------------------------------------------------------------- #
800
805
801
806
802
- def intersect_plane (line , plane ): # pylint: disable=no-self-argument
807
+ def intersect_plane (self , plane ): # pylint: disable=no-self-argument
803
808
r"""
804
809
Line intersection with a plane
805
810
@@ -835,17 +840,16 @@ def intersect_plane(line, plane): # pylint: disable=no-self-argument
835
840
# Note that this is in homogeneous coordinates.
<
AC3F
/tr>836
841
# intersection of plane (n,p) with the line (v,p)
837
842
# returns point and line parameter
838
-
839
843
if not isinstance (plane , Plane ):
840
- plane = Plane (arg .getvector (plane , 4 ))
844
+ plane = Plane (base .getvector (plane , 4 ))
841
845
842
- den = np .dot (line .w , plane .n )
846
+ den = np .dot (self .w , plane .n )
843
847
844
848
if abs (den ) > (100 * _eps ):
845
849
# P = -(np.cross(line.v, plane.n) + plane.d * line.w) / den
846
- p = (np .cross (line .v , plane .n ) - plane .d * line .w ) / den
850
+ p = (np .cross (self .v , plane .n ) - plane .d * self .w ) / den
847
851
848
- t = np .dot ( line .pp - p , plane .n )
852
+ t = np .dot ( self .pp - p , plane .n )
849
853
return namedtuple ('intersect_plane' , 'p lam' )(p , t )
850
854
else :
851
855
return None
0 commit comments