r"""
Actuators are the interface by which an Agent moves in the World. The following description is summarized from 
`Mujoco <https://mujoco.readthedocs.io/en/stable/computation.html#actuation-model>`__. The nomenclature is 
summarized in the following table.

=========================================== ===============================================
Keyword                                     Nomenclature                                    
=========================================== ===============================================
Actuator index                              :math:`{}_i`                           
Actuator control input                      :math:`u_i`                           
Actuator force output                       :math:`p_i`                           
Actuator Length                             :math:`l_i(q)`                           
Joint position                              :math:`q`                             
Joint momentum arms for each Actuator       :math:`\nabla l_i(q)`                           
Gain term                                   :math:`\bf a`                           
Bias term                                   :math:`\bf b`                           
Gain parameters                             :math:`\vec a`                           
Bias parameters                             :math:`\vec b`                           
Dynamics parameters                         :math:`\vec d`                           
=========================================== ===============================================


Force Transmission
==================

:class:`Actuators <BaseActuator>` enables :class:`Agents <blueprints.agent.Agent>` to move in the 
:class:`World <blueprints.world.World>`. Each Actuator has a single control input :math:`u_i` and a single force 
output :math:`p_i`. Additionally each Actuator has a scalar value called its length :math:`l_i(q)` which is a 
function of the Joints position :math:`q` and is defined by the Actuators properties. The force of an 
:class:`Actuator <BaseActuator>` acting on a :class:`Joint <blueprints.joints.BaseJoint>` is the Actuator force :math:`p` 
distributed over the Joints degrees of freedoms by scaling them with the momentum arms :math:`\nabla l`. The 
net force acting on a Joint than is: 

:math:`\vec F = \sum_i \nabla l_i(q) \cdot p_i`

Force Generation
================

The force generated by an Actuator affacting a Joint is dependent on three variables, 'Actuator control input 
:math:`u_i`, a dynamic activation state :math:`w_i` (for some Actuators) and the Actuator length for the Joint 
:math:`l_i(q)` as well as its velocity :math:`\dot{l}_i(q)`. The force is generated from an affine 
mapping with a linear gain term :math:`\bf a` and a bias term :math:`\bf b`.

:math:`p_i(u_i, w_i, l_i, \dot l_i) = \textbf {a} (l_i, \dot l_i) \cdot (u_i \text{ or } w_i) + \textbf {b} (l_i, \dot l_i)`

Gain
----

The gain term :math:`\bf a` is linear w.r.t. either the control output :math:`u_i` or the activation variable 
:math:`w_i` which dynamically depends on :math:`u_i` and will be explained in a later section. The activation 
dynamics are defined in :attr:`dyntype <BaseActuator.dyntype>`. The gain term is computed by one of the 
following options, which can be specified in :attr:`gaintype <BaseActuator.gaintype>` and the respective gain 
parameters :math:`\vec a` are defined in :attr:`gainprm <BaseActuator.gainprm>`.

========================================  ===============================================
:attr:`gaintype <BaseActuator.gaintype>`  :math:`\textbf{a}(l_i, \dot l_i)`                                    
========================================  ===============================================
``'fixed'``                               :math:`\textbf{a} = a_0`
``'affine'``                              :math:`\textbf{a} = a_0 + a_1 \cdot  l_i +  a_2 \cdot  \dot l_i`    
``'muscle'``                              :math:`\textbf{a} = \text{muscle_gain}(…)`         
``'user'``                                :math:`\textbf{a} = \text{user_gain}(…)`        
========================================  ===============================================

A description for muscle dynamics can be found `here <https://mujoco.readthedocs.io/en/latest/modeling.html#cmuscle>`__.

Bias
----

The bias term is a constant added to the Actuator force and does not depend on the Actuator outputs. The bias 
term is computed by one of the following options, which can be specified in :attr:`biastype <BaseActuator.biastype>` 
and the respective bias parameters :math:`\vec b` are defined in :attr:`biasprm <BaseActuator.biasprm>`.

========================================  ===============================================
:attr:`biastype <BaseActuator.biastype>`  :math:`\textbf{b}(l_i, \dot l_i)`                                    
========================================  ===============================================
``'none'``                                :math:`\textbf{b} = 0`                  
``'affine'``                              :math:`\textbf{b} = b_0 + b_1 \cdot  l_i +  b_2 \cdot  \dot l_i`    
``'muscle'``                              :math:`\textbf{b} = \text{muscle_bias}(…)`         
``'user'``                                :math:`\textbf{b} = \text{user_bias}(…)`        
========================================  ===============================================

A description for muscle dynamics can be found `here <https://mujoco.readthedocs.io/en/latest/modeling.html#cmuscle>`__.

Activation
----------

Some Actuators use an activation variable :math:`\textbf{w}` instead of the control variable :math:`\textbf{u}` 
to compute :math:`\textbf{a}`. The activation dynamics are specified in :attr:`dyntype <BaseActuator.dyntype>` 
and the respective dynamics parameters :math:`\vec d` are defined in :attr:`dynprm <BaseActuator.dynprm>`.

======================================  =====================================================  ==========================================================
:attr:`dyntype <BaseActuator.dyntype>`  Force Generation :math:`p_i(u_i, w_i, l_i, \dot l_i)`  Activation dynamics :math:`w_i`                           
======================================  =====================================================  ==========================================================
``'none'``                              :math:`p_i = \textbf{a} \cdot u_i + \textbf{b}`        No internal state                                         
``'integrator'``                        :math:`p_i = \textbf{a} \cdot w_i + \textbf{b}`        :math:`\dot w = u`                                       
``'filter'``                            :math:`p_i = \textbf{a} \cdot w_i + \textbf{b}`        :math:`\dot w = (u - w)/d_0`               
``'filterexact'``                       :math:`p_i = \textbf{a} \cdot w_i + \textbf{b}`        like filter but with exact integration                    
``'muscle'``                            :math:`p_i = \textbf{a} \cdot w_i + \textbf{b}`        :math:`\dot w = \text{muscle_dyn}(…)`           
``'user'``                              :math:`p_i = \textbf{a} \cdot w_i + \textbf{b}`        :math:`\dot w = \text{user_dyn}(…)`                 
======================================  =====================================================  ==========================================================

A description for muscle dynamics can be found `here <https://mujoco.readthedocs.io/en/latest/modeling.html#cmuscle>`__.


Usage
=====

Actuators can either be defined using the :class:`General` Actuator, or by using one of the shortcut Actuators — 
:class:`Motor`, :class:`Position`, :class:`Velocity`, :class:`IntVelocity`, :class:`Damper`, :class:`Cylinder`, 
:class:`Muscle` and :class:`Adhesion`.

Attachment
----------

Actuators will apply a force to their parent. They can be attached to :class:`Bodies <blueprints.body.Body>`, 
:class:`Joints <blueprints.joints.BaseJoint>` and :class:`Sites <blueprints.sites.BaseSite>`.

.. code-block:: python
	:caption: Attachment to Parents

	>>> actuator = blue.actuators.General()
	>>> body  = blue.Body(actuators=actuator)
	>>> joint = blue.joints.Joint(actuators=actuator)
	>>> site  = blue.sites.Site(actuators=actuator)

For the force computation an Actuator that is attached to a :class:`Joint <blueprints.joints.BaseJoint>` can 
also use the joints parent frame of reference by setting the :attr:`inparent <BaseActuator.inparent>` argument. 

.. code-block:: python
	:caption: Joint in parent
	
	>>> actuator = blue.actuators.General(inparent=True)
	>>> joint = blue.joints.Joint(actuators=actuator)

:class:`Sites <blueprints.sites.BaseSite>` can also use a different frame of reference using either the 
:attr:`refsite <BaseActuator.refsite>` or the :attr:`ref_actuators <blueprints.sites.BaseSite.ref_actuators>` 
attribute. This can only be done for Sites that are part of the same kinematic tree.

Reference Sites
---------------

.. code-block:: python
	:caption: Reference Sites Error
	:emphasize-lines: 5

	>>> source = blue.sites.Site(name='source')
	>>> target = blue.sites.Site(name='target')
	>>> source.attach(actuator)
	>>> target.ref_actuators = source.actuators
	ValueError: Setting refsite is only allowed for references that share the same root.

Setting reference :class:`Sites <blueprints.sites.BaseSite>` can either be done, by setting the 
:attr:`ref_actuators <blueprints.sites.BaseSite.ref_actuators>` or the :attr:`refsite <BaseActuator.refsite>` 
attribute.

.. code-block:: python
	:caption: Reference Sites Variations

	>>> root = blue.Body(sites=[source, target])
	
	VARIATION 1
	
	>>> root.sites['target'].ref_actuators = root.sites['source'].actuators

	VARIATION 2

	>>> root.sites['source'].actuators.refsite = root.sites['target']

The resulting XML looks like this:

.. code-block:: mxml
	:caption: XML Structure Attachment

	<mujoco>
	    <actuator>
	        <general name="unnamed_general" refsite="target" site="source" />
	    </actuator>
	    <worldbody>
	        <body name="root">
	            <site type="site" name="source" />
	            <site type="site" name="target" />
	        </body>
	    </worldbody>
	</mujoco>

Detachment
----------

When the parent Site or the reference Site are no longer part of the same kinematic tree, the reference Site 
gets detached. This is necessary to avoid redundant copies of Actuators of orphaned parents and references.

.. code-block:: python
	:caption: Reference Sites Detachment

	>>> root.sites['source'].actuators.refsite
	View[1|root.sites.actuators.refsite]
	>>> root.detach(root.sites['target'])
	>>> root.sites['source'].actuators.refsite
	[]

The resulting XML looks like this:

.. code-block:: mxml
	:caption: XML Structure Detachment

	<mujoco>
	    <actuator>
	        <general name="unnamed_general" site="source" />
	    </actuator>
	    <worldbody>
	        <body name="root">
	            <site type="site" name="source" />
	        </body>
	    </worldbody>
	</mujoco>

.. code-block:: python
	:caption: Parent Sites Detachment

	>>> root.sites['source'].actuators.refsite
	View[1|root.sites.actuators.refsite]
	>>> root.detach(root.sites['source'])
	>>> root.sites['target'].ref_actuators
	View[0|root.sites.ref_actuators]

The resulting XML looks like this:

.. code-block:: mxml
	:caption: XML Structure Detachment

	<mujoco>
	    <worldbody>
	        <body name="root">
	            <site type="site" name="target" />
	        </body>
	    </worldbody>
	</mujoco>

.. note::
	Actuators will not appear as children of their parents in xml, but instead in a separate ``<actuators>`` element.


"""

