Combine all mjs_attach functions into one.

PiperOrigin-RevId: 742702092
Change-Id: I89e35c59ada017cd061e2031584eeea71776743d
This commit is contained in:
Alessio Quaglino
2025-04-01 08:04:58 -07:00
committed by Copybara-Service
parent 08d22baea1
commit a02a27d4a4
13 changed files with 202 additions and 319 deletions
+5 -32
View File
@@ -3808,41 +3808,14 @@ Free all pointers with ``mju_free()``.
Attachment
^^^^^^^^^^
.. _mjs_attachBody:
.. _mjs_attach:
`mjs_attachBody <#mjs_attachBody>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`mjs_attach <#mjs_attach>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjs_attachBody
.. mujoco-include:: mjs_attach
Attach child body to a parent frame, return the attached body if success or NULL otherwise.
.. _mjs_attachFrame:
`mjs_attachFrame <#mjs_attachFrame>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjs_attachFrame
Attach child frame to a parent body, return the attached frame if success or NULL otherwise.
.. _mjs_attachToSite:
`mjs_attachToSite <#mjs_attachToSite>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjs_attachToSite
Attach child body to a parent site, return the attached body if success or NULL otherwise.
.. _mjs_attachFrameToSite:
`mjs_attachFrameToSite <#mjs_attachFrameToSite>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjs_attachFrameToSite
Attach child frame to a parent site, return the attached frame if success or NULL otherwise.
Attach child to a parent, return the attached element if success or NULL otherwise.
.. _mjs_detachBody:
+2 -3
View File
@@ -175,11 +175,10 @@ replicating 200 times, suffixes will be ``000, 001, ...`` etc). All referencing
and namespaced appropriately. Detailed examples of models using replicate can be found in the
`model/replicate/ <https://github.com/google-deepmind/mujoco/tree/main/model/replicate>`__ directory.
There is a caveat concerning :ref:`keyframes<keyframe>` when using replicate. Since :ref:`mjs_attachFrame` is used to
There is a caveat concerning :ref:`keyframes<keyframe>` when using replicate. Since :ref:`mjs_attach` is used to
self-attach multiple times the enclosed kinematic tree, if this tree contains further :ref:`attach<body-attach>`
elements, keyframes will not be replicated nor namespaced by :ref:`replicate<replicate>`, but they will be attached and
namespaced once by the innermost call of :ref:`mjs_attachFrame` or :ref:`mjs_attachBody`. See the limitations discussed
in :ref:`attach<body-attach>`.
namespaced once by the innermost call of :ref:`mjs_attach`. See the limitations discussed in :ref:`attach<body-attach>`.
.. _replicate-count:
+2
View File
@@ -10,6 +10,8 @@ Upcoming version (not yet released)
- The default value of the flag for toggling :ref:`internal flex contacts<flex-contact-internal>` was changed from
"true" to "false". This feature has proven to be counterintuitive for users.
- All of the attach functions (``mjs_attachBody``, ``mjs_attachFrame``, ``mjs_attachToSite``,
``mjs_attachFrameToSite``) have been removed and replaced by a single function :ref:`mjs_attach`.
General
^^^^^^^
+2 -8
View File
@@ -3613,14 +3613,8 @@ void mju_threadPoolEnqueue(mjThreadPool* thread_pool, mjTask* task);
void mju_threadPoolDestroy(mjThreadPool* thread_pool);
void mju_defaultTask(mjTask* task);
void mju_taskJoin(mjTask* task);
mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child,
const char* prefix, const char* suffix);
mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
const char* prefix, const char* suffix);
mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
const char* prefix, const char* suffix);
mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child,
const char* prefix, const char* suffix);
mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix);
int mjs_detachBody(mjSpec* s, mjsBody* b);
int mjs_detachDefault(mjSpec* s, mjsDefault* d);
mjsBody* mjs_addBody(mjsBody* body, const mjsDefault* def);
+15 -14
View File
@@ -111,9 +111,10 @@ This framework introduces a powerful new feature: attaching and detaching model
to power the :ref:`attach<body-attach>` an :ref:`replicate<replicate>` meta-elements in MJCF. Attachment allows the user
to move or copy a subtree from one model into another, while also copying or moving related referenced assets and
referencing elements from outside the kinematic tree (e.g., actuators and sensors). Similarly, detaching a subtree will
remove all associated elements from the model. The default behavior is to move during attach. The user can select to
instead copy by passing the corresponding flag to ``mjs_setDeepCopy``. This flag is temporary set to true while parsing
XMLs. It is possible to :ref:`attach a body to a frame<mjs_attachBody>`:
remove all associated elements from the model. The default behavior is to move the child into the parent while
attaching, so subsequent changes to the child will also change the parent. Alternatively, the user can choose to make an
entirely new copy during attach using :ref:`mjs_setDeepCopy`. This flag is temporarily set to true while parsing XMLs.
It is possible to :ref:`attach a body to a frame<mjs_attach>`:
.. code-block:: C
@@ -121,29 +122,29 @@ XMLs. It is possible to :ref:`attach a body to a frame<mjs_attachBody>`:
mjSpec* child = mj_makeSpec();
parent->compiler.degree = 0;
child->compiler.degree = 1;
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), NULL);
mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), NULL);
mjsBody* attached_body_1 = mjs_attachBody(frame, body, "attached-", "-1");
mjsElement* frame = mjs_addFrame(mjs_findBody(parent, "world"), NULL)->element;
mjsElement* body = mjs_addBody(mjs_findBody(child, "world"), NULL)->element;
mjsBody* attached_body_1 = mjs_asBody(mjs_attach(frame, body, "attached-", "-1"));
or :ref:`attach a body to a site<mjs_attachToSite>`:
or :ref:`attach a body to a site<mjs_attach>`:
.. code-block:: C
mjSpec* parent = mj_makeSpec();
mjSpec* child = mj_makeSpec();
mjsSite* site = mjs_addSite(mjs_findBody(parent, "world"), NULL);
mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), NULL);
mjsBody* attached_body_2 = mjs_attachToSite(site, body, "attached-", "-2");
mjsElement* site = mjs_addSite(mjs_findBody(parent, "world"), NULL)->element;
mjsElement* body = mjs_addBody(mjs_findBody(child, "world"), NULL)->element;
mjsBody* attached_body_2 = mjs_asBody(mjs_attach(site, body, "attached-", "-2"));
or :ref:`attach a frame to a body<mjs_attachFrame>`:
or :ref:`attach a frame to a body<mjs_attach>`:
.. code-block:: C
mjSpec* parent = mj_makeSpec();
mjSpec* child = mj_makeSpec();
mjsBody* body = mjs_addBody(mjs_findBody(parent, "world"), NULL);
mjsFrame* frame = mjs_addFrame(mjs_findBody(child, "world"), NULL);
mjsFrame* attached_frame = mjs_attachFrame(body, frame, "attached-", "-1");
mjsElement* body = mjs_addBody(mjs_findBody(parent, "world"), NULL)->element;
mjsElement* frame = mjs_addFrame(mjs_findBody(child, "world"), NULL)->element;
mjsFrame* attached_frame = mjs_asFrame(mjs_attach(body, frame, "attached-", "-1"));
Note that in the above examples, the parent and child models have different values for ``compiler.degree``,
corresponding to the :ref:`compiler/angle<compiler-angle>` attribute, specifying the units in which angles are
+3 -15
View File
@@ -1414,21 +1414,9 @@ MJAPI void mju_taskJoin(mjTask* task);
//---------------------------------- Attachment ----------------------------------------------------
// Attach child body to a parent frame, return the attached body if success or NULL otherwise.
MJAPI mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child,
const char* prefix, const char* suffix);
// Attach child frame to a parent body, return the attached frame if success or NULL otherwise.
MJAPI mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
const char* prefix, const char* suffix);
// Attach child body to a parent site, return the attached body if success or NULL otherwise.
MJAPI mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
const char* prefix, const char* suffix);
// Attach child frame to a parent site, return the attached frame if success or NULL otherwise.
MJAPI mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child,
const char* prefix, const char* suffix);
// Attach child to a parent, return the attached element if success or NULL otherwise.
MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix);
// Delete body and descendants from mjSpec, remove all references, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b);
+6 -108
View File
@@ -9000,23 +9000,23 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
doc='Wait for a task to complete.',
)),
('mjs_attachBody',
('mjs_attach',
FunctionDecl(
name='mjs_attachBody',
name='mjs_attach',
return_type=PointerType(
inner_type=ValueType(name='mjsBody'),
inner_type=ValueType(name='mjsElement'),
),
parameters=(
FunctionParameterDecl(
name='parent',
type=PointerType(
inner_type=ValueType(name='mjsFrame'),
inner_type=ValueType(name='mjsElement'),
),
),
FunctionParameterDecl(
name='child',
type=PointerType(
inner_type=ValueType(name='mjsBody', is_const=True),
inner_type=ValueType(name='mjsElement', is_const=True),
),
),
FunctionParameterDecl(
@@ -9032,109 +9032,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
),
),
doc='Attach child body to a parent frame, return the attached body if success or NULL otherwise.', # pylint: disable=line-too-long
)),
('mjs_attachFrame',
FunctionDecl(
name='mjs_attachFrame',
return_type=PointerType(
inner_type=ValueType(name='mjsFrame'),
),
parameters=(
FunctionParameterDecl(
name='parent',
type=PointerType(
inner_type=ValueType(name='mjsBody'),
),
),
FunctionParameterDecl(
name='child',
type=PointerType(
inner_type=ValueType(name='mjsFrame', is_const=True),
),
),
FunctionParameterDecl(
name='prefix',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
FunctionParameterDecl(
name='suffix',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
),
doc='Attach child frame to a parent body, return the attached frame if success or NULL otherwise.', # pylint: disable=line-too-long
)),
('mjs_attachToSite',
FunctionDecl(
name='mjs_attachToSite',
return_type=PointerType(
inner_type=ValueType(name='mjsBody'),
),
parameters=(
FunctionParameterDecl(
name='parent',
type=PointerType(
inner_type=ValueType(name='mjsSite'),
),
),
FunctionParameterDecl(
name='child',
type=PointerType(
inner_type=ValueType(name='mjsBody', is_const=True),
),
),
FunctionParameterDecl(
name='prefix',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
FunctionParameterDecl(
name='suffix',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
),
doc='Attach child body to a parent site, return the attached body if success or NULL otherwise.', # pylint: disable=line-too-long
)),
('mjs_attachFrameToSite',
FunctionDecl(
name='mjs_attachFrameToSite',
return_type=PointerType(
inner_type=ValueType(name='mjsFrame'),
),
parameters=(
FunctionParameterDecl(
name='parent',
type=PointerType(
inner_type=ValueType(name='mjsSite'),
),
),
FunctionParameterDecl(
name='child',
type=PointerType(
inner_type=ValueType(name='mjsFrame', is_const=True),
),
),
FunctionParameterDecl(
name='prefix',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
FunctionParameterDecl(
name='suffix',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
),
doc='Attach child frame to a parent site, return the attached frame if success or NULL otherwise.', # pylint: disable=line-too-long
doc='Attach child to a parent, return the attached element if success or NULL otherwise.', # pylint: disable=line-too-long
)),
('mjs_detachBody',
FunctionDecl(
+13 -11
View File
@@ -540,7 +540,7 @@ PYBIND11_MODULE(_specs, m) {
SetFrame(worldbody, mjOBJ_CAMERA, worldframe);
const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.value().c_str() : "";
raw::MjsFrame* attached_frame = nullptr;
raw::MjsElement* attached_frame = nullptr;
if (frame.has_value()) {
raw::MjsFrame* frame_ptr = nullptr;
try {
@@ -560,11 +560,12 @@ PYBIND11_MODULE(_specs, m) {
if (!parent_body) {
throw pybind11::value_error("Frame does not have a parent body.");
}
attached_frame = mjs_attachFrame(parent_body, worldframe, p, s);
attached_frame =
mjs_attach(parent_body->element, worldframe->element, p, s);
if (!attached_frame) {
throw pybind11::value_error(mjs_getError(self.ptr));
}
if (mjs_setFrame(attached_frame->element, frame_ptr) != 0) {
if (mjs_setFrame(attached_frame, frame_ptr) != 0) {
throw pybind11::value_error(mjs_getError(self.ptr));
}
}
@@ -583,7 +584,8 @@ PYBIND11_MODULE(_specs, m) {
throw pybind11::value_error(
"Site spec does not match parent spec.");
}
attached_frame = mjs_attachFrameToSite(site_ptr, worldframe, p, s);
attached_frame =
mjs_attach(site_ptr->element, worldframe->element, p, s);
if (!attached_frame) {
throw pybind11::value_error(mjs_getError(self.ptr));
}
@@ -597,7 +599,7 @@ PYBIND11_MODULE(_specs, m) {
self.assets[asset.first] = asset.second;
}
child.parent = &self;
return attached_frame;
return mjs_asFrame(attached_frame);
},
py::arg("child"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(), py::arg("site") = py::none(),
@@ -829,11 +831,11 @@ PYBIND11_MODULE(_specs, m) {
std::optional<std::string>& suffix) -> raw::MjsFrame* {
const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.value().c_str() : "";
auto new_frame = mjs_attachFrame(&self, &frame, p, s);
auto new_frame = mjs_attach(self.element, frame.element, p, s);
if (!new_frame) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
}
return new_frame;
return mjs_asFrame(new_frame);
},
py::arg("frame"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(),
@@ -870,12 +872,12 @@ PYBIND11_MODULE(_specs, m) {
std::optional<std::string>& suffix) -> raw::MjsBody* {
const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.value().c_str() : "";
auto new_body = mjs_attachBody(&self, &body, p, s);
auto new_body = mjs_attach(self.element, body.element, p, s);
if (!new_body) {
throw pybind11::value_error(
mjs_getError(mjs_getSpec(self.element)));
}
return new_body;
return mjs_asBody(new_body);
},
py::arg("body"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(),
@@ -953,12 +955,12 @@ PYBIND11_MODULE(_specs, m) {
std::optional<std::string>& suffix) -> raw::MjsBody* {
const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.value().c_str() : "";
auto new_body = mjs_attachToSite(&self, &body, p, s);
auto new_body = mjs_attach(self.element, body.element, p, s);
if (!new_body) {
throw pybind11::value_error(
mjs_getError(mjs_getSpec(self.element)));
}
return new_body;
return mjs_asBody(new_body);
},
py::arg("body"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(),
+92 -67
View File
@@ -73,7 +73,7 @@ mjSpec* mj_copySpec(const mjSpec* s) {
try {
modelC = new mjCModel(*static_cast<mjCModel*>(s->element));
} catch (mjCError& e) {
mju_error("Failed to copy spec: %s", e.message);
modelC->SetError(e);
return nullptr;
}
return &modelC->spec;
@@ -121,100 +121,125 @@ mjModel* mj_compile(mjSpec* s, const mjVFS* vfs) {
// attach body to a frame of the parent
mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child,
const char* prefix, const char* suffix) {
if (!parent) {
mju_error("parent frame is null");
return nullptr;
}
mjCFrame* frame_parent = static_cast<mjCFrame*>(parent->element);
mjCBody* child_body = static_cast<mjCBody*>(child->element);
static mjsElement* attachBody(mjCFrame* parent, const mjCBody* child,
const char* prefix, const char* suffix) {
try {
*frame_parent += std::string(prefix) + *child_body + std::string(suffix);
*parent += std::string(prefix) + *(mjCBody*)child + std::string(suffix);
} catch (mjCError& e) {
frame_parent->model->SetError(e);
parent->model->SetError(e);
return nullptr;
}
mjsBody* attached_body = frame_parent->last_attached;
frame_parent->last_attached = nullptr;
return attached_body;
mjsBody* attached_body = parent->last_attached;
parent->last_attached = nullptr;
return attached_body->element;
}
// attach frame to a parent body
mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
const char* prefix, const char* suffix) {
if (!parent) {
mju_error("parent body is null");
return nullptr;
}
mjCBody* body_parent = static_cast<mjCBody*>(parent->element);
mjCFrame* child_frame = static_cast<mjCFrame*>(child->element);
static mjsElement* attachFrame(mjCBody* parent, const mjCFrame* child,
const char* prefix, const char* suffix) {
try {
*body_parent += std::string(prefix) + *child_frame + std::string(suffix);
*parent += std::string(prefix) + *(mjCFrame*)child + std::string(suffix);
} catch (mjCError& e) {
body_parent->model->SetError(e);
parent->model->SetError(e);
return nullptr;
}
mjsFrame* attached_frame = body_parent->last_attached;
body_parent->last_attached = nullptr;
return attached_frame;
mjsFrame* attached_frame = parent->last_attached;
parent->last_attached = nullptr;
return attached_frame->element;
}
// attach child body to a parent site
mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
const char* prefix, const char* suffix) {
if (!parent) {
mju_error("parent site is null");
return nullptr;
}
mjSpec* spec = mjs_getSpec(parent->element);
mjCSite* site = static_cast<mjCSite*>(parent->element);
mjCBody* body = site->Body();
mjCFrame* frame = body->AddFrame(site->frame);
static mjsElement* attachToSite(mjCSite* parent, const mjCBody* child,
const char* prefix, const char* suffix) {
mjSpec* spec = mjs_getSpec(parent->spec.element);
mjCBody* body = parent->Body();
mjCFrame* frame = body->AddFrame(parent->frame);
frame->SetParent(body);
frame->spec.pos[0] = site->spec.pos[0];
frame->spec.pos[1] = site->spec.pos[1];
frame->spec.pos[2] = site->spec.pos[2];
frame->spec.quat[0] = site->spec.quat[0];
frame->spec.quat[1] = site->spec.quat[1];
frame->spec.quat[2] = site->spec.quat[2];
frame->spec.quat[3] = site->spec.quat[3];
frame->spec.pos[0] = parent->spec.pos[0];
frame->spec.pos[1] = parent->spec.pos[1];
frame->spec.pos[2] = parent->spec.pos[2];
frame->spec.quat[0] = parent->spec.quat[0];
frame->spec.quat[1] = parent->spec.quat[1];
frame->spec.quat[2] = parent->spec.quat[2];
frame->spec.quat[3] = parent->spec.quat[3];
mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree,
spec->compiler.eulerseq, &site->spec.alt);
return mjs_attachBody(&frame->spec, child, prefix, suffix);
spec->compiler.eulerseq, &parent->spec.alt);
return attachBody(frame, child, prefix, suffix);
}
// attach child frame to a parent site
mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child,
const char* prefix, const char* suffix) {
static mjsElement* attachFrameToSite(mjCSite* parent, const mjCFrame* child,
const char* prefix, const char* suffix) {
mjSpec* spec = mjs_getSpec(parent->spec.element);
mjCBody* body = parent->Body();
mjCFrame* frame = body->AddFrame(parent->frame);
frame->SetParent(body);
frame->spec.pos[0] = parent->spec.pos[0];
frame->spec.pos[1] = parent->spec.pos[1];
frame->spec.pos[2] = parent->spec.pos[2];
frame->spec.quat[0] = parent->spec.quat[0];
frame->spec.quat[1] = parent->spec.quat[1];
frame->spec.quat[2] = parent->spec.quat[2];
frame->spec.quat[3] = parent->spec.quat[3];
mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree,
spec->compiler.eulerseq, &parent->spec.alt);
mjsElement* attached_frame = attachFrame(body, child, prefix, suffix);
mjs_setFrame(attached_frame, &frame->spec);
return attached_frame;
}
mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix) {
if (!parent) {
mju_error("parent site is null");
mju_error("parent element is null");
return nullptr;
}
mjSpec* spec = mjs_getSpec(parent->element);
mjCSite* site = static_cast<mjCSite*>(parent->element);
mjCBody* body = site->Body();
mjCFrame* frame = body->AddFrame(site->frame);
frame->SetParent(body);
frame->spec.pos[0] = site->spec.pos[0];
frame->spec.pos[1] = site->spec.pos[1];
frame->spec.pos[2] = site->spec.pos[2];
frame->spec.quat[0] = site->spec.quat[0];
frame->spec.quat[1] = site->spec.quat[1];
frame->spec.quat[2] = site->spec.quat[2];
frame->spec.quat[3] = site->spec.quat[3];
mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree,
spec->compiler.eulerseq, &site->spec.alt);
mjsFrame* attached_frame = mjs_attachFrame(&body->spec, child, prefix, suffix);
mjs_setFrame(attached_frame->element, &frame->spec);
return attached_frame;
if (!child) {
mju_error("child element is null");
return nullptr;
}
mjCModel* model = static_cast<mjCModel*>(mjs_getSpec(parent)->element);
switch (parent->elemtype) {
case mjOBJ_FRAME:
if (child->elemtype == mjOBJ_BODY) {
return attachBody(static_cast<mjCFrame*>(parent),
static_cast<const mjCBody*>(child), prefix, suffix);
} else {
model->SetError(mjCError(0, "child element is not a body"));
return nullptr;
}
case mjOBJ_BODY:
if (child->elemtype == mjOBJ_FRAME) {
return attachFrame(static_cast<mjCBody*>(parent),
static_cast<const mjCFrame*>(child), prefix, suffix);
} else {
model->SetError(mjCError(0, "child element is not a frame"));
return nullptr;
}
case mjOBJ_SITE:
if (child->elemtype == mjOBJ_BODY) {
return attachToSite(static_cast<mjCSite*>(parent),
static_cast<const mjCBody*>(child), prefix, suffix);
} else if (child->elemtype == mjOBJ_FRAME) {
return attachFrameToSite(static_cast<mjCSite*>(parent),
static_cast<const mjCFrame*>(child), prefix, suffix);
} else {
model->SetError(mjCError(0, "child element is not a body or frame"));
return nullptr;
}
default:
model->SetError(mjCError(0, "parent element is not a frame, body or site"));
return nullptr;
}
return nullptr;
}
+3 -15
View File
@@ -69,21 +69,9 @@ MJAPI int mjs_setDeepCopy(mjSpec* s, int deepcopy);
//---------------------------------- Attachment ----------------------------------------------------
// Attach child body to a parent frame, return the attached body if success or NULL otherwise.
MJAPI mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child,
const char* prefix, const char* suffix);
// Attach child frame to a parent body, return the attached frame if success or NULL otherwise.
MJAPI mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
const char* prefix, const char* suffix);
// Attach child body to a parent site, return the attached body if success or NULL otherwise.
MJAPI mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
const char* prefix, const char* suffix);
// Attach child frame to a parent site, return the attached frame if success or NULL otherwise.
MJAPI mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child,
const char* prefix, const char* suffix);
// Attach child to a parent, return the attached element if success or NULL otherwise.
MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix);
// Detach body from mjSpec, remove all references and delete the body, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b);
+2 -2
View File
@@ -3566,7 +3566,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
UpdateString(suffix, count, i);
// attach to parent
if (!mjs_attachFrame(body, pframe, /*prefix=*/"", suffix.c_str())) {
if (!mjs_attach(body->element, pframe->element, /*prefix=*/"", suffix.c_str())) {
throw mjXError(elem, mjs_getError(spec));
}
}
@@ -3642,7 +3642,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
if (!child) {
throw mjXError(elem, "could not find body '%s''%s'", body_name.c_str());
}
if (!mjs_attachBody(pframe, child, prefix.c_str(), "")) {
if (!mjs_attach(pframe->element, child->element, prefix.c_str(), "")) {
throw mjXError(elem, mjs_getError(spec));
}
} else {
+56 -43
View File
@@ -279,20 +279,23 @@ TEST_F(PluginTest, AttachPlugin) {
EXPECT_THAT(body_1, NotNull());
mjsFrame* attachment_frame = mjs_addFrame(body_1, 0);
EXPECT_THAT(attachment_frame, NotNull());
mjs_attachBody(attachment_frame, mjs_findBody(spec_1, "body"), "child-", "");
mjs_attach(attachment_frame->element, mjs_findBody(spec_1, "body")->element,
"child-", "");
mjModel* model_1 = mj_compile(parent, nullptr);
EXPECT_THAT(model_1, NotNull());
EXPECT_THAT(model_1->nbody, 3);
// attach it a second time to test namespacing and compile
ASSERT_THAT(spec_2, NotNull()) << err.data();
mjs_attachBody(attachment_frame, mjs_findBody(spec_2, "body"), "copy-", "");
mjs_attach(attachment_frame->element, mjs_findBody(spec_2, "body")->element,
"copy-", "");
mjModel* model_2 = mj_compile(parent, nullptr);
EXPECT_THAT(model_2, NotNull());
EXPECT_THAT(model_2->nbody, 4);
// attach a body not referencing the plugin and compile
mjs_attachBody(attachment_frame, mjs_findBody(spec_3, "empty"), "empty-", "");
mjs_attach(attachment_frame->element, mjs_findBody(spec_3, "empty")->element,
"empty-", "");
mjModel* model_3 = mj_compile(parent, nullptr);
EXPECT_THAT(model_3, NotNull());
EXPECT_THAT(model_3->nbody, 5);
@@ -314,9 +317,9 @@ TEST_F(PluginTest, DetachPlugin) {
ASSERT_THAT(child, NotNull()) << err.data();
// attach a body referencing the plugin to the frame
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), 0);
mjsBody* body = mjs_findBody(child, "body");
EXPECT_THAT(mjs_attachBody(frame, body, "child-", ""), NotNull());
mjsElement* frame = mjs_addFrame(mjs_findBody(parent, "world"), 0)->element;
mjsElement* body = mjs_findBody(child, "body")->element;
EXPECT_THAT(mjs_attach(frame, body, "child-", ""), NotNull());
// detach the body and compile
mjsBody* body_to_detach = mjs_findBody(parent, "child-body");
@@ -372,7 +375,8 @@ TEST_F(PluginTest, AttachExplicitPlugin) {
mjsFrame* attachment_frame = mjs_addFrame(body_parent, 0);
EXPECT_THAT(attachment_frame, NotNull());
mjs_attachBody(attachment_frame, mjs_findBody(child, "body"), "child-", "");
mjs_attach(attachment_frame->element, mjs_findBody(child, "body")->element,
"child-", "");
mjModel* model = mj_compile(parent, nullptr);
EXPECT_THAT(model, NotNull());
EXPECT_THAT(model->nplugin, 1);
@@ -937,7 +941,8 @@ TEST_F(MujocoTest, AttachSame) {
EXPECT_THAT(body, NotNull());
// attach child to parent frame
mjsBody* attached = mjs_attachBody(frame, body, "attached-", "-1");
mjsBody* attached =
mjs_asBody(mjs_attach(frame->element, body->element, "attached-", "-1"));
EXPECT_THAT(attached, mjs_findBody(parent, "attached-body-1"));
// check that the spec was not copied
@@ -1073,7 +1078,8 @@ TEST_F(MujocoTest, AttachDifferent) {
EXPECT_THAT(body, NotNull());
// attach child to parent frame
mjsBody* attached = mjs_attachBody(frame, body, "attached-", "-1");
mjsBody* attached =
mjs_asBody(mjs_attach(frame->element, body->element, "attached-", "-1"));
EXPECT_THAT(attached, mjs_findBody(parent, "attached-body-1"));
// check that the spec was copied
@@ -1212,7 +1218,8 @@ TEST_F(MujocoTest, AttachFrame) {
EXPECT_THAT(frame, NotNull());
// attach child frame to parent body
mjsFrame* attached = mjs_attachFrame(body, frame, "attached-", "-1");
mjsFrame* attached =
mjs_asFrame(mjs_attach(body->element, frame->element, "attached-", "-1"));
EXPECT_THAT(attached, mjs_findFrame(parent, "attached-pframe-1"));
// check that the spec was copied
@@ -1286,7 +1293,7 @@ TEST_F(MujocoTest, AttachCompiled) {
// attach child body to the frame
mjsBody* to_attach = mjs_findBody(child, "base");
EXPECT_THAT(to_attach, NotNull()) << mjs_getError(child);
mjs_attachBody(frame, to_attach, "", "");
mjs_attach(frame->element, to_attach->element, "", "");
// check that attached model can be compiled
mjModel* m_attached = mj_compile(parent, 0);
@@ -1433,7 +1440,8 @@ TEST_F(MujocoTest, AttachToSite) {
EXPECT_THAT(site, NotNull());
mjsBody* body = mjs_findBody(child, "sphere");
EXPECT_THAT(body, NotNull());
mjsBody* attached = mjs_attachToSite(site, body, "attached-", "-1");
mjsBody* attached =
mjs_asBody(mjs_attach(site->element, body->element, "attached-", "-1"));
EXPECT_THAT(attached, NotNull());
mjModel* model = mj_compile(parent, 0);
@@ -1498,7 +1506,8 @@ TEST_F(MujocoTest, AttachFrameToSite) {
EXPECT_THAT(site, NotNull());
mjsFrame* frame = mjs_findFrame(child, "frame");
EXPECT_THAT(frame, NotNull());
mjsFrame* attached = mjs_attachFrameToSite(site, frame, "attached-", "-1");
mjsFrame* attached =
mjs_asFrame(mjs_attach(site->element, frame->element, "attached-", "-1"));
EXPECT_THAT(attached, NotNull());
mjModel* model = mj_compile(parent, 0);
@@ -1573,7 +1582,8 @@ TEST_F(MujocoTest, BodyToFrame) {
EXPECT_THAT(frame, NotNull());
mjsBody* body = mjs_findBody(child1, "sphere");
EXPECT_THAT(body, NotNull());
mjsBody* attached = mjs_attachBody(frame, body, "attached-", "-1");
mjsBody* attached =
mjs_asBody(mjs_attach(frame->element, body->element, "attached-", "-1"));
EXPECT_THAT(attached, NotNull());
mjModel* model1 = mj_compile(parent, 0);
EXPECT_THAT(model1, NotNull());
@@ -1581,7 +1591,8 @@ TEST_F(MujocoTest, BodyToFrame) {
// attach the world to the same frame and convert it to a frame
mjsBody* world = mjs_findBody(child2, "world");
EXPECT_THAT(world, NotNull());
mjsBody* child_world = mjs_attachBody(frame, world, "attached-", "-2");
mjsBody* child_world =
mjs_asBody(mjs_attach(frame->element, world->element, "attached-", "-2"));
EXPECT_THAT(child_world, NotNull());
mjsFrame* frame_world = mjs_bodyToFrame(&child_world);
EXPECT_THAT(frame_world, NotNull());
@@ -1662,7 +1673,8 @@ TEST_F(MujocoTest, AttachSpecToSite) {
mjs_setFrame(mjs_firstChild(world, mjOBJ_CAMERA, 0), frame);
// attach the entire spec to the site
mjsFrame* worldframe = mjs_attachFrameToSite(site, frame, "attached-", "-1");
mjsFrame* worldframe =
mjs_asFrame(mjs_attach(site->element, frame->element, "attached-", "-1"));
EXPECT_THAT(worldframe, NotNull());
// compile and compare
@@ -1739,7 +1751,8 @@ TEST_F(MujocoTest, AttachSpecToBody) {
mjs_setFrame(mjs_firstChild(world, mjOBJ_CAMERA, 0), frame);
// attach the entire spec to the site
mjsFrame* worldframe = mjs_attachFrame(body, frame, "attached-", "-1");
mjsFrame* worldframe =
mjs_asFrame(mjs_attach(body->element, frame->element, "attached-", "-1"));
EXPECT_THAT(worldframe, NotNull());
worldframe->pos[0] = 1;
worldframe->pos[1] = 2;
@@ -1937,8 +1950,8 @@ TEST_F(MujocoTest, RecompileAttach) {
mjSpec* child2 = mj_parseXMLString(xml, 0, er.data(), er.size());
EXPECT_THAT(child2, NotNull());
mjsFrame* frame1 = mjs_addFrame(mjs_findBody(parent, "world"), 0);
mjs_attachBody(frame1, mjs_findBody(child1, "body"), "child-", "-1");
mjsElement* frame1 = mjs_addFrame(mjs_findBody(parent, "world"), 0)->element;
mjs_attach(frame1, mjs_findBody(child1, "body")->element, "child-", "-1");
mjModel* model = mj_compile(parent, 0);
EXPECT_THAT(model, NotNull());
@@ -1950,8 +1963,8 @@ TEST_F(MujocoTest, RecompileAttach) {
mj_step(model, data);
}
mjsFrame* frame2 = mjs_addFrame(mjs_findBody(parent, "world"), 0);
mjs_attachBody(frame2, mjs_findBody(child2, "body"), "child-", "-2");
mjsElement* frame2 = mjs_addFrame(mjs_findBody(parent, "world"), 0)->element;
mjs_attach(frame2, mjs_findBody(child2, "body")->element, "child-", "-2");
EXPECT_EQ(mj_recompile(parent, 0, model, data), 0);
EXPECT_THAT(model, NotNull());
@@ -2003,8 +2016,8 @@ TEST_F(MujocoTest, AttachMocap) {
mjsBody* world = mjs_findBody(spec, "world");
EXPECT_THAT(world, NotNull());
mjsFrame* frame = mjs_addFrame(world, NULL);
mjs_attachBody(frame, body, "attached-", "-1");
mjsElement* frame = mjs_addFrame(world, NULL)->element;
mjs_attach(frame, body->element, "attached-", "-1");
mjsBody* attached_body = mjs_findBody(spec, "attached-mocap-1");
EXPECT_THAT(attached_body, NotNull());
@@ -2083,7 +2096,7 @@ TEST_F(MujocoTest, AttachUnnamedAssets) {
geom->type = mjGEOM_MESH;
mjSpec* spec = mj_makeSpec();
mjs_attachFrame(mjs_findBody(spec, "world"), frame, "_", "");
mjs_attach(mjs_findBody(spec, "world")->element, frame->element, "_", "");
mjModel* model = mj_compile(spec, vfs.get());
EXPECT_THAT(model, NotNull());
@@ -2229,8 +2242,8 @@ void AttachNestedKeyframe(bool compile) {
mjs_setDeepCopy(child, true);
// attach gchild to child
mjs_attachBody(mjs_findFrame(child, "frame"),
mjs_findBody(gchild, "body"), "gchild-", "");
mjs_attach(mjs_findFrame(child, "frame")->element,
mjs_findBody(gchild, "body")->element, "gchild-", "");
// compile required before further attachment
mjModel* m_child = compile ? mj_compile(child, 0) : nullptr;
@@ -2243,8 +2256,8 @@ void AttachNestedKeyframe(bool compile) {
};
// attach child to parent
mjs_attachBody(mjs_findFrame(parent, "frame"),
mjs_findBody(child, "body"), "child-", "");
mjs_attach(mjs_findFrame(parent, "frame")->element,
mjs_findBody(child, "body")->element, "child-", "");
EXPECT_THAT(warning, HasSubstr(compile ? "" : "model has pending keyframes"));
@@ -2305,11 +2318,11 @@ TEST_F(MujocoTest, RepeatedAttachKeyframe) {
EXPECT_THAT(child, NotNull()) << er.data();
mjsBody* body_1 = mjs_findBody(parent, "body");
mjsFrame* attachment_frame = mjs_addFrame(body_1, 0);
mjs_attachBody(attachment_frame, mjs_findBody(child, "b1"), "b1-", "");
mjsElement* attachment_frame = mjs_addFrame(body_1, 0)->element;
mjs_attach(attachment_frame, mjs_findBody(child, "b1")->element, "b1-", "");
mjModel* model_1 = mj_compile(parent, 0);
EXPECT_THAT(model_1, NotNull());
mjs_attachBody(attachment_frame, mjs_findBody(child, "b2"), "b2-", "");
mjs_attach(attachment_frame, mjs_findBody(child, "b2")->element, "b2-", "");
mjModel* model_2 = mj_compile(parent, 0);
EXPECT_THAT(model_2, NotNull());
@@ -2374,8 +2387,8 @@ TEST_F(MujocoTest, ResizeParentKeyframe) {
mjSpec* child = mj_parseXMLString(xml_child, 0, er.data(), er.size());
EXPECT_THAT(child, NotNull()) << er.data();
mjs_attachBody(mjs_findFrame(parent, "frame"), mjs_findBody(child, "body"),
"child-", "");
mjs_attach(mjs_findFrame(parent, "frame")->element,
mjs_findBody(child, "body")->element, "child-", "");
mjModel* model = mj_compile(parent, 0);
EXPECT_THAT(model, NotNull());
@@ -2466,12 +2479,10 @@ TEST_F(MujocoTest, DifferentUnitsAllowed) {
mjSpec* child = mj_parseXMLString(child_xml, 0, error.data(), error.size());
mjSpec* spec = mj_parseXMLString(parent_xml, 0, error.data(), error.size());
ASSERT_THAT(spec, NotNull()) << error.data();
mjs_attachBody(mjs_findFrame(child, "frame"),
mjs_findBody(gchild, "gchild"),
"gchild_", "");
mjs_attachBody(mjs_findFrame(spec, "frame"),
mjs_findBody(child, "child"),
"child_", "");
mjs_attach(mjs_findFrame(child, "frame")->element,
mjs_findBody(gchild, "gchild")->element, "gchild_", "");
mjs_attach(mjs_findFrame(spec, "frame")->element,
mjs_findBody(child, "child")->element, "child_", "");
mjModel* model = mj_compile(spec, 0);
EXPECT_THAT(model, NotNull());
@@ -2544,7 +2555,8 @@ TEST_F(MujocoTest, DifferentOptionsInAttachedFrame) {
EXPECT_THAT(world, NotNull());
mjsFrame* child_frame = mjs_findFrame(child, "child");
EXPECT_THAT(child_frame, NotNull());
mjsFrame* attached_frame = mjs_attachFrame(world, child_frame, "child-", "");
mjsElement* attached_frame =
mjs_attach(world->element, child_frame->element, "child-", "");
EXPECT_THAT(attached_frame, NotNull());
// wrap the child frame in the parent frame and compile
@@ -2659,8 +2671,9 @@ TEST_F(MujocoTest, ApplyNameSpaceToDefaults) {
mjSpec* parent = mj_parseXMLString(xml_p, 0, err.data(), err.size());
EXPECT_THAT(parent, NotNull()) << err.data();
mjsBody* attached = mjs_attachBody(mjs_findFrame(parent, "parent"),
mjs_findBody(child, "body"), "child-", "");
mjsElement* attached =
mjs_attach(mjs_findFrame(parent, "parent")->element,
mjs_findBody(child, "body")->element, "child-", "");
EXPECT_THAT(attached, NotNull());
mjModel* model = mj_compile(parent, vfs.get());
@@ -2774,7 +2787,7 @@ TEST_F(MujocoTest, ErrorWhenCompilingOrphanedSpec) {
EXPECT_THAT(body, NotNull());
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), nullptr);
EXPECT_THAT(frame, NotNull());
mjs_attachBody(frame, body, "child-", "");
mjs_attach(frame->element, body->element, "child-", "");
mj_deleteSpec(parent);
mjModel* model = mj_compile(child, 0);
EXPECT_THAT(model, IsNull());
+1 -1
View File
@@ -623,7 +623,7 @@ TEST_F(MujocoTest, Modeldir) {
mjSpec* spec = mj_makeSpec();
mjs_setDeepCopy(spec, true);
mjs_setString(spec->meshdir, "asset");
mjs_attachFrame(mjs_findBody(spec, "world"), frame, "_", "");
mjs_attach(mjs_findBody(spec, "world")->element, frame->element, "_", "");
mjModel* model = mj_compile(spec, vfs.get());
EXPECT_THAT(model, NotNull());