@@ -441,6 +441,87 @@ def copy(self, prefix="", scale=None):
441441 return c
442442
443443
444+ class Capsule (URDFType ):
445+ """A capsule whose center is at the local origin.
446+
447+ Parameters
448+ ----------
449+ radius : float
450+ The radius of the capsule in meters.
451+ length : float
452+ The length of the capsule in meters.
453+ """
454+
455+ _ATTRIBS = {
456+ "radius" : (float , True ),
457+ "length" : (float , True ),
458+ }
459+ _TAG = "capsule"
460+
461+ def __init__ (self , radius , length ):
462+ self .radius = radius
463+ self .length = length
464+ self ._meshes = []
465+
466+ @property
467+ def radius (self ):
468+ """float : The radius of the capsule in meters."""
469+ return self ._radius
470+
471+ @radius .setter
472+ def radius (self , value ):
473+ self ._radius = float (value )
474+ self ._meshes = []
475+
476+ @property
477+ def length (self ):
478+ """float : The length of the capsule in meters."""
479+ return self ._length
480+
481+ @length .setter
482+ def length (self , value ):
483+ self ._length = float (value )
484+ self ._meshes = []
485+
486+ @property
487+ def meshes (self ):
488+ """list of :class:`~trimesh.base.Trimesh` : The triangular meshes
489+ that represent this object.
490+ """
491+ if len (self ._meshes ) == 0 :
492+ self ._meshes = [trimesh .creation .capsule (radius = self .radius , height = self .length )]
493+ return self ._meshes
494+
495+ def copy (self , prefix = "" , scale = None ):
496+ """Create a deep copy with the prefix applied to all names.
497+
498+ Parameters
499+ ----------
500+ prefix : str
501+ A prefix to apply to all names.
502+
503+ Returns
504+ -------
505+ :class:`.Capsule`
506+ A deep copy.
507+ """
508+ if scale is None :
509+ scale = 1.0
510+ if isinstance (scale , (list , np .ndarray )):
511+ if scale [0 ] != scale [1 ]:
512+ raise ValueError ("Cannot rescale capsule geometry with asymmetry in x/y" )
513+ c = Capsule (
514+ radius = self .radius * scale [0 ],
515+ length = self .length * scale [2 ],
516+ )
517+ else :
518+ c = Capsule (
519+ radius = self .radius * scale ,
520+ length = self .length * scale ,
521+ )
522+ return c
523+
524+
444525class Sphere (URDFType ):
445526 """A sphere whose center is at the local origin.
446527
@@ -657,6 +738,8 @@ class Geometry(URDFType):
657738 Box geometry.
658739 cylinder : :class:`.Cylinder`
659740 Cylindrical geometry.
741+ capsule : :class:`.Capsule`
742+ Capsule geometry.
660743 sphere : :class:`.Sphere`
661744 Spherical geometry.
662745 mesh : :class:`.Mesh`
@@ -666,16 +749,18 @@ class Geometry(URDFType):
666749 _ELEMENTS = {
667750 "box" : (Box , False , False ),
668751 "cylinder" : (Cylinder , False , False ),
752+ "capsule" : (Capsule , False , False ),
669753 "sphere" : (Sphere , False , False ),
670754 "mesh" : (Mesh , False , False ),
671755 }
672756 _TAG = "geometry"
673757
674- def __init__ (self , box = None , cylinder = None , sphere = None , mesh = None ):
675- if box is None and cylinder is None and sphere is None and mesh is None :
758+ def __init__ (self , box = None , cylinder = None , capsule = None , sphere = None , mesh = None ):
759+ if box is None and cylinder is None and capsule is None and sphere is None and mesh is None :
676760 raise ValueError ("At least one geometry element must be set" )
677761 self .box = box
678762 self .cylinder = cylinder
763+ self .capsule = capsule
679764 self .sphere = sphere
680765 self .mesh = mesh
681766
@@ -701,6 +786,17 @@ def cylinder(self, value):
701786 raise TypeError ("Expected Cylinder type" )
702787 self ._cylinder = value
703788
789+ @property
790+ def capsule (self ):
791+ """:class:`.Capsule` : Capsule geometry."""
792+ return self ._capsule
793+
794+ @capsule .setter
795+ def capsule (self , value ):
796+ if value is not None and not isinstance (value , Capsule ):
797+ raise TypeError ("Expected Capsule type" )
798+ self ._capsule = value
799+
704800 @property
705801 def sphere (self ):
706802 """:class:`.Sphere` : Spherical geometry."""
@@ -732,6 +828,8 @@ def geometry(self):
732828 return self .box
733829 if self .cylinder is not None :
734830 return self .cylinder
831+ if self .capsule is not None :
832+ return self .capsule
735833 if self .sphere is not None :
736834 return self .sphere
737835 if self .mesh is not None :
@@ -761,6 +859,7 @@ def copy(self, prefix="", scale=None):
761859 v = Geometry (
762860 box = (self .box .copy (prefix = prefix , scale = scale ) if self .box else None ),
763861 cylinder = (self .cylinder .copy (prefix = prefix , scale = scale ) if self .cylinder else None ),
862+ capsule = (self .capsule .copy (prefix = prefix , scale = scale ) if self .capsule else None ),
764863 sphere = (self .sphere .copy (prefix = prefix , scale = scale ) if self .sphere else None ),
765864 mesh = (self .mesh .copy (prefix = prefix , scale = scale ) if self .mesh else None ),
766865 )
0 commit comments