import xml.etree.ElementTree as xml
import numpy as np
import blueprints as blue



class BaseActuator(blue.ActuatorType, blue.CyclicalThing, blue.NodeThing):

	"""
	Most attribute descriptions are partially taken from `Mujoco <https://mujoco.readthedocs.io/en/latest/XMLreference.html#actuator>`__.
	"""
	
	@blue.restrict
	def __init__(self, 
		     name:          str|None                             =  None, 
		     inparent:      bool                                 =  False, 
		     ctrllimited:   bool|None                            =  None, 
		     forcelimited:  bool|None                            =  None, 
		     actlimited:    bool|None                            =  None, 
		     ctrlrange:     list[int|float]|np.ndarray           = [0., 0.], 
		     forcerange:    list[int|float]|np.ndarray           = [0., 0.], 
		     actrange:      list[int|float]|np.ndarray           = [0., 0.], 
		     lengthrange:   list[int|float]|np.ndarray           = [0., 0.], 
		     gear:          int|float|list[int|float]|np.ndarray = [1., 0., 0., 0., 0., 0.], 
		     cranklength:   int|float|list[int|float]|np.ndarray =  0., 
		     dyntype:       str                                  = 'none', 
		     gaintype:      str                                  = 'fixed', 
		     biastype:      str                                  = 'none', 
		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
		     sensors:       list|blue.SensorType|None            =  None, 
		     copy:          bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will 
			be altered by an enumeration scheme.
		inparent : bool, optional
			If`:attr:`inparent` is True and the parent of the Actuator is :class:`blueprints.joints.BaseJoint` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. It is different from the gain in the force 
			generation mechanism, because the gain only scales the force output and does not affect 
			the length, moment arms and velocity. For actuators with scalar transmission, only the 
			first element of this vector is used. The remaining elements are needed for joint (with 
			or without inparent) and site transmissions where this attribute is used to specify 3D 
			force and torque axes.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			Activation dynamics type for the actuator.
		
			=================  ===============================================
			Keyword            Description                                    
			=================  ===============================================
			``'none'``         No internal state                              
			``'integrator'``   :math:`\\dot w = u`                            
			``'filter'``       :math:`\\dot w = (u - w)/\\text{dynprm}[0]`    
			``'filterexact'``  Like filter but with exact integration         
			``'muscle'``       :math:`\\dot w = \\text{mju_muscleDynamics}(…)`
			``'user'``         :math:`\\dot w = \\text{mjcb_act_dyn}(…)`      
			=================  ===============================================
		
		gaintype : str, optional
			The gain and bias together determine the output of the force generation mechanism, which is 
			currently assumed to be affine. As already explained :attr:`above <blueprints.actuators.__doc__>`, the general formula 
			is: :math:`p = \\textbf{a} \\cdot (u \\text{ or } w) + \\textbf{b}` 
			The formula uses the activation state when present, and the control otherwise. The keywords 
			have the following meaning:
		
			=================  ===============================================
			Keyword            Description                                    
			=================  ===============================================
			``'fixed'``        :math:`\\textbf{a} = a_0`
			``'affine'``       :math:`\\textbf{a} = a_0 + a_1 \\cdot  length +  a_2 \\cdot  velocity`    
			``'muscle'``       :math:`\\textbf{a} = \\text{mju_muscleGain}(…)`         
			``'user'``         :math:`\\textbf{a} = \\text{mjcb_act_gain}(…)`        
			=================  ===============================================
		
		biastype : str, optional
			The keywords have the following meaning:
		
			=================  ===============================================
			Keyword            Description                                    
			=================  ===============================================
			``'none'``         :math:`\\textbf{b} = 0`                  
			``'affine'``       :math:`\\textbf{b} = b_0 + b_1 \\cdot  length +  b_2 \\cdot  velocity`    
			``'muscle'``       :math:`\\textbf{b} = \\text{mju_muscleBias}(…)`         
			``'user'``         :math:`\\textbf{b} = \\text{mjcb_act_bias}(…)`        
			=================  ===============================================
		
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. The built-in activation types (except for muscle) use only the 
			first parameter, but we provide additional parameters in case user callbacks implement a more 
			elaborate model. The length of this array is not enforced by the parser, so the user can enter 
			as many parameters as needed. These defaults are not compatible with muscle actuators.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			Gain parameters. The built-in gain types (except for muscle) use only the first parameter, but 
			we provide additional parameters in case user callbacks implement a more elaborate model. The 
			length of this array is not enforced by the parser, so the user can enter as many parameters as 
			needed. These defaults are not compatible with muscle actuators.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. The affine bias type uses three parameters. The length of this array is not 
			enforced by the parser, so the user can enter as many parameters as needed. These defaults are 
			not compatible with muscle actuators.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		if isinstance(gear, (int, float)):
			gear = [float(gear), 0., 0., 0., 0., 0.]
		# MUJOCO PROPERTIES
		self.ctrllimited  = ctrllimited
		self.forcelimited = forcelimited
		self.actlimited   = actlimited
		self.ctrlrange    = ctrlrange
		self.forcerange   = forcerange
		self.actrange     = actrange
		self.lengthrange  = lengthrange
		self.gear         = gear
		self.cranklength  = cranklength
		self.dyntype      = dyntype
		self.gaintype     = gaintype
		self.biastype     = biastype
		self.dynprm       = dynprm
		self.gainprm      = gainprm
		self.biasprm      = biasprm
		self.inparent     = inparent
		# PARENT PROPERTIES
		pack    = lambda x: x if isinstance(x, list) else [x]
		sensors = pack(sensors) if sensors else []
		self._sensors       = []
		self._CHILDREN      = {'sensors': {'type':     blue.SensorType, 
						   'children': self._sensors}}
		self._tag           = None
		super().__init__(name=name, 
				 **kwargs)
		self.attach(*sensors, 
			    copy=copy)
		self._check_children_types()


	@blue.restrict
	def _build(self, parent, world, indicies, **kwargs):
		"""
		This method is called by the Actuators parent to construct the xml representations of 
		the kinematic tree. It recursively calls the :meth:`blueprints.sensors.BaseSensor._build` 
		methods if its sensor children.
		
		Parameters
		----------
		parent : xml.etree.ElementTree.Element
			The xml element of its parent
		world : WorldType
			The World from which the build method was called initially
		**kwargs
			Keyword arguments are overwrite the mujoco specs for building.
		
		Returns
		-------
		xml.etree.ElementTree.Element
			The built xml element of the Actuator.
		"""
		self._force_index  = indicies['force']
		indicies['force'] += 1
		if self.dyntype != 'none':
			self._activation_index  = indicies['activation']
			indicies['activation'] += 1
		specs = self._mujoco_specs(kwargs)
		if self.parent_tag == 'joints' and self.inparent:
			specs['jointinparent'] = self.parent.name
		else:
			if self.refsite is not None:
				specs['refsite'] = self.refsite.name
			specs[self.parent_tag] = self.parent.name
		self._xml_root = xml.SubElement(world._xml_actuator, 
						self.tag, 
						**specs)
		self._build_children(parent=self._xml_root, 
				     world=world, 
				     indicies=indicies)
		return self._xml_root


	@property
	def force(self) -> float:
		"""
		The force applied to the Actuator
		
		Returns
		-------
		float
		"""
		if not hasattr(self, '_force_index'):
			raise Exception('Actuator must first be build by a World before forces are available.')
		else:
			return float(self.root._mj_data.ctrl[self._force_index])


	@force.setter
	@blue.restrict
	def force(self, force: int|float|np.float16|np.float32|np.float64) -> None:
		"""
		Parameters
		----------
		force : int | float | np.float
			The force which is to be applied to the Actuator
		"""
		if not hasattr(self, '_force_index'):
			raise Exception('Actuator must first be build by a World before forces are available.')
		else:
			force = float(force)
			if force < self.ctrlrange[0]:
				force = self.ctrlrange[0]
			elif force > self.ctrlrange[1]:
				force = self.ctrlrange[1]
			self.root._mj_data.ctrl[self._force_index] = force


	@property
	def activation(self) -> float|None:
		"""
		The activation state of the Actuator
		
		Returns
		-------
		float
		"""
		if not hasattr(self, '_force_index'):
			raise Exception('Actuator must first be build by a World before activations are available.')
		elif self.dyntype == 'none':
			return None
		else:
			return float(self.root._mj_data.act[self._activation_index])


	@activation.setter
	@blue.restrict
	def activation(self, activation: int|float|np.float16|np.float32|np.float64) -> None:
		"""
		Parameters
		----------
		activation : int | float | np.float
			The activation which is to be applied to the Actuator
		"""
		if not hasattr(self, '_force_index'):
			raise Exception('Actuator must first be build by a World before activations are available.')
		if self.dyntype == 'none':
			raise Exception('Actuators of dyntype "none" cannot be used for activations. See :class:`Actuators` for a detailed explanation.')
		else:
			self.root._mj_data.act[self._activation_index] = float(activation)


	def __str__(self) -> str:
		"""
		The xml representation of the Actuator
		
		Returns
		-------
		str
		"""
		specs = self._mujoco_specs()
		if self.parent_tag == 'joints' and self.inparent:
			specs['jointinparent'] = self.parent.name
		else:
			if self.refsite is not None:
				specs['refsite'] = self.refsite.name
			specs[self.parent_tag] = self.parent.name
		xml_element = xml.Element(self.tag, **specs)
		return xml.tostring(xml_element, encoding='unicode')

	# XML PROPERTIES

	@blue.restrict
	@classmethod
	def _from_xml_element(cls, 
			      xml_element: xml.Element, 
			      sensor:      blue.SensorType|None = None) -> blue.ThingType:
		"""
		This method reconstructs an Actuator from an xml element.
		
		Parameters
		----------
		xml_element : xml.Element
			The xml element from which a Mesh is reconstructed.
		sensor : blue.AssetType
			The Sensor which is attached to the Actuator.
		
		Returns
		-------
		blue.ThingType
			The reconstructed Actuator.
		"""
		init_args, post_args, rest_args = cls._xml_element_args(xml_element)
		init_args['sensors'] = sensor
		init_args['copy']    = False
		actuator_type = xml_element.tag
		actuator = object.__new__(blue.REGISTER.ACTUATOR_THINGS[actuator_type])
		actuator.__init__(**init_args)
		for key, val in post_args.items():
			setattr(actuator, key, val)
		return actuator

	# PARENT PROPERTIES

	@property
	def parent_tag(self):
		"""
		The name of the parent that is used for the reference attribute in the xml tag.
		
		Returns
		-------
		str
			A string describing the parent tag
		
		Raises
		------
		ValueError
			If the parent is not of a valid ThingType an error is raised.
		"""
		if self.parent is None:
			return None
		elif isinstance(self.parent, blue.JointType):
			return 'joint'
		elif isinstance(self.parent, blue.SiteType):
			return 'site'
		elif isinstance(self.parent, blue.BodyType):
			return 'body'
		else:
			raise ValueError(f'Parent attribute is not Joint, Site or Body but {type(self.parent)}.')


	@property
	def inparent(self) -> bool:
		"""
		If ``inparent`` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
		the actuator assumes the frame of reference from the parent.
		
		Returns
		-------
		bool
		"""
		return self._inparent


	@inparent.setter
	@blue.restrict
	def inparent(self, inparent: bool) -> None:
		"""
		If ``inparent`` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
		the actuator assumes the frame of reference from the parent.
		
		Parameters
		----------
		inparent : bool
			indicates whether the reference frame is taken from the parent Joint
		"""
		self._inparent = inparent


	@property
	def refsite(self) -> blue.SiteType|None:
		"""
		If refsite is set to a side, the Actuator will assume the frame of reference of the refsite 
		instead of its parent. This only possible if the parent is also a :class:`Site <blueprints.sites.BaseSite>`.
		
		Returns
		-------
		blue.SiteType | None
		"""
		if hasattr(self, '_refsite'):
			return self._refsite
		else:
			return None


	@refsite.setter
	@blue.restrict
	def refsite(self, refsite: blue.SiteType|None) -> None:
		"""
		If refsite is set to a side, the Actuator will assume the frame of reference of the refsite 
		instead of its parent. This only possible if the parent is also a :class:`Site <blueprints.sites.BaseSite>`.
		
		Parameters
		----------
		refsite : blue.SiteType | None
			blue.SiteType | None
		
		Raises
		------
		ValueError
			If the parent is not a :class:`Site <blueprints.sites.BaseSite>` an error is raised.
		"""
		if not isinstance(self.parent, blue.SiteType) and not self._IGNORE_CHECKS:
			raise ValueError(f'To set the refsite reference, site must be set first.')
		if self.refsite is not None:
			self.refsite.detach(self)
		if refsite is not None:
			refsite._ref_actuators.append(self)
		self._refsite = refsite

	# MUJOCO PROPERTIES

	@property
	def tag(self) -> str:
		"""
		The tag will later become the tag name of the xml element.
		
		Returns
		-------
		str
		"""
		if self._tag is None:
			return self.__class__.__name__.lower()
		else:
			return self._tag


	@tag.setter
	@blue.restrict
	def tag(self, tag: str|None) -> None:
		"""
		The tag will later become the tag name of the xml element.
		
		Parameters
		----------
		tag : str | None
		"""
		self._tag = tag


	@property
	def ctrllimited(self) -> bool|None:
		"""
		If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
		at runtime. If false, control input clamping is disabled.
		
		Returns
		-------
		bool | None
		"""
		return self._ctrllimited


	@ctrllimited.setter
	@blue.restrict
	def ctrllimited(self, ctrllimited: bool|None) -> None:
		"""
		If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
		at runtime. If false, control input clamping is disabled.
		
		Parameters
		----------
		ctrllimited : bool | None
		"""
		self._ctrllimited = ctrllimited


	@property
	def forcelimited(self) -> bool|None:
		"""
		If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
		at runtime. If false, force clamping is disabled.
		
		Returns
		-------
		bool | None
		"""
		return self._forcelimited


	@forcelimited.setter
	@blue.restrict
	def forcelimited(self, forcelimited: bool|None) -> None:
		"""
		If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
		at runtime. If false, force clamping is disabled.
		
		Parameters
		----------
		forcelimited : bool | None
		"""
		self._forcelimited = forcelimited


	@property
	def actlimited(self) -> bool|None:
		"""
		If true, the internal state (activation) associated with this actuator is automatically 
		clamped to :attr:`actrange` at runtime.
		
		Returns
		-------
		bool | None
		"""
		return self._actlimited


	@actlimited.setter
	@blue.restrict
	def actlimited(self, actlimited: bool|None) -> None:
		"""
		If true, the internal state (activation) associated with this actuator is automatically 
		clamped to :attr:`actrange` at runtime.
		
		Parameters
		----------
		actlimited : bool | None
		"""
		self._actlimited = actlimited


	@property
	def ctrlrange(self) -> np.ndarray:
		"""
		Range for clamping the control input. The compiler expects the first value to be smaller 
		than the second value.
		
		Returns
		-------
		np.ndarray
		"""
		return self._ctrlrange


	@ctrlrange.setter
	@blue.restrict
	def ctrlrange(self, ctrlrange: np.ndarray|list[int|float]) -> None:
		"""
		Range for clamping the control input. The compiler expects the first value to be smaller 
		than the second value.
		
		Parameters
		----------
		ctrlrange : np.ndarray | list[int | float]
		"""
		self._ctrlrange = np.array(ctrlrange, dtype=np.float32)


	@property
	def forcerange(self) -> np.ndarray:
		"""
		Range for clamping the force output. The compiler expects the first value to be no greater 
		than the second value.
		
		Returns
		-------
		np.ndarray
		"""
		return self._forcerange


	@forcerange.setter
	@blue.restrict
	def forcerange(self, forcerange: np.ndarray|list[int|float]) -> None:
		"""
		Range for clamping the force output. The compiler expects the first value to be no greater 
		than the second value.
		
		Parameters
		----------
		forcerange : np.ndarray | list[int | float]
		"""
		self._forcerange = np.array(forcerange, dtype=np.float32)


	@property
	def actrange(self) -> np.ndarray:
		"""
		Range for clamping the activation state. The compiler expects the first value to be no 
		greater than the second value.
		
		Returns
		-------
		np.ndarray
		"""
		return self._actrange


	@actrange.setter
	@blue.restrict
	def actrange(self, actrange: np.ndarray|list[int|float]) -> None:
		"""
		Range for clamping the activation state. The compiler expects the first value to be no 
		greater than the second value.
		
		Parameters
		----------
		actrange : np.ndarray | list[int | float]
		"""
		self._actrange = np.array(actrange, dtype=np.float32)


	@property
	def lengthrange(self) -> np.ndarray:
		"""
		Range of feasible lengths of the actuator’s transmission.
		
		Returns
		-------
		np.ndarray
		"""
		return self._lengthrange


	@lengthrange.setter
	@blue.restrict
	def lengthrange(self, lengthrange: np.ndarray|list[int|float]) -> None:
		"""
		Range of feasible lengths of the actuator’s transmission.
		
		Parameters
		----------
		lengthrange : np.ndarray | list[int | float]
		"""
		self._lengthrange = np.array(lengthrange, dtype=np.float32)


	@property
	def gear(self) -> np.ndarray:
		"""
		This attribute scales the length (and consequently moment arms, velocity and force) of the 
		Actuator, for all transmission types. It is different from the gain in the force generation 
		mechanism, because the gain only scales the force output and does not affect the length, 
		moment arms and velocity. For actuators with scalar transmission, only the first element of 
		this vector is used. The remaining elements are needed for joint (with or without inparent) 
		and site transmissions where this attribute is used to specify 3D force and torque axes.
		
		Returns
		-------
		np.ndarray
		"""
		return self._gear


	@gear.setter
	@blue.restrict
	def gear(self, gear: np.ndarray|list[int|float]) -> None:
		"""
		This attribute scales the length (and consequently moment arms, velocity and force) of the 
		Actuator, for all transmission types. It is different from the gain in the force generation 
		mechanism, because the gain only scales the force output and does not affect the length, 
		moment arms and velocity. For actuators with scalar transmission, only the first element of 
		this vector is used. The remaining elements are needed for joint (with or without inparent) 
		and site transmissions where this attribute is used to specify 3D force and torque axes.
		
		Parameters
		----------
		gear : np.ndarray | list[int | float]
		"""
		self._gear = np.array(gear, dtype=np.float32)


	@property
	def cranklength(self) -> float:
		"""
		Used only for the slider-crank transmission type. Specifies the length of the connecting rod. 
		The compiler expects this value to be positive when a slider-crank transmission is present.
		
		Returns
		-------
		float
		"""
		return self._cranklength


	@cranklength.setter
	@blue.restrict
	def cranklength(self, cranklength: int|float) -> None:
		"""
		Used only for the slider-crank transmission type. Specifies the length of the connecting rod. 
		The compiler expects this value to be positive when a slider-crank transmission is present.
		
		Parameters
		----------
		cranklength : int | float
		"""
		self._cranklength = float(cranklength)


	@property
	def dyntype(self) -> str:
		"""
		Activation dynamics type for the actuator. See :attr:`dyntype` for a detailed description.
		
		Returns
		-------
		str
		"""
		return self._dyntype


	@dyntype.setter
	@blue.restrict
	def dyntype(self, dyntype: str) -> None:
		"""
		Activation dynamics type for the actuator. See :attr:`dyntype` for a detailed description.
		
		Parameters
		----------
		dyntype : str
			Legal values are ``'none'``, ``'integrator'``, ``'filter'``, ``'filterexact'``, 
			``'muscle'`` and ``'user'``.
		"""
		self._dyntype = dyntype


	@property
	def gaintype(self) -> str:
		"""
		The gain and bias together determine the output of the force generation mechanism, which is 
		currently assumed to be affine. See :attr:`gaintype` for a detailed description.
		
		Returns
		-------
		str
		"""
		return self._gaintype


	@gaintype.setter
	@blue.restrict
	def gaintype(self, gaintype: str) -> None:
		"""
		The gain and bias together determine the output of the force generation mechanism, which is 
		currently assumed to be affine. See :attr:`gaintype` for a detailed description.
		
		Parameters
		----------
		gaintype : str
			Legal values are ``'fixed'``, ``'affine'``, ``'muscle'`` and ``'user'``.
		"""
		self._gaintype = gaintype


	@property
	def biastype(self) -> str:
		"""
		See :attr:`biastype` for a detailed description.
		
		Returns
		-------
		str
		"""
		return self._biastype


	@biastype.setter
	@blue.restrict
	def biastype(self, biastype: str) -> None:
		"""
		See :attr:`biastype` for a detailed description.
		
		Parameters
		----------
		biastype : str
			Legal values are ``'fixed'``, ``'affine'``, ``'muscle'`` and ``'user'``.
		"""
		self._biastype = biastype


	@property
	def dynprm(self) -> np.ndarray:
		"""
		Activation dynamics parameters. The built-in activation types (except for muscle) use only 
		the first parameter, but we provide additional parameters in case user callbacks implement 
		a more elaborate model. The length of this array is not enforced by the parser, so the user 
		can enter as many parameters as needed. These defaults are not compatible with muscle 
		actuators.
		
		Returns
		-------
		np.ndarray
		"""
		return self._dynprm


	@dynprm.setter
	@blue.restrict
	def dynprm(self, dynprm: int|float|list[int|float]|np.ndarray) -> None:
		"""
		Activation dynamics parameters. The built-in activation types (except for muscle) use only 
		the first parameter, but we provide additional parameters in case user callbacks implement 
		a more elaborate model. The length of this array is not enforced by the parser, so the user 
		can enter as many parameters as needed. These defaults are not compatible with muscle 
		actuators.
		
		Parameters
		----------
		dynprm : int | float | list[int | float] | np.ndarray
		"""
		if isinstance(dynprm, (int, float)):
			dynprm = [dynprm]
		self._dynprm = np.array(dynprm, dtype=np.float32)


	@property
	def gainprm(self) -> np.ndarray:
		"""
		Gain parameters. The built-in gain types (except for muscle) use only the first parameter, 
		but we provide additional parameters in case user callbacks implement a more elaborate model. 
		The length of this array is not enforced by the parser, so the user can enter as many 
		parameters as needed. These defaults are not compatible with muscle actuators.
		
		Returns
		-------
		np.ndarray
		"""
		return self._gainprm


	@gainprm.setter
	@blue.restrict
	def gainprm(self, gainprm: int|float|list[int|float]|np.ndarray) -> None:
		"""
		Gain parameters. The built-in gain types (except for muscle) use only the first parameter, 
		but we provide additional parameters in case user callbacks implement a more elaborate model. 
		The length of this array is not enforced by the parser, so the user can enter as many 
		parameters as needed. These defaults are not compatible with muscle actuators.
		
		Parameters
		----------
		gainprm : int | float | list[int | float] | np.ndarray
		"""
		if isinstance(gainprm, (int, float)):
			gainprm = [gainprm]
		self._gainprm = np.array(gainprm, dtype=np.float32)


	@property
	def biasprm(self) -> np.ndarray:
		"""
		Bias parameters. The affine bias type uses three parameters. The length of this array is not 
		enforced by the parser, so the user can enter as many parameters as needed. These defaults are 
		not compatible with muscle actuators.
		
		Returns
		-------
		np.ndarray
		"""
		return self._biasprm


	@biasprm.setter
	@blue.restrict
	def biasprm(self, biasprm: int|float|list[int|float]|np.ndarray) -> None:
		"""
		Bias parameters. The affine bias type uses three parameters. The length of this array is not 
		enforced by the parser, so the user can enter as many parameters as needed. These defaults are 
		not compatible with muscle actuators.
		
		Parameters
		----------
		biasprm : int | float | list[int | float] | np.ndarray
		"""
		if isinstance(biasprm, (int, float)):
			biasprm = [biasprm]
		self._biasprm = np.array(biasprm, dtype=np.float32)



class General(BaseActuator):

	"""
	This is the General form of an :class:`Actuator <BaseActuator>`. Useful shortcuts are defined in the 
	following classes.
	
	Parameters
	----------
	name : str | None, optional
		The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
		by an enumeration scheme.
	inparent : bool, optional
		If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
		the actuator assumes the frame of reference from the parent.
	ctrllimited : bool | None, optional
		If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
		at runtime. If false, control input clamping is disabled.
	forcelimited : bool | None, optional
		If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
		at runtime. If false, force clamping is disabled.
	actlimited : bool | None, optional
		If true, the internal state (activation) associated with this actuator is automatically 
		clamped to :attr:`actrange` at runtime.
	ctrlrange : np.ndarray | list[int | float], optional
		Range for clamping the control input. The compiler expects the first value to be smaller 
		than the second value. 
	forcerange : np.ndarray | list[int | float], optional
		Range for clamping the force output. The compiler expects the first value to be no greater 
		than the second value. 
	actrange : np.ndarray | list[int | float], optional
		Range for clamping the activation state. The compiler expects the first value to be no 
		greater than the second value.
	lengthrange : np.ndarray | list[int | float], optional
		Range of feasible lengths of the actuator’s transmission.
	gear : np.ndarray | list[int | float], optional
		This attribute scales the length (and consequently moment arms, velocity and force) of 
		the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
		detailed description.
	cranklength : int | float, optional
		Used only for the slider-crank transmission type. Specifies the length of the connecting 
		rod. The compiler expects this value to be positive when a slider-crank transmission is 
		present.
	dyntype : str, optional
		See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
	gaintype : str, optional
		See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
	biastype : str, optional
		See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
	dynprm : int | float | list[int | float] | np.ndarray, optional
		Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
	gainprm : int | float | list[int | float] | np.ndarray, optional
		The gain and bias together determine the output of the force generation mechanism. See 
		:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
	biasprm : int | float | list[int | float] | np.ndarray, optional
		Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
	sensors : list | blue.SensorType | None, optional
		The Sensors are attached to the actuator on initialization.
	copy : bool, optional
		If True, the Sensors passed during init will be copied before attachment, otherwise the original 
		Sensor is attached.
	**kwargs
		Keyword arguments are passed to ``super().__init__``.
	"""
	
	pass



#class Translation(BaseActuator):
#
#	@blue.restrict
#	def __init__(self, 
#		     name:          str|None                             =  None, 
#		     axis:          str|np.ndarray|list[int|float]|None  =  None, 
#		     ctrllimited:   bool|None                            =  None, 
#		     forcelimited:  bool|None                            =  None, 
#		     actlimited:    bool|None                            =  None, 
#		     ctrlrange:     np.ndarray|list[int|float]           = [0., 0.], 
#		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
#		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
#		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
#		     cranklength:   int|float                            =  0., 
#		     dyntype:       str                                  = 'none', 
#		     gaintype:      str                                  = 'fixed', 
#		     biastype:      str                                  = 'none', 
#		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
#		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
#		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
#		     sensors:       list|blue.SensorType|None            =  None, 
#		     copy:          bool                                 =  True, 
#		     **kwargs):
#		"""
#		Parameters
#		----------
#		name : str | None, optional
#			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
#			by an enumeration scheme.
#		inparent : bool, optional
#			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
#			the actuator assumes the frame of reference from the parent.
#		ctrllimited : bool | None, optional
#			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
#			at runtime. If false, control input clamping is disabled.
#		forcelimited : bool | None, optional
#			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
#			at runtime. If false, force clamping is disabled.
#		actlimited : bool | None, optional
#			If true, the internal state (activation) associated with this actuator is automatically 
#			clamped to :attr:`actrange` at runtime.
#		ctrlrange : np.ndarray | list[int | float], optional
#			Range for clamping the control input. The compiler expects the first value to be smaller 
#			than the second value. 
#		forcerange : np.ndarray | list[int | float], optional
#			Range for clamping the force output. The compiler expects the first value to be no greater 
#			than the second value. 
#		actrange : np.ndarray | list[int | float], optional
#			Range for clamping the activation state. The compiler expects the first value to be no 
#			greater than the second value.
#		lengthrange : np.ndarray | list[int | float], optional
#			Range of feasible lengths of the actuator’s transmission.
#		gear : np.ndarray | list[int | float], optional
#			This attribute scales the length (and consequently moment arms, velocity and force) of 
#			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
#			detailed description.
#		cranklength : int | float, optional
#			Used only for the slider-crank transmission type. Specifies the length of the connecting 
#			rod. The compiler expects this value to be positive when a slider-crank transmission is 
#			present.
#		dyntype : str, optional
#			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
#		gaintype : str, optional
#			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
#		biastype : str, optional
#			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
#		dynprm : int | float | list[int | float] | np.ndarray, optional
#			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
#		gainprm : int | float | list[int | float] | np.ndarray, optional
#			The gain and bias together determine the output of the force generation mechanism. See 
#			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
#		biasprm : int | float | list[int | float] | np.ndarray, optional
#			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
#		sensors : list | blue.SensorType | None, optional
#			The Sensors are attached to the actuator on initialization.
#		copy : bool, optional
#			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
#			Sensor is attached.
#		**kwargs
#			Keyword arguments are passed to ``super().__init__``.
#		"""
#		raise NotImplemented
#		if axis is not None:
#			if 'gear' in kwargs:
#				kwargs.pop('gear')
#			axis = blue.utils.geometry.Vector.get_axis(axis)
#			gear = np.concatenate([axis, np.zeros(3, dtype=np.float32)])
#		else:
#			if 'gear' in kwargs:
#				gear = kwargs.pop('gear')
#			else:
#				gear = self._DEFAULT_VALS['gear']
#		super().__init__(name=name, 
#				 ctrllimited=ctrllimited, 
#				 forcelimited=forcelimited, 
#				 actlimited=actlimited, 
#				 ctrlrange=ctrlrange, 
#				 forcerange=forcerange, 
#				 actrange=actrange, 
#				 lengthrange=lengthrange, 
#				 gear=gear, 
#				 cranklength=cranklength, 
#				 dyntype=dyntype, 
#				 gaintype=gaintype, 
#				 biastype=biastype, 
#				 dynprm=dynprm, 
#				 gainprm=gainprm, 
#				 biasprm=biasprm, 
#				 sensors=sensors, 
#				 copy=copy, 
#				 **kwargs)
#		self.tag = 'general'



#class Rotation(BaseActuator):
#
#	@blue.restrict
#	def __init__(self, 
#		     name:          str|None                             =  None, 
#		     axis:          str|np.ndarray|list[int|float]|None  =  None, 
#		     ctrllimited:   bool|None                            =  True, 
#		     forcelimited:  bool|None                            =  None, 
#		     actlimited:    bool|None                            =  None, 
#		     ctrlrange:     np.ndarray|list[int|float]           = [-1.571, 1.571], 
#		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
#		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
#		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
#		     cranklength:   int|float                            =  0., 
#		     dyntype:       str                                  = 'none', 
#		     gaintype:      str                                  = 'fixed', 
#		     biastype:      str                                  = 'none', 
#		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
#		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
#		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
#		     sensors:       list|blue.SensorType|None            =  None, 
#		     copy:          bool                                 =  True, 
#		     **kwargs):
#		"""
#		Parameters
#		----------
#		name : str | None, optional
#			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
#			by an enumeration scheme.
#		inparent : bool, optional
#			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
#			the actuator assumes the frame of reference from the parent.
#		ctrllimited : bool | None, optional
#			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
#			at runtime. If false, control input clamping is disabled.
#		forcelimited : bool | None, optional
#			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
#			at runtime. If false, force clamping is disabled.
#		actlimited : bool | None, optional
#			If true, the internal state (activation) associated with this actuator is automatically 
#			clamped to :attr:`actrange` at runtime.
#		ctrlrange : np.ndarray | list[int | float], optional
#			Range for clamping the control input. The compiler expects the first value to be smaller 
#			than the second value. 
#		forcerange : np.ndarray | list[int | float], optional
#			Range for clamping the force output. The compiler expects the first value to be no greater 
#			than the second value. 
#		actrange : np.ndarray | list[int | float], optional
#			Range for clamping the activation state. The compiler expects the first value to be no 
#			greater than the second value.
#		lengthrange : np.ndarray | list[int | float], optional
#			Range of feasible lengths of the actuator’s transmission.
#		gear : np.ndarray | list[int | float], optional
#			This attribute scales the length (and consequently moment arms, velocity and force) of 
#			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
#			detailed description.
#		cranklength : int | float, optional
#			Used only for the slider-crank transmission type. Specifies the length of the connecting 
#			rod. The compiler expects this value to be positive when a slider-crank transmission is 
#			present.
#		dyntype : str, optional
#			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
#		gaintype : str, optional
#			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
#		biastype : str, optional
#			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
#		dynprm : int | float | list[int | float] | np.ndarray, optional
#			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
#		gainprm : int | float | list[int | float] | np.ndarray, optional
#			The gain and bias together determine the output of the force generation mechanism. See 
#			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
#		biasprm : int | float | list[int | float] | np.ndarray, optional
#			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
#		sensors : list | blue.SensorType | None, optional
#			The Sensors are attached to the actuator on initialization.
#		copy : bool, optional
#			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
#			Sensor is attached.
#		**kwargs
#			Keyword arguments are passed to ``super().__init__``.
#		"""
#		raise NotImplemented
#		if axis is not None:
#			if 'gear' in kwargs:
#				kwargs.pop('gear')
#			axis = blue.utils.geometry.Vector.get_axis(axis)
#			gear = np.concatenate([np.zeros(3, dtype=np.float32), axis])
#		else:
#			if 'gear' in kwargs:
#				gear = kwargs.pop('gear')
#			else:
#				gear = self._DEFAULT_VALS['gear']
#		super().__init__(name=name, 
#				 ctrllimited=ctrllimited, 
#				 forcelimited=forcelimited, 
#				 actlimited=actlimited, 
#				 ctrlrange=ctrlrange, 
#				 forcerange=forcerange, 
#				 actrange=actrange, 
#				 lengthrange=lengthrange, 
#				 gear=gear, 
#				 cranklength=cranklength, 
#				 dyntype=dyntype, 
#				 gaintype=gaintype, 
#				 biastype=biastype, 
#				 dynprm=dynprm, 
#				 gainprm=gainprm, 
#				 biasprm=biasprm, 
#				 sensors=sensors, 
#				 copy=copy, 
#				 **kwargs)
#		self.tag = 'general'



# ACTUATOR SHORTCUTS



class Motor(BaseActuator):

	"""
	Parameters
	----------
	name : str | None, optional
		The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
		by an enumeration scheme.
	inparent : bool, optional
		If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
		the actuator assumes the frame of reference from the parent.
	ctrllimited : bool | None, optional
		If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
		at runtime. If false, control input clamping is disabled.
	forcelimited : bool | None, optional
		If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
		at runtime. If false, force clamping is disabled.
	actlimited : bool | None, optional
		If true, the internal state (activation) associated with this actuator is automatically 
		clamped to :attr:`actrange` at runtime.
	ctrlrange : np.ndarray | list[int | float], optional
		Range for clamping the control input. The compiler expects the first value to be smaller 
		than the second value. 
	forcerange : np.ndarray | list[int | float], optional
		Range for clamping the force output. The compiler expects the first value to be no greater 
		than the second value. 
	actrange : np.ndarray | list[int | float], optional
		Range for clamping the activation state. The compiler expects the first value to be no 
		greater than the second value.
	lengthrange : np.ndarray | list[int | float], optional
		Range of feasible lengths of the actuator’s transmission.
	gear : np.ndarray | list[int | float], optional
		This attribute scales the length (and consequently moment arms, velocity and force) of 
		the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
		detailed description.
	cranklength : int | float, optional
		Used only for the slider-crank transmission type. Specifies the length of the connecting 
		rod. The compiler expects this value to be positive when a slider-crank transmission is 
		present.
	dyntype : str, optional
		See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
	gaintype : str, optional
		See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
	biastype : str, optional
		See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
	dynprm : int | float | list[int | float] | np.ndarray, optional
		Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
	gainprm : int | float | list[int | float] | np.ndarray, optional
		The gain and bias together determine the output of the force generation mechanism. See 
		:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
	biasprm : int | float | list[int | float] | np.ndarray, optional
		Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
	sensors : list | blue.SensorType | None, optional
		The Sensors are attached to the actuator on initialization.
	copy : bool, optional
		If True, the Sensors passed during init will be copied before attachment, otherwise the original 
		Sensor is attached.
	**kwargs
		Keyword arguments are passed to ``super().__init__``.
	"""

	pass



class Position(BaseActuator):

	@blue.restrict
	def __init__(self, 
		     name:          str|None                             =  None, 
		     gear:          np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     kp:            int|float                            = 1., 
		     ctrllimited:   bool|None                            =  True, 
		     forcelimited:  bool|None                            =  None, 
		     actlimited:    bool|None                            =  None, 
		     ctrlrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:   int|float                            =  0., 
		     dyntype:       str                                  = 'none', 
		     gaintype:      str                                  = 'fixed', 
		     biastype:      str                                  = 'none', 
		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
		     sensors:       list|blue.SensorType|None            =  None, 
		     copy:          bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
			by an enumeration scheme.
		kp : int|float
			Position feedback gain.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.kp = kp
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def kp(self) -> float:
		"""
		Position feedback gain.
		
		Returns
		-------
		float
		"""
		return self._kp


	@kp.setter
	@blue.restrict
	def kp(self, kp: int|float) -> None:
		"""
		Parameters
		----------
		kp : int | float
			Position feedback gain.
		"""
		self._kp = float(kp)



class Velocity(BaseActuator):
	
	@blue.restrict
	def __init__(self, 
		     name:          str|None                             =  None, 
		     gear:          np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     kv:            int|float                            = 1., 
		     ctrllimited:   bool|None                            =  True, 
		     forcelimited:  bool|None                            =  None, 
		     actlimited:    bool|None                            =  None, 
		     ctrlrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:   int|float                            =  0., 
		     dyntype:       str                                  = 'none', 
		     gaintype:      str                                  = 'fixed', 
		     biastype:      str                                  = 'none', 
		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
		     sensors:       list|blue.SensorType|None            =  None, 
		     copy:          bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
			by an enumeration scheme.
		kv : int|float
			Velocity feedback gain.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.kv = kv
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def kv(self) -> float:
		"""
		Velocity feedback gain.
		
		Returns
		-------
		float
		"""
		return self._kv


	@kv.setter
	@blue.restrict
	def kv(self, kv: int|float) -> None:
		"""
		Parameters
		----------
		kv : int | float
			Velocity feedback gain.
		"""
		self._kv = float(kv)



class IntVelocity(BaseActuator):
	
	@blue.restrict
	def __init__(self, 
		     name:          str|None                             =  None, 
		     gear:          np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     kp:            int|float                            = 1., 
		     ctrllimited:   bool|None                            =  True, 
		     forcelimited:  bool|None                            =  None, 
		     actlimited:    bool|None                            =  None, 
		     ctrlrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:   int|float                            =  0., 
		     dyntype:       str                                  = 'none', 
		     gaintype:      str                                  = 'fixed', 
		     biastype:      str                                  = 'none', 
		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
		     sensors:       list|blue.SensorType|None            =  None, 
		     copy:          bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
			by an enumeration scheme.
		kp : int | float
			Position feedback gain.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.kp = kp
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def kp(self) -> float:
		"""
		Position feedback gain.
		
		Returns
		-------
		float
		"""
		return self._kp


	@kp.setter
	@blue.restrict
	def kp(self, kp: int|float) -> None:
		"""
		Parameters
		----------
		kp : int | float
			Position feedback gain.
		"""
		self._kp = float(kp)



class Damper(BaseActuator):

	@blue.restrict
	def __init__(self, 
		     name:          str|None                             =  None, 
		     gear:          np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     kv:            int|float                            = 1., 
		     ctrllimited:   bool|None                            =  True, 
		     forcelimited:  bool|None                            =  None, 
		     actlimited:    bool|None                            =  None, 
		     ctrlrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:   int|float                            =  0., 
		     dyntype:       str                                  = 'none', 
		     gaintype:      str                                  = 'fixed', 
		     biastype:      str                                  = 'none', 
		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
		     sensors:       list|blue.SensorType|None            =  None, 
		     copy:          bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
			by an enumeration scheme.
		kv : int | float
			Velocity feedback gain.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.kv = kv
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def kv(self) -> float:
		"""
		Velocity feedback gain.
		
		Returns
		-------
		float
		"""
		return self._kv


	@kv.setter
	@blue.restrict
	def kv(self, kv: int|float) -> None:
		"""
		Parameters
		----------
		kv : int | float
			Velocity feedback gain.
		"""
		self._kv = float(kv)



class Cylinder(BaseActuator):

	@blue.restrict
	def __init__(self, 
		     name:         str|None                             =  None, 
		     gear:         np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     timeconst:    int|float                            =  1., 
		     area:         int|float                            =  1., 
		     diameter:     int|float|None                       =  None, 
		     bias:         np.ndarray|list[int|float]           = [0., 0., 0.], 
		     ctrllimited:  bool|None                            =  True, 
		     forcelimited: bool|None                            =  None, 
		     actlimited:   bool|None                            =  None, 
		     ctrlrange:    np.ndarray|list[int|float]           = [0., 0.], 
		     forcerange:   np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:  np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:  int|float                            =  0., 
		     dyntype:      str                                  = 'none', 
		     gaintype:     str                                  = 'fixed', 
		     biastype:     str                                  = 'none', 
		     dynprm:       int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:      int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:      int|float|list[int|float]|np.ndarray =  1., 
		     sensors:      list|blue.SensorType|None            =  None, 
		     copy:         bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
			by an enumeration scheme.
		timeconst : int | float
			Time constant of the activation dynamics.
		area : int | float
			Area of the cylinder. This is used internally as actuator gain.
		diameter : int | float | None
			Instead of area the user can specify diameter. If both are specified, diameter has precedence.
		bias : np.ndarray | list[int | float]
			Bias parameters, copied internally into biasprm.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.timeconst = timeconst
		self.area      = area
		self.diameter  = diameter
		self.bias      = bias
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def timeconst(self) -> float:
		"""
		Time constant of the activation dynamics.
		
		Returns
		-------
		float
		"""
		return self._timeconst


	@timeconst.setter
	@blue.restrict
	def timeconst(self, timeconst: int|float) -> None:
		"""
		Parameters
		----------
		timeconst : int | float
			Time constant of the activation dynamics.
		"""
		self._timeconst = float(timeconst)


	@property
	def area(self) -> float:
		"""
		Area of the cylinder. This is used internally as actuator gain.
		
		Returns
		-------
		float
		"""
		return self._area


	@area.setter
	@blue.restrict
	def area(self, area: int|float) -> None:
		"""
		Parameters
		----------
		area : int | float
			Area of the cylinder. This is used internally as actuator gain.
		"""
		self._area = float(area)


	@property
	def diameter(self) -> float|None:
		"""
		Instead of area the user can specify diameter. If both are specified, diameter has precedence.

		Returns
		-------
		float | None
		"""
		return self._diameter


	@diameter.setter
	@blue.restrict
	def diameter(self, diameter: int|float|None) -> None:
		"""
		Parameters
		----------
		diameter : int | float | None
			Instead of area the user can specify diameter. If both are specified, diameter has precedence.
		"""
		self._diameter = None if diameter is None else float(diameter)


	@property
	def bias(self) -> np.ndarray:
		"""
		Bias parameters, copied internally into biasprm.

		Returns
		-------
		np.ndarray
		"""
		return self._bias


	@bias.setter
	@blue.restrict
	def bias(self, bias: list[int|float]|np.ndarray) -> None:
		"""
		Parameters
		----------
		bias : list[int | float] | np.ndarray
			Bias parameters, copied internally into biasprm.
		"""
		self._bias = np.array(bias, dtype=np.float32)



class Muscle(BaseActuator):
	
	@blue.restrict
	def __init__(self, 
		     name:          str|None                             =  None, 
		     gear:          np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     timeconst:     np.ndarray|list[int|float]           = [0.01, 0.04], 
		     tausmooth:     int|float                            =  0., 
		     range:         np.ndarray|list[int|float]           = [0.75, 1.05], 
		     force:         int|float                            = -1., 
		     scale:         int|float                            =  200, 
		     lmin:          int|float                            =  0.5, 
		     lmax:          int|float                            =  1.6, 
		     vmax:          int|float                            =  1.5, 
		     fpmax:         int|float                            =  1.3, 
		     fvmax:         int|float                            =  1.2, 
		     ctrllimited:   bool|None                            =  True, 
		     forcelimited:  bool|None                            =  None, 
		     actlimited:    bool|None                            =  None, 
		     ctrlrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     forcerange:    np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:      np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:   np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:   int|float                            =  0., 
		     dyntype:       str                                  = 'none', 
		     gaintype:      str                                  = 'fixed', 
		     biastype:      str                                  = 'none', 
		     dynprm:        int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:       int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:       int|float|list[int|float]|np.ndarray =  1., 
		     sensors:       list|blue.SensorType|None            =  None, 
		     copy:          bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will be altered 
			by an enumeration scheme.
		timeconst : np.ndarray | list[int | float]
			Time constants for activation and de-activation dynamics.
		tausmooth : int | float
			Width of smooth transition between activation and deactivation time constants. Units of ctrl, 
			must be nonegative.
		range : np.ndarray | list[int | float]
			Operating length range of the muscle, in units of L0.
		force : int | float
			Peak active force at rest. If this value is negative, the peak force is determined automatically 
			using the scale attribute below.
		scale : int | float
			If the force attribute is negative, the peak active force for the muscle is set to this value 
			divided by mjModel.actuator_acc0. The latter is the norm of the joint-space acceleration vector 
			caused by unit force on the actuator’s transmission in qpos0. In other words, scaling produces 
			higher peak forces for muscles that pull more weight.
		lmin : int | float
			Lower position range of the normalized FLV curve, in units of L0.
		lmax : int | float
			Upper position range of the normalized FLV curve, in units of L0.
		vmax : int | float
			Shortening velocity at which muscle force drops to zero, in units of L0 per second.
		fpmax : int | float
			Passive force generated at lmax, relative to the peak rest force.
		fvmax : int | float
			Active force generated at saturating lengthening velocity, relative to the peak rest force.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.timeconst = timeconst
		self.tausmooth = tausmooth
		self.range     = range
		self.force     = force
		self.scale     = scale
		self.lmin      = lmin
		self.lmax      = lmax
		self.vmax      = vmax
		self.fpmax     = fpmax
		self.fvmax     = fvmax
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def timeconst(self) -> np.ndarray:
		"""
		Time constants for activation and de-activation dynamics.
		
		Returns
		-------
		np.ndarray
		"""
		return self._timeconst


	@timeconst.setter
	@blue.restrict
	def timeconst(self, timeconst: np.ndarray|list[int|float]) -> None:
		"""
		Parameters
		----------
		timeconst : np.ndarray | list[int | float]
			Time constants for activation and de-activation dynamics.
		"""
		self._timeconst = np.array(timeconst, dtype=np.float32)


	@property
	def tausmooth(self) -> float:
		"""
		Width of smooth transition between activation and deactivation time constants. Units of 
		ctrl, must be nonegative.
		
		Returns
		-------
		float
		"""
		return self._area


	@tausmooth.setter
	@blue.restrict
	def tausmooth(self, tausmooth: int|float) -> None:
		"""
		Parameters
		----------
		tausmooth : int | float
			Width of smooth transition between activation and deactivation time constants. Units of 
			ctrl, must be nonegative.
		"""
		self._tausmooth = float(tausmooth)


	@property
	def range(self) -> np.ndarray:
		"""
		Operating length range of the muscle, in units of L0.
		
		Returns
		-------
		np.ndarray
		"""
		return self._range


	@range.setter
	@blue.restrict
	def range(self, range: np.ndarray|list[int|float]) -> None:
		"""
		Parameters
		----------
		range : np.ndarray | list[int | float]
			Operating length range of the muscle, in units of L0.
		"""
		self._range = np.array(range, dtype=np.float32)


	@property
	def force(self) -> float:
		"""
		Peak active force at rest. If this value is negative, the peak force is determined 
		automatically using the scale attribute below.
		
		Returns
		-------
		float
		"""
		return self._force


	@force.setter
	@blue.restrict
	def force(self, force: int|float|np.int32|np.int64|np.float32|np.float64) -> None:
		"""
		Parameters
		----------
		force : int | float
			Peak active force at rest. If this value is negative, the peak force is determined 
			automatically using the scale attribute below.
		"""
		self._force = float(force)


	@property
	def scale(self) -> float:
		"""
		If the force attribute is negative, the peak active force for the muscle is set to this value 
		divided by mjModel.actuator_acc0. The latter is the norm of the joint-space acceleration vector 
		caused by unit force on the actuator’s transmission in qpos0. In other words, scaling produces 
		higher peak forces for muscles that pull more weight.
		
		Returns
		-------
		float
		"""
		return self._scale


	@scale.setter
	@blue.restrict
	def scale(self, scale: int|float) -> None:
		"""
		Parameters
		----------
		scale : int | float
			If the force attribute is negative, the peak active force for the muscle is set to this 
			value divided by mjModel.actuator_acc0. The latter is the norm of the joint-space 
			acceleration vector caused by unit force on the actuator’s transmission in qpos0. In other 
			words, scaling produces higher peak forces for muscles that pull more weight.
		"""
		self._scale = float(scale)


	@property
	def lmin(self) -> float:
		"""
		Lower position range of the normalized FLV curve, in units of L0.
		
		Returns
		-------
		float
		"""
		return self._lmin


	@lmin.setter
	@blue.restrict
	def lmin(self, lmin: int|float) -> None:
		"""
		Parameters
		----------
		lmin : int | float
			Lower position range of the normalized FLV curve, in units of L0.
		"""
		self._lmin = float(lmin)


	@property
	def lmax(self) -> float:
		"""
		Upper position range of the normalized FLV curve, in units of L0.
		
		Returns
		-------
		float
		"""
		return self._flmax

	@lmax.setter
	@blue.restrict
	def lmax(self, lmax: int|float) -> None:
		"""
		Parameters
		----------
		lmax : int | float
			Upper position range of the normalized FLV curve, in units of L0.
		"""
		self._lmax = float(lmax)


	@property
	def vmax(self) -> float:
		"""
		Shortening velocity at which muscle force drops to zero, in units of L0 per second.
		
		Returns
		-------
		float
		"""
		return self._vmax


	@vmax.setter
	@blue.restrict
	def vmax(self, vmax: int|float) -> None:
		"""
		Parameters
		----------
		vmax : int | float
			Shortening velocity at which muscle force drops to zero, in units of L0 per second.
		"""
		self._vmax = float(vmax)


	@property
	def fpmax(self) -> float:
		"""
		Passive force generated at lmax, relative to the peak rest force.
		
		Returns
		-------
		float
		"""
		return self._fpmax


	@fpmax.setter
	@blue.restrict
	def fpmax(self, fpmax: int|float) -> None:
		"""
		Parameters
		----------
		fpmax : int | float
			Passive force generated at lmax, relative to the peak rest force.
		"""
		self._fpmax = float(fpmax)


	@property
	def fvmax(self) -> float:
		"""
		Active force generated at saturating lengthening velocity, relative to the peak rest force.
		
		Returns
		-------
		float
		"""
		return self._fvmax


	@fvmax.setter
	@blue.restrict
	def fvmax(self, fvmax: int|float) -> None:
		"""
		Parameters
		----------
		fvmax : int | float
			Active force generated at saturating lengthening velocity, relative to the peak rest force.
		"""
		self._fvmax = float(fvmax)



class Adhesion(BaseActuator):

	@blue.restrict
	def __init__(self, 
		     name:         str|None                             =  None, 
		     gear:         np.ndarray|list[int|float]           = [1., 0., 0., 0., 0., 0.], 
		     gain:         int|float                            =  1., 
		     ctrllimited:  bool|None                            =  None, 
		     forcelimited: bool|None                            =  None, 
		     actlimited:   bool|None                            =  None, 
		     ctrlrange:    np.ndarray|list[int|float]           = [0., 1.], 
		     forcerange:   np.ndarray|list[int|float]           = [0., 0.], 
		     actrange:     np.ndarray|list[int|float]           = [0., 0.], 
		     lengthrange:  np.ndarray|list[int|float]           = [0., 0.], 
		     cranklength:  int|float                            =  0., 
		     dyntype:      str                                  = 'none', 
		     gaintype:     str                                  = 'fixed', 
		     biastype:     str                                  = 'none', 
		     dynprm:       int|float|list[int|float]|np.ndarray =  1., 
		     gainprm:      int|float|list[int|float]|np.ndarray =  1., 
		     biasprm:      int|float|list[int|float]|np.ndarray =  1., 
		     sensors:      list|blue.SensorType|None            =  None, 
		     copy:         bool                                 =  True, 
		     **kwargs):
		"""
		Parameters
		----------
		name : str | None, optional
			The user specified name for the Actuator. In the case of a naming conflict the name will 
			be altered by an enumeration scheme.
		gain : int | float
			Gain of the adhesion actuator, in units of force. The total adhesion force applied by the 
			Actuator is the control value multiplied by the gain. This force is distributed equally 
			between all the contacts involving geoms belonging to the target Body.
		inparent : bool, optional
			If `:attr:`inparent` is True and the parent of the Actuator is :class:`Joint <blueprints.joints.BaseJoint>` 
			the actuator assumes the frame of reference from the parent.
		ctrllimited : bool | None, optional
			If true, the control input to this actuator is automatically clamped to :attr:`ctrlrange` 
			at runtime. If false, control input clamping is disabled.
		forcelimited : bool | None, optional
			If true, the force output of this actuator is automatically clamped to :attr:`forcerange` 
			at runtime. If false, force clamping is disabled.
		actlimited : bool | None, optional
			If true, the internal state (activation) associated with this actuator is automatically 
			clamped to :attr:`actrange` at runtime.
		ctrlrange : np.ndarray | list[int | float], optional
			Range for clamping the control input. The compiler expects the first value to be smaller 
			than the second value. 
		forcerange : np.ndarray | list[int | float], optional
			Range for clamping the force output. The compiler expects the first value to be no greater 
			than the second value. 
		actrange : np.ndarray | list[int | float], optional
			Range for clamping the activation state. The compiler expects the first value to be no 
			greater than the second value.
		lengthrange : np.ndarray | list[int | float], optional
			Range of feasible lengths of the actuator’s transmission.
		gear : np.ndarray | list[int | float], optional
			This attribute scales the length (and consequently moment arms, velocity and force) of 
			the actuator, for all transmission types. See :attr:`gear <BaseActuator.gear>` for a 
			detailed description.
		cranklength : int | float, optional
			Used only for the slider-crank transmission type. Specifies the length of the connecting 
			rod. The compiler expects this value to be positive when a slider-crank transmission is 
			present.
		dyntype : str, optional
			See :attr:`dyntype <BaseActuator.dyntype>` for a detailed description.
		gaintype : str, optional
			See :attr:`gaintype <BaseActuator.gaintype>` for a detailed description.
		biastype : str, optional
			See :attr:`biastype <BaseActuator.biastype>` for a detailed description.
		dynprm : int | float | list[int | float] | np.ndarray, optional
			Activation dynamics parameters. See :attr:`dynprm <BaseActuator.dynprm>` for a detailed description.
		gainprm : int | float | list[int | float] | np.ndarray, optional
			The gain and bias together determine the output of the force generation mechanism. See 
			:attr:`gainprm <BaseActuator.gainprm>` for a detailed description.
		biasprm : int | float | list[int | float] | np.ndarray, optional
			Bias parameters. See :attr:`biasprm <BaseActuator.biasprm>` for a detailed description.
		sensors : list | blue.SensorType | None, optional
			The Sensors are attached to the actuator on initialization.
		copy : bool, optional
			If True, the Sensors passed during init will be copied before attachment, otherwise the original 
			Sensor is attached.
		**kwargs
			Keyword arguments are passed to ``super().__init__``.
		"""
		self.gain = gain
		super().__init__(name=name, 
				 ctrllimited=ctrllimited, 
				 forcelimited=forcelimited, 
				 actlimited=actlimited, 
				 ctrlrange=ctrlrange, 
				 forcerange=forcerange, 
				 actrange=actrange, 
				 lengthrange=lengthrange, 
				 gear=gear, 
				 cranklength=cranklength, 
				 dyntype=dyntype, 
				 gaintype=gaintype, 
				 biastype=biastype, 
				 dynprm=dynprm, 
				 gainprm=gainprm, 
				 biasprm=biasprm, 
				 sensors=sensors, 
				 copy=copy, 
				 **kwargs)


	@property
	def parent(self) -> blue.BodyType|None:
		"""
		The parent to which the Thing is attach, if it unattached parent is None.

		Returns
		-------
		blueBodyType
		"""
		if hasattr(self, '_parent'):
			return self._parent
		else:
			return None
		#return self._parent # super().super().__getattribute__('parent')


	@parent.setter
	@blue.restrict
	def parent(self, parent: blue.BodyType|None) -> None:
		"""
		Parameters
		----------
		parent : blueBodyType
			The parent to which the Thing is attach, if it unattached parent is None. Only 
			:class:`Bodies <blueprints.body.Body>` are valid parents of :class:`Adhesion`.
		"""
		
		# OLD
		#super().__setattr__('parent', parent)
		# NEW
		if parent is not None and self.parent is not parent and self not in parent:
			parent.attach(self, copy=False)
		self._parent = parent


	@property
	def gain(self) -> float:
		"""
		Gain of the adhesion actuator, in units of force. The total adhesion force applied by the 
		Actuator is the control value multiplied by the gain. This force is distributed equally 
		between all the contacts involving geoms belonging to the target Body.
		
		Returns
		-------
		float
		"""
		return self._fvmax


	@gain.setter
	@blue.restrict
	def gain(self, gain: int|float) -> None:
		"""
		Parameters
		----------
		gain : int | float
			Gain of the adhesion actuator, in units of force. The total adhesion force applied by the 
			Actuator is the control value multiplied by the gain. This force is distributed equally 
			between all the contacts involving geoms belonging to the target Body.
		"""
		self._gain = float(gain)



ACTUATOR_THINGS = {'general':     General, 
		   'motor':       Motor, 
		   'position':    Position, 
		   'velocity':    Velocity, 
		   'intvelocity': IntVelocity, 
		   'damper':      Damper, 
		   'cylinder':    Cylinder, 
		   'muscle':      Muscle, 
		   'adhesion':    Adhesion}