World¶
raisim::World
class creates/manages all resources.
All objects defined in the same World class instance can collide with each other unless otherwise their collision mask and group explicitly disables the collision (read Contact and Collision
section for this topic).
There are two ways to generate the World instance (i.e., two constructors).
The first way is to load an raisim world configuration file, which is in a form of an XML file.
You can read more about the XML format in the World Configuration File
section.
The second way is to generate world dynamically in code.
You can also mix the two ways, by loading an XML file and dynamically adding objects.
There is an experimental MJCF (MuJoCo file format) reader. Simple models can be constructed with an MJCF file.
Adding New Objects¶
To add a new object of a shape X, a method named addX
is used.
For example, to add a sphere
raisim::World world;
auto sphere = world.addSphere(0.5, 1.0);
Here sphere
is a pointer to the internal resource.
It can be used to access or to modify the internal variables.
There are three hidden arguments to all object-creation methods: material
, collisionGroup
and collisionMask
.
Descriptions of the collision variables are given in “Collision and Contact” chapter.
material
argument specifies the material which governs contact dynamics.
It is further explained in “Material System” chapter.
The list of objects is given in “Object” chapter.
Once an object is added, a name can be set as below
sphere.setName("ball");
A pointer to an object with a specific name can be retrieved as below
auto ball = world.getObject("ball");
An object might contain multiple bodies (i.e., articulated system). To designate each body, local index can be used. To keep the interface consistent, many methods ask for the local index even for simgle body objects. In a single body object case, local index arguments are ignored and users can simply put 0 to comply with the AIP.
Save world to an XML file¶
raisim::World::exportToXml()
lets you save the world to an XML file.
EXAMPLES
MuJoCo mjcf file loader¶
RaiSim partially supports mjcf file. There are inherent differences between the two simulators and inevitably some features are not supported. But you can load simple mjcf files (e.g., ones provided by OpenAI).
Changing Simulation Parameters¶
The following parameters can be changed using the world API
Time step
RaiSim uses a fixed time step. The time step obtained and modified using getTimeStep
and setTimeStep
method.
API¶
-
class
World
¶ Public Functions
-
explicit
World
()¶ Create an empty world
-
explicit
World
(const std::string &configFile, const std::unordered_map<std::string, std::string> ¶ms = {})¶ Create an world as specified in the xml config file
-
void
exportToXml
(const std::string &dir, const std::string &fileName)¶ export the world to an xml config file, which can be loaded using a constructor
- Parameters
dir – [in] directory to save the xml file
fileName – [in] file name
-
inline void
exportToXml
(const std::string &pathIn)¶ export the world to an xml config file, which can be loaded using a constructor
- Parameters
path – [in] path to save the xml file
-
inline void
setTimeStep
(double dt)¶ set the time step
- Parameters
dt – [in] the time step
-
inline double
getTimeStep
() const¶ - Returns
the time step
-
Sphere *
addSphere
(double radius, double mass, const std::string &material = "default", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
radius – [in] radius
mass – [in] mass
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”.
- Returns
pointer to the created box
-
Box *
addBox
(double xLength, double yLength, double zLength, double mass, const std::string &material = "default", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
xLength – [in] x dimension
yLength – [in] y dimension
zLength – [in] z dimension
mass – [in] mass
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”.
- Returns
pointer to the created box
-
Cylinder *
addCylinder
(double radius, double height, double mass, const std::string &material = "default", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
radius – [in] radius
height – [in] center-to-center distance
mass – [in] mass
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”.
- Returns
pointer to the created cylinder
-
Capsule *
addCapsule
(double radius, double height, double mass, const std::string &material = "default", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
radius – [in] radius
height – [in] center-to-center distance
mass – [in] mass
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”.
- Returns
pointer to the created capsule
-
Ground *
addGround
(double zHeight = 0.0, const std::string &material = "default", CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
zHeight – [in] height of the terrain
material – [in] material of the height map (which defines the contact dynamics)
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”. Note that collision group of a static object is CollisionGroup(1) << 61ul
- Returns
pointer to the created ground
-
HeightMap *
addHeightMap
(size_t xSamples, size_t ySamples, double xSize, double ySize, double centerX, double centerY, const std::vector<double> &height, const std::string &material = "default", CollisionGroup collisionGroup = RAISIM_STATIC_COLLISION_GROUP, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
xSamples – [in] how many points along x axis
ySamples – [in] how many points along y axis
xSize – [in] x width of the height map
ySize – [in] y length of the height map
centerX – [in] x coordinate of the center of the height map
centerY – [in] y coordinate of the center of the height map
height – [in] a vector of doubles representing heights. the size should be xSample X ySamples
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created height map
-
HeightMap *
addHeightMap
(const std::string &raisimHeightMapFileName, double centerX, double centerY, const std::string &material = "default", CollisionGroup collisionGroup = RAISIM_STATIC_COLLISION_GROUP, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
raisimHeightMapFileName – [in] the raisim text file which will be used to create the height map
centerX – [in] x coordinate of the center of the height map
centerY – [in] y coordinate of the center of the height map
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created height map
-
HeightMap *
addHeightMap
(const std::string &pngFileName, double centerX, double centerY, double xSize, double ySize, double heightScale, double heightOffset, const std::string &material = "default", CollisionGroup collisionGroup = RAISIM_STATIC_COLLISION_GROUP, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
pngFileName – [in] the png file which will be used to create the height map
centerX – [in] x coordinate of the center of the height map
centerY – [in] y coordinate of the center of the height map
xSize – [in] x width of the height map
ySize – [in] y length of the height map
heightScale – [in] a png file (if 8-bit) has pixel values from 0 to 255. This parameter scales the pixel values to the actual height
heightOffset – [in] height of the 0-value pixel
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created height map
-
HeightMap *
addHeightMap
(double centerX, double centerY, TerrainProperties &terrainProperties, const std::string &material = "default", CollisionGroup collisionGroup = RAISIM_STATIC_COLLISION_GROUP, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
centerX – [in] x coordinate of the center of the height map
centerY – [in] y coordinate of the center of the height map
terrainProperties – [in] perlin noise parameters which will be used to create the height map
material – [in] material of the height map (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created height map
-
HeightMap *
addHeightMap
(const HeightMap *heightmapToBeCloned, CollisionGroup collisionGroup = RAISIM_STATIC_COLLISION_GROUP, CollisionGroup collisionMask = CollisionGroup(-1))¶ - Parameters
heightmapToBeCloned – [in] Another height map to be cloned
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created height map
-
ArticulatedSystem *
addArticulatedSystem
(const std::string &filePathOrURDFScript, const std::string &resPath = "", const std::vector<std::string> &jointOrder = {}, CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1), ArticulatedSystemOption options = ArticulatedSystemOption())¶ - Parameters
filePathOrURDFScript – [in] Path to urdf file or a URDF string. Depending on the contents of the string, RaiSim will interpret it as an xml string or a file path.
resPath – [in] Path to the resource directory. Leave it empty (“”) if it is the urdf file directory
jointOrder – [in] this can be used to redefine the joint order. A child cannot precede its parent. Leave it empty ({}) to use the joint order defined in the URDF file.
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
options – [in] Currently only support “doNotCollideWithParent”
- Returns
pointer to the articulated system
-
ArticulatedSystem *
addArticulatedSystem
(const std::string &xmlFileTemplate, const std::unordered_map<std::string, std::string> ¶ms, const std::string &resPath = "", const std::vector<std::string> &jointOrder = {}, CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1), ArticulatedSystemOption options = ArticulatedSystemOption())¶ - Parameters
xmlFileTemplate – [in] xml template file.
params – [in] parameters for the xml file.
resPath – [in] Path to the resource directory. Leave it empty (“”) if it is the urdf file directory
jointOrder – [in] this can be used to redefine the joint order. A child cannot precede its parent. Leave it empty ({}) to use the joint order defined in the URDF file.
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
options – [in] Currently only support “doNotCollideWithParent”
- Returns
pointer to the articulated system
-
ArticulatedSystem *
addArticulatedSystem
(const Child &child, const std::string &resPath = "", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1), ArticulatedSystemOption options = ArticulatedSystemOption())¶ This method programmatically creates an articulated system without an URDF file.
- Parameters
child – [in] an instance of Child class which has an articulated system structure.
resPath – [in] Path to the resource directory. Leave it empty (“”) if it is the urdf file directory
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
options – [in] Currently only support “doNotCollideWithParent”
- Returns
pointer to the articulated system
-
Compound *
addCompound
(const std::vector<Compound::CompoundObjectChild> &children, double mass, Vec<3> COM, const Mat<3, 3> &inertia, CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ Add a single body which is composed of multiple primitive collision shapes
- Parameters
children – [in] a vector of CompoundObjectChild which contains each primitive shape’s position, orientation, material and shape parameters
mass – [in] mass of the composite body
COM – [in] center of the composite body
inertia – [in] inertia of the composite body
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created compound object
-
Mesh *
addMesh
(const std::string &meshFileInObjFormat, double mass, const Mat<3, 3> &inertia, const Vec<3> &COM, double scale = 1, const std::string &material = "", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ create mesh collision body. only the obj format is supported
- Parameters
meshFileInObjFormat – [in] obj file of the mesh
mass – [in] mass
inertia – [in] inertia
COM – [in] the center of the mass
scale – [in] rescale the mesh
material – [in] material of the mesh (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created wire
-
Mesh *
addMesh
(const std::string &meshFileInObjFormat, double mass, double scale = 1, const std::string &material = "", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ create mesh collision body. only the obj format is supported. Inertia and COM are estimated from the mesh geometry assuming that it is a box.
- Parameters
meshFileInObjFormat – [in] obj file of the mesh
mass – [in] mass
scale – [in] rescale the mesh
material – [in] material of the mesh (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created wire
-
Mesh *
addMesh
(const Mesh *meshToClone, const std::string &material = "", CollisionGroup collisionGroup = 1, CollisionGroup collisionMask = CollisionGroup(-1))¶ create mesh collision body. only the obj format is supported
- Parameters
meshToClone – [in] mesh to copy
material – [in] material of the mesh (which defines the contact dynamics)
collisionGroup – [in] read “Contact and Collision/ Collision Group and Mask”
collisionMask – [in] read “Contact and Collision/ Collision Group and Mask”
- Returns
pointer to the created wire
-
StiffLengthConstraint *
addStiffWire
(Object *obj1, size_t localIdx1, Vec<3> pos1_b, Object *obj2, size_t localIdx2, Vec<3> pos2_b, double length)¶ Stiff unilateral constraint. It cannot push. It can only pull.
- Parameters
obj1 – [in] the first object the wire is attached to
localIdx1 – [in] the body index (0 for a SingleBodyObject) for the first object
pos1_b – [in] location of the cable attachment on the first object
obj2 – [in] the second object the wire is attached to
localIdx2 – [in] the body index (0 for a SingleBodyObject) for the second object
pos2_b – [in] location of the cable attachment on the second object
length – [in] length of the wire
- Returns
pointer to the created wire
-
CompliantLengthConstraint *
addCompliantWire
(Object *obj1, int localIdx1, Vec<3> pos1_b, Object *obj2, int localIdx2, Vec<3> pos2_b, double length, double stiffness)¶ soft unilateral constraint. It cannot push. It can only pull.
- Parameters
obj1 – [in] the first object the wire is attached to
localIdx1 – [in] the body index (0 for a SingleBodyObject) for the first object
pos1_b – [in] location of the cable attachment on the first object
obj2 – [in] the second object the wire is attached to
localIdx2 – [in] the body index (0 for a SingleBodyObject) for the second object
pos2_b – [in] location of the cable attachment on the second object
length – [in] length of the wire
stiffness – [in] stiffness of the wire
- Returns
pointer to the created wire
-
CustomLengthConstraint *
addCustomWire
(Object *obj1, int localIdx1, Vec<3> pos1_b, Object *obj2, int localIdx2, Vec<3> pos2_b, double length)¶ Custom wire that applies user-set tension between two points.
- Parameters
obj1 – [in] the first object the wire is attached to
localIdx1 – [in] the body index (0 for a SingleBodyObject) for the first object
pos1_b – [in] location of the cable attachment on the first object
obj2 – [in] the second object the wire is attached to
localIdx2 – [in] the body index (0 for a SingleBodyObject) for the second object
pos2_b – [in] location of the cable attachment on the second object
length – [in] length of the wire. You can use this to compute how much it stretched from a nominal length. It might not be necessary for some wire types.
- Returns
pointer to the created wire
-
Object *
getObject
(const std::string &name)¶ - Returns
object with the given name. returns nullptr if the object doesn’t exist. The name can be set by Object::setName()
-
inline Object *
getObject
(std::size_t worldIndex)¶ - Returns
object with the given index. This index can be retrieved by Object::getIndexInWorld()
-
Constraints *
getConstraint
(const std::string &name)¶ - Returns
a constraint (e.g., wires) with the given name. returns nullptr if the object doesn’t exist. The name can be set by Wire::setName(). This is equivalent to getWire(const std::string&)
-
LengthConstraint *
getWire
(const std::string &name)¶ - Returns
a wire with the given name. returns nullptr if the object doesn’t exist. The name can be set by Wire::setName()
-
inline unsigned long
getConfigurationNumber
()¶ - Returns
the configuration number. this number is updated every time an object is added or removed
-
const RayCollisionList &
rayTest
(const Eigen::Vector3d &start, const Eigen::Vector3d &direction, double length, bool closestOnly = true, size_t objectId = size_t(-10), size_t localId = size_t(-10), CollisionGroup collisionMask = CollisionGroup(-1))¶ Returns the internal reference of the ray collision list it contains the geoms (position, normal, object world/local id) and the number of intersections This returns
- Parameters
start – [in] The start position of the ray.
direction – [in] The direction of the ray.
length – [in] The length of the ray.
closestOnly – [in] Only stores the first collision.
objectId – [in] Collision with objects with both matching objectId and localId will be ignored. This can be useful if the sensor is attached to a robot.
localId – [in] Collision with objects with both matching objectId and localId will be ignored. This can be useful if the sensor is attached to a robot.
collisionMask – [in] Collision mask to filter collisions. By default, it records collisions with all collision groups.
- Returns
A reference to the internal container which contains all ray collisions.
-
void
removeObject
(LengthConstraint *wire)¶ removes a wire (i.e., LengthConstraint)
- Parameters
wire – [in] the wire to be removed
-
void
integrate
()¶ integrate the world It is equivalent to “integrate1(); integrate2();”
-
void
integrate1
()¶ It performs 1) deletion contacts from previous time step 2) collision detection 3) register contacts to each body 4) calls “preContactSolverUpdate1()” of each object
-
void
integrate2
()¶ It performs 1) calls “preContactSolverUpdate2()” of each body 2) run collision solver 3) calls “integrate” method of each object
-
inline const contact::ContactProblems *
getContactProblem
() const¶ It performs 1) calls “preContactSolverUpdate2()” of each body 2) run collision solver 3) calls “integrate” method of each object
-
void
setGravity
(const Vec<3> &gravity)¶ Set gravity
- Parameters
gravity – [in] gravitational acceleration of the world
-
void
updateMaterialProp
(const MaterialManager &prop)¶ this deletes the existing material props and replace them with the argument
- Parameters
prop – new material prop
-
void
setMaterialPairProp
(const std::string &mat1, const std::string &mat2, double friction, double restitution, double resThreshold)¶ Add a new material pair property. In RaiSim, material property is defined by the pair.
- Parameters
mat1 – [in] name of the first material (the order of mat1 and mat2 is not important)
mat2 – [in] name of the first material
friction – [in] the coefficient of friction
restitution – [in] the coefficient of restitution
resThreshold – [in] the minimum impact velocity to make the object bounce
-
void
setMaterialPairProp
(const std::string &mat1, const std::string &mat2, double friction, double restitution, double resThreshold, double staticFriction, double staticFrictionThresholdVelocity)¶ Add a new material pair property. In RaiSim, material property is defined by the pair.
- Parameters
mat1 – [in] name of the first material (the order of mat1 and mat2 is not important)
mat2 – [in] name of the first material
friction – [in] the dynamic coefficient of friction
restitution – [in] the coefficient of restitution
resThreshold – [in] the minimum impact velocity to make the object bounce
staticFriction – [in] the static coefficient of friction
staticFrictionThresholdVelocity – [in] if the relative velocity of two points is bigger than this value, then the dynamic coefficient of friction is applied. Otherwise, the coefficient of friction is interpolated between the static and dynamic one proportional to the relative velocity.
-
void
setDefaultMaterial
(double friction, double restitution, double resThreshold)¶ this default material property is used if a material pair property is not defined for the specific collision
- Parameters
friction – [in] the coefficient of friction
restitution – [in] the coefficient of restitution
resThreshold – [in] the minimum impact velocity to make the object bounce
-
void
setDefaultMaterial
(double friction, double restitution, double resThreshold, double staticFriction, double staticFrictionThresholdVelocity)¶ this default material property is used if a material pair property is not defined for the specific collision
- Parameters
friction – [in] the coefficient of friction
restitution – [in] the coefficient of restitution
resThreshold – [in] the minimum impact velocity to make the object bounce
staticFriction – [in] the static coefficient of friction
staticFrictionThresholdVelocity – [in] if the relative velocity of two points is bigger than this value, then the dynamic coefficient of friction is applied. Otherwise, the coefficient of friction is interpolated between the static and dynamic one proportional to the relative velocity.
-
inline const Vec<3> &
getGravity
() const¶ - Returns
gravitational acceleration of the world
-
void
setERP
(double erp, double erp2 = 0)¶ Changes the Error Reduction Parameter. It often has very minimalistic impact on simulation
- Parameters
erp – [in] spring constant between object. This constant is scaled by the apparent inertia so it has no well-defined physical meaning
erp2 – [in] damping constant between object. This constant is scaled by the apparent inertia so it has no well-defined physical meaning
-
void
setContactSolverParam
(double alpha_init, double alpha_min, double alpha_decay, int maxIter, double threshold)¶ Changes the contact solver parameter. For details, please check “Hwangbo, Jemin, Joonho Lee, and Marco Hutter. “Per-contact iteration method for solving contact dynamics.” IEEE Robotics and Automation Letters 3.2 (2018): 895-902.” We found that the three default alpha values are always the best. So they are hardcoded now. So only the maxIter and threshold will affect the simulation. The three input alpha values will be ignored
- Parameters
alpha_init – [in] NOT_USED_ANYMORE (default: 1.0) how aggressive the solver is initially
alpha_min – [in] NOT_USED_ANYMORE (default: 1.0) how aggressive the solver is after an infinite number of solver iterations
alpha_decay – [in] NOT_USED_ANYMORE (default: 1.0) how fast alpha converges from alpha_init to alpha_min
threshold – [in] (default: 1e-8) error threshold for termination
maxIter – [in] (default: 150) the maximum number of iterations allowed
-
inline double
getWorldTime
() const¶ - Returns
the total integrated time (which is updated at every integrate2() call)
-
inline void
setWorldTime
(double time)¶ manually adjust the world time
- Parameters
time – [in] the world time
-
inline raisim::contact::BisectionContactSolver &
getContactSolver
()¶ - Returns
a non-const ref of the contact solver. contact::BisectionContactSolver::setOrder(bool) can be used to make the solver deterministic
-
inline void
setContactSolverIterationOrder
(bool order)¶ This sets the iteration order (e.g., forward and backward) of the contact solver. Given this order, Raisim is deterministic. Without an explicit order, the order flips in every
integrate
call.- Parameters
order –
-
inline const raisim::contact::BisectionContactSolver &
getContactSolver
() const¶ - Returns
a const ref of the contact solver. Internal states can be retrieved using this method
-
inline const std::string &
getConfigFile
()¶ get the config file if the world was created using a xml config file
- Returns
the path to the xml config file
-
inline std::vector<std::unique_ptr<LengthConstraint>> &
getWires
()¶ get a vector wires in the world
- Returns
a vector of unique_ptrs of wires
-
inline const MaterialPairProperties &
getMaterialPairProperties
(const std::string &mat1, const std::string &mat2) const¶ get the material pair properties. The order of materials does not matter.
- Parameters
mat1 – [in] material name
mat2 – [in] material name
- Returns
material pair properties
-
inline void
lockMutex
()¶ locks world mutex. This can be used if you use raisim in a multi-threaded environment.
-
inline void
unlockMutex
()¶ unlock world mutex. This can be used if you use raisim in a multi-threaded environment.
Public Static Functions
-
static inline void
setActivationKey
(const std::string &activationKey)¶ export the world to an xml config file, which can be loaded using a constructor
- Parameters
activationKey – [in] path to the license file
-
explicit