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 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. Attach child to a parent, return the attached element 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.
.. _mjs_detachBody: .. _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 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. `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>` 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 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 namespaced once by the innermost call of :ref:`mjs_attach`. See the limitations discussed in :ref:`attach<body-attach>`.
in :ref:`attach<body-attach>`.
.. _replicate-count: .. _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 - 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. "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 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_threadPoolDestroy(mjThreadPool* thread_pool);
void mju_defaultTask(mjTask* task); void mju_defaultTask(mjTask* task);
void mju_taskJoin(mjTask* task); void mju_taskJoin(mjTask* task);
mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child, mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix); 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);
int mjs_detachBody(mjSpec* s, mjsBody* b); int mjs_detachBody(mjSpec* s, mjsBody* b);
int mjs_detachDefault(mjSpec* s, mjsDefault* d); int mjs_detachDefault(mjSpec* s, mjsDefault* d);
mjsBody* mjs_addBody(mjsBody* body, const mjsDefault* def); 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 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 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 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 remove all associated elements from the model. The default behavior is to move the child into the parent while
instead copy by passing the corresponding flag to ``mjs_setDeepCopy``. This flag is temporary set to true while parsing attaching, so subsequent changes to the child will also change the parent. Alternatively, the user can choose to make an
XMLs. It is possible to :ref:`attach a body to a frame<mjs_attachBody>`: 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 .. 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(); mjSpec* child = mj_makeSpec();
parent->compiler.degree = 0; parent->compiler.degree = 0;
child->compiler.degree = 1; child->compiler.degree = 1;
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), NULL); mjsElement* frame = mjs_addFrame(mjs_findBody(parent, "world"), NULL)->element;
mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), NULL); mjsElement* body = mjs_addBody(mjs_findBody(child, "world"), NULL)->element;
mjsBody* attached_body_1 = mjs_attachBody(frame, body, "attached-", "-1"); 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 .. code-block:: C
mjSpec* parent = mj_makeSpec(); mjSpec* parent = mj_makeSpec();
mjSpec* child = mj_makeSpec(); mjSpec* child = mj_makeSpec();
mjsSite* site = mjs_addSite(mjs_findBody(parent, "world"), NULL); mjsElement* site = mjs_addSite(mjs_findBody(parent, "world"), NULL)->element;
mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), NULL); mjsElement* body = mjs_addBody(mjs_findBody(child, "world"), NULL)->element;
mjsBody* attached_body_2 = mjs_attachToSite(site, body, "attached-", "-2"); 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 .. code-block:: C
mjSpec* parent = mj_makeSpec(); mjSpec* parent = mj_makeSpec();
mjSpec* child = mj_makeSpec(); mjSpec* child = mj_makeSpec();
mjsBody* body = mjs_addBody(mjs_findBody(parent, "world"), NULL); mjsElement* body = mjs_addBody(mjs_findBody(parent, "world"), NULL)->element;
mjsFrame* frame = mjs_addFrame(mjs_findBody(child, "world"), NULL); mjsElement* frame = mjs_addFrame(mjs_findBody(child, "world"), NULL)->element;
mjsFrame* attached_frame = mjs_attachFrame(body, frame, "attached-", "-1"); 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``, 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 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 ---------------------------------------------------- //---------------------------------- Attachment ----------------------------------------------------
// Attach child body to a parent frame, return the attached body if success or NULL otherwise. // Attach child to a parent, return the attached element if success or NULL otherwise.
MJAPI mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child, MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix); 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);
// Delete body and descendants from mjSpec, remove all references, return 0 on success. // Delete body and descendants from mjSpec, remove all references, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b); 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.', doc='Wait for a task to complete.',
)), )),
('mjs_attachBody', ('mjs_attach',
FunctionDecl( FunctionDecl(
name='mjs_attachBody', name='mjs_attach',
return_type=PointerType( return_type=PointerType(
inner_type=ValueType(name='mjsBody'), inner_type=ValueType(name='mjsElement'),
), ),
parameters=( parameters=(
FunctionParameterDecl( FunctionParameterDecl(
name='parent', name='parent',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjsFrame'), inner_type=ValueType(name='mjsElement'),
), ),
), ),
FunctionParameterDecl( FunctionParameterDecl(
name='child', name='child',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjsBody', is_const=True), inner_type=ValueType(name='mjsElement', is_const=True),
), ),
), ),
FunctionParameterDecl( 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 doc='Attach child to a parent, return the attached element 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
)), )),
('mjs_detachBody', ('mjs_detachBody',
FunctionDecl( FunctionDecl(
+13 -11
View File
@@ -540,7 +540,7 @@ PYBIND11_MODULE(_specs, m) {
SetFrame(worldbody, mjOBJ_CAMERA, worldframe); SetFrame(worldbody, mjOBJ_CAMERA, worldframe);
const char* p = prefix.has_value() ? prefix.value().c_str() : ""; const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.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()) { if (frame.has_value()) {
raw::MjsFrame* frame_ptr = nullptr; raw::MjsFrame* frame_ptr = nullptr;
try { try {
@@ -560,11 +560,12 @@ PYBIND11_MODULE(_specs, m) {
if (!parent_body) { if (!parent_body) {
throw pybind11::value_error("Frame does not have a 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) { if (!attached_frame) {
throw pybind11::value_error(mjs_getError(self.ptr)); 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)); throw pybind11::value_error(mjs_getError(self.ptr));
} }
} }
@@ -583,7 +584,8 @@ PYBIND11_MODULE(_specs, m) {
throw pybind11::value_error( throw pybind11::value_error(
"Site spec does not match parent spec."); "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) { if (!attached_frame) {
throw pybind11::value_error(mjs_getError(self.ptr)); throw pybind11::value_error(mjs_getError(self.ptr));
} }
@@ -597,7 +599,7 @@ PYBIND11_MODULE(_specs, m) {
self.assets[asset.first] = asset.second; self.assets[asset.first] = asset.second;
} }
child.parent = &self; child.parent = &self;
return attached_frame; return mjs_asFrame(attached_frame);
}, },
py::arg("child"), py::arg("prefix") = py::none(), py::arg("child"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(), py::arg("site") = 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* { std::optional<std::string>& suffix) -> raw::MjsFrame* {
const char* p = prefix.has_value() ? prefix.value().c_str() : ""; const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.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) { if (!new_frame) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); 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("frame"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(), py::arg("suffix") = py::none(),
@@ -870,12 +872,12 @@ PYBIND11_MODULE(_specs, m) {
std::optional<std::string>& suffix) -> raw::MjsBody* { std::optional<std::string>& suffix) -> raw::MjsBody* {
const char* p = prefix.has_value() ? prefix.value().c_str() : ""; const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.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) { if (!new_body) {
throw pybind11::value_error( throw pybind11::value_error(
mjs_getError(mjs_getSpec(self.element))); mjs_getError(mjs_getSpec(self.element)));
} }
return new_body; return mjs_asBody(new_body);
}, },
py::arg("body"), py::arg("prefix") = py::none(), py::arg("body"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(), py::arg("suffix") = py::none(),
@@ -953,12 +955,12 @@ PYBIND11_MODULE(_specs, m) {
std::optional<std::string>& suffix) -> raw::MjsBody* { std::optional<std::string>& suffix) -> raw::MjsBody* {
const char* p = prefix.has_value() ? prefix.value().c_str() : ""; const char* p = prefix.has_value() ? prefix.value().c_str() : "";
const char* s = suffix.has_value() ? suffix.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) { if (!new_body) {
throw pybind11::value_error( throw pybind11::value_error(
mjs_getError(mjs_getSpec(self.element))); mjs_getError(mjs_getSpec(self.element)));
} }
return new_body; return mjs_asBody(new_body);
}, },
py::arg("body"), py::arg("prefix") = py::none(), py::arg("body"), py::arg("prefix") = py::none(),
py::arg("suffix") = py::none(), py::arg("suffix") = py::none(),
+92 -67
View File
@@ -73,7 +73,7 @@ mjSpec* mj_copySpec(const mjSpec* s) {
try { try {
modelC = new mjCModel(*static_cast<mjCModel*>(s->element)); modelC = new mjCModel(*static_cast<mjCModel*>(s->element));
} catch (mjCError& e) { } catch (mjCError& e) {
mju_error("Failed to copy spec: %s", e.message); modelC->SetError(e);
return nullptr; return nullptr;
} }
return &modelC->spec; return &modelC->spec;
@@ -121,100 +121,125 @@ mjModel* mj_compile(mjSpec* s, const mjVFS* vfs) {
// attach body to a frame of the parent // attach body to a frame of the parent
mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child, static mjsElement* attachBody(mjCFrame* parent, const mjCBody* child,
const char* prefix, const char* suffix) { 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);
try { try {
*frame_parent += std::string(prefix) + *child_body + std::string(suffix); *parent += std::string(prefix) + *(mjCBody*)child + std::string(suffix);
} catch (mjCError& e) { } catch (mjCError& e) {
frame_parent->model->SetError(e); parent->model->SetError(e);
return nullptr; return nullptr;
} }
mjsBody* attached_body = frame_parent->last_attached; mjsBody* attached_body = parent->last_attached;
frame_parent->last_attached = nullptr; parent->last_attached = nullptr;
return attached_body; return attached_body->element;
} }
// attach frame to a parent body // attach frame to a parent body
mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child, static mjsElement* attachFrame(mjCBody* parent, const mjCFrame* child,
const char* prefix, const char* suffix) { 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);
try { try {
*body_parent += std::string(prefix) + *child_frame + std::string(suffix); *parent += std::string(prefix) + *(mjCFrame*)child + std::string(suffix);
} catch (mjCError& e) { } catch (mjCError& e) {
body_parent->model->SetError(e); parent->model->SetError(e);
return nullptr; return nullptr;
} }
mjsFrame* attached_frame = body_parent->last_attached; mjsFrame* attached_frame = parent->last_attached;
body_parent->last_attached = nullptr; parent->last_attached = nullptr;
return attached_frame; return attached_frame->element;
} }
// attach child body to a parent site // attach child body to a parent site
mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child, static mjsElement* attachToSite(mjCSite* parent, const mjCBody* child,
const char* prefix, const char* suffix) { const char* prefix, const char* suffix) {
if (!parent) { mjSpec* spec = mjs_getSpec(parent->spec.element);
mju_error("parent site is null"); mjCBody* body = parent->Body();
return nullptr; mjCFrame* frame = body->AddFrame(parent->frame);
}
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->SetParent(body);
frame->spec.pos[0] = site->spec.pos[0]; frame->spec.pos[0] = parent->spec.pos[0];
frame->spec.pos[1] = site->spec.pos[1]; frame->spec.pos[1] = parent->spec.pos[1];
frame->spec.pos[2] = site->spec.pos[2]; frame->spec.pos[2] = parent->spec.pos[2];
frame->spec.quat[0] = site->spec.quat[0]; frame->spec.quat[0] = parent->spec.quat[0];
frame->spec.quat[1] = site->spec.quat[1]; frame->spec.quat[1] = parent->spec.quat[1];
frame->spec.quat[2] = site->spec.quat[2]; frame->spec.quat[2] = parent->spec.quat[2];
frame->spec.quat[3] = site->spec.quat[3]; frame->spec.quat[3] = parent->spec.quat[3];
mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree, mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree,
spec->compiler.eulerseq, &site->spec.alt); spec->compiler.eulerseq, &parent->spec.alt);
return mjs_attachBody(&frame->spec, child, prefix, suffix); return attachBody(frame, child, prefix, suffix);
} }
// attach child frame to a parent site // attach child frame to a parent site
mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child, static mjsElement* attachFrameToSite(mjCSite* parent, const mjCFrame* child,
const char* prefix, const char* suffix) { 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) { if (!parent) {
mju_error("parent site is null"); mju_error("parent element is null");
return nullptr; return nullptr;
} }
mjSpec* spec = mjs_getSpec(parent->element); if (!child) {
mjCSite* site = static_cast<mjCSite*>(parent->element); mju_error("child element is null");
mjCBody* body = site->Body(); return nullptr;
mjCFrame* frame = body->AddFrame(site->frame); }
frame->SetParent(body); mjCModel* model = static_cast<mjCModel*>(mjs_getSpec(parent)->element);
frame->spec.pos[0] = site->spec.pos[0]; switch (parent->elemtype) {
frame->spec.pos[1] = site->spec.pos[1]; case mjOBJ_FRAME:
frame->spec.pos[2] = site->spec.pos[2]; if (child->elemtype == mjOBJ_BODY) {
frame->spec.quat[0] = site->spec.quat[0]; return attachBody(static_cast<mjCFrame*>(parent),
frame->spec.quat[1] = site->spec.quat[1]; static_cast<const mjCBody*>(child), prefix, suffix);
frame->spec.quat[2] = site->spec.quat[2]; } else {
frame->spec.quat[3] = site->spec.quat[3]; model->SetError(mjCError(0, "child element is not a body"));
mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree, return nullptr;
spec->compiler.eulerseq, &site->spec.alt); }
case mjOBJ_BODY:
mjsFrame* attached_frame = mjs_attachFrame(&body->spec, child, prefix, suffix); if (child->elemtype == mjOBJ_FRAME) {
mjs_setFrame(attached_frame->element, &frame->spec); return attachFrame(static_cast<mjCBody*>(parent),
return attached_frame; 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 ---------------------------------------------------- //---------------------------------- Attachment ----------------------------------------------------
// Attach child body to a parent frame, return the attached body if success or NULL otherwise. // Attach child to a parent, return the attached element if success or NULL otherwise.
MJAPI mjsBody* mjs_attachBody(mjsFrame* parent, const mjsBody* child, MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix); 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);
// Detach body from mjSpec, remove all references and delete the body, return 0 on success. // Detach body from mjSpec, remove all references and delete the body, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b); 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); UpdateString(suffix, count, i);
// attach to parent // 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)); throw mjXError(elem, mjs_getError(spec));
} }
} }
@@ -3642,7 +3642,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
if (!child) { if (!child) {
throw mjXError(elem, "could not find body '%s''%s'", body_name.c_str()); 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)); throw mjXError(elem, mjs_getError(spec));
} }
} else { } else {
+56 -43
View File
@@ -279,20 +279,23 @@ TEST_F(PluginTest, AttachPlugin) {
EXPECT_THAT(body_1, NotNull()); EXPECT_THAT(body_1, NotNull());
mjsFrame* attachment_frame = mjs_addFrame(body_1, 0); mjsFrame* attachment_frame = mjs_addFrame(body_1, 0);
EXPECT_THAT(attachment_frame, NotNull()); 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); mjModel* model_1 = mj_compile(parent, nullptr);
EXPECT_THAT(model_1, NotNull()); EXPECT_THAT(model_1, NotNull());
EXPECT_THAT(model_1->nbody, 3); EXPECT_THAT(model_1->nbody, 3);
// attach it a second time to test namespacing and compile // attach it a second time to test namespacing and compile
ASSERT_THAT(spec_2, NotNull()) << err.data(); 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); mjModel* model_2 = mj_compile(parent, nullptr);
EXPECT_THAT(model_2, NotNull()); EXPECT_THAT(model_2, NotNull());
EXPECT_THAT(model_2->nbody, 4); EXPECT_THAT(model_2->nbody, 4);
// attach a body not referencing the plugin and compile // 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); mjModel* model_3 = mj_compile(parent, nullptr);
EXPECT_THAT(model_3, NotNull()); EXPECT_THAT(model_3, NotNull());
EXPECT_THAT(model_3->nbody, 5); EXPECT_THAT(model_3->nbody, 5);
@@ -314,9 +317,9 @@ TEST_F(PluginTest, DetachPlugin) {
ASSERT_THAT(child, NotNull()) << err.data(); ASSERT_THAT(child, NotNull()) << err.data();
// attach a body referencing the plugin to the frame // attach a body referencing the plugin to the frame
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), 0); mjsElement* frame = mjs_addFrame(mjs_findBody(parent, "world"), 0)->element;
mjsBody* body = mjs_findBody(child, "body"); mjsElement* body = mjs_findBody(child, "body")->element;
EXPECT_THAT(mjs_attachBody(frame, body, "child-", ""), NotNull()); EXPECT_THAT(mjs_attach(frame, body, "child-", ""), NotNull());
// detach the body and compile // detach the body and compile
mjsBody* body_to_detach = mjs_findBody(parent, "child-body"); 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); mjsFrame* attachment_frame = mjs_addFrame(body_parent, 0);
EXPECT_THAT(attachment_frame, NotNull()); 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); mjModel* model = mj_compile(parent, nullptr);
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
EXPECT_THAT(model->nplugin, 1); EXPECT_THAT(model->nplugin, 1);
@@ -937,7 +941,8 @@ TEST_F(MujocoTest, AttachSame) {
EXPECT_THAT(body, NotNull()); EXPECT_THAT(body, NotNull());
// attach child to parent frame // 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")); EXPECT_THAT(attached, mjs_findBody(parent, "attached-body-1"));
// check that the spec was not copied // check that the spec was not copied
@@ -1073,7 +1078,8 @@ TEST_F(MujocoTest, AttachDifferent) {
EXPECT_THAT(body, NotNull()); EXPECT_THAT(body, NotNull());
// attach child to parent frame // 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")); EXPECT_THAT(attached, mjs_findBody(parent, "attached-body-1"));
// check that the spec was copied // check that the spec was copied
@@ -1212,7 +1218,8 @@ TEST_F(MujocoTest, AttachFrame) {
EXPECT_THAT(frame, NotNull()); EXPECT_THAT(frame, NotNull());
// attach child frame to parent body // 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")); EXPECT_THAT(attached, mjs_findFrame(parent, "attached-pframe-1"));
// check that the spec was copied // check that the spec was copied
@@ -1286,7 +1293,7 @@ TEST_F(MujocoTest, AttachCompiled) {
// attach child body to the frame // attach child body to the frame
mjsBody* to_attach = mjs_findBody(child, "base"); mjsBody* to_attach = mjs_findBody(child, "base");
EXPECT_THAT(to_attach, NotNull()) << mjs_getError(child); 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 // check that attached model can be compiled
mjModel* m_attached = mj_compile(parent, 0); mjModel* m_attached = mj_compile(parent, 0);
@@ -1433,7 +1440,8 @@ TEST_F(MujocoTest, AttachToSite) {
EXPECT_THAT(site, NotNull()); EXPECT_THAT(site, NotNull());
mjsBody* body = mjs_findBody(child, "sphere"); mjsBody* body = mjs_findBody(child, "sphere");
EXPECT_THAT(body, NotNull()); 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()); EXPECT_THAT(attached, NotNull());
mjModel* model = mj_compile(parent, 0); mjModel* model = mj_compile(parent, 0);
@@ -1498,7 +1506,8 @@ TEST_F(MujocoTest, AttachFrameToSite) {
EXPECT_THAT(site, NotNull()); EXPECT_THAT(site, NotNull());
mjsFrame* frame = mjs_findFrame(child, "frame"); mjsFrame* frame = mjs_findFrame(child, "frame");
EXPECT_THAT(frame, NotNull()); 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()); EXPECT_THAT(attached, NotNull());
mjModel* model = mj_compile(parent, 0); mjModel* model = mj_compile(parent, 0);
@@ -1573,7 +1582,8 @@ TEST_F(MujocoTest, BodyToFrame) {
EXPECT_THAT(frame, NotNull()); EXPECT_THAT(frame, NotNull());
mjsBody* body = mjs_findBody(child1, "sphere"); mjsBody* body = mjs_findBody(child1, "sphere");
EXPECT_THAT(body, NotNull()); 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()); EXPECT_THAT(attached, NotNull());
mjModel* model1 = mj_compile(parent, 0); mjModel* model1 = mj_compile(parent, 0);
EXPECT_THAT(model1, NotNull()); 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 // attach the world to the same frame and convert it to a frame
mjsBody* world = mjs_findBody(child2, "world"); mjsBody* world = mjs_findBody(child2, "world");
EXPECT_THAT(world, NotNull()); 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()); EXPECT_THAT(child_world, NotNull());
mjsFrame* frame_world = mjs_bodyToFrame(&child_world); mjsFrame* frame_world = mjs_bodyToFrame(&child_world);
EXPECT_THAT(frame_world, NotNull()); EXPECT_THAT(frame_world, NotNull());
@@ -1662,7 +1673,8 @@ TEST_F(MujocoTest, AttachSpecToSite) {
mjs_setFrame(mjs_firstChild(world, mjOBJ_CAMERA, 0), frame); mjs_setFrame(mjs_firstChild(world, mjOBJ_CAMERA, 0), frame);
// attach the entire spec to the site // 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()); EXPECT_THAT(worldframe, NotNull());
// compile and compare // compile and compare
@@ -1739,7 +1751,8 @@ TEST_F(MujocoTest, AttachSpecToBody) {
mjs_setFrame(mjs_firstChild(world, mjOBJ_CAMERA, 0), frame); mjs_setFrame(mjs_firstChild(world, mjOBJ_CAMERA, 0), frame);
// attach the entire spec to the site // 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()); EXPECT_THAT(worldframe, NotNull());
worldframe->pos[0] = 1; worldframe->pos[0] = 1;
worldframe->pos[1] = 2; worldframe->pos[1] = 2;
@@ -1937,8 +1950,8 @@ TEST_F(MujocoTest, RecompileAttach) {
mjSpec* child2 = mj_parseXMLString(xml, 0, er.data(), er.size()); mjSpec* child2 = mj_parseXMLString(xml, 0, er.data(), er.size());
EXPECT_THAT(child2, NotNull()); EXPECT_THAT(child2, NotNull());
mjsFrame* frame1 = mjs_addFrame(mjs_findBody(parent, "world"), 0); mjsElement* frame1 = mjs_addFrame(mjs_findBody(parent, "world"), 0)->element;
mjs_attachBody(frame1, mjs_findBody(child1, "body"), "child-", "-1"); mjs_attach(frame1, mjs_findBody(child1, "body")->element, "child-", "-1");
mjModel* model = mj_compile(parent, 0); mjModel* model = mj_compile(parent, 0);
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
@@ -1950,8 +1963,8 @@ TEST_F(MujocoTest, RecompileAttach) {
mj_step(model, data); mj_step(model, data);
} }
mjsFrame* frame2 = mjs_addFrame(mjs_findBody(parent, "world"), 0); mjsElement* frame2 = mjs_addFrame(mjs_findBody(parent, "world"), 0)->element;
mjs_attachBody(frame2, mjs_findBody(child2, "body"), "child-", "-2"); mjs_attach(frame2, mjs_findBody(child2, "body")->element, "child-", "-2");
EXPECT_EQ(mj_recompile(parent, 0, model, data), 0); EXPECT_EQ(mj_recompile(parent, 0, model, data), 0);
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
@@ -2003,8 +2016,8 @@ TEST_F(MujocoTest, AttachMocap) {
mjsBody* world = mjs_findBody(spec, "world"); mjsBody* world = mjs_findBody(spec, "world");
EXPECT_THAT(world, NotNull()); EXPECT_THAT(world, NotNull());
mjsFrame* frame = mjs_addFrame(world, NULL); mjsElement* frame = mjs_addFrame(world, NULL)->element;
mjs_attachBody(frame, body, "attached-", "-1"); mjs_attach(frame, body->element, "attached-", "-1");
mjsBody* attached_body = mjs_findBody(spec, "attached-mocap-1"); mjsBody* attached_body = mjs_findBody(spec, "attached-mocap-1");
EXPECT_THAT(attached_body, NotNull()); EXPECT_THAT(attached_body, NotNull());
@@ -2083,7 +2096,7 @@ TEST_F(MujocoTest, AttachUnnamedAssets) {
geom->type = mjGEOM_MESH; geom->type = mjGEOM_MESH;
mjSpec* spec = mj_makeSpec(); 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()); mjModel* model = mj_compile(spec, vfs.get());
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
@@ -2229,8 +2242,8 @@ void AttachNestedKeyframe(bool compile) {
mjs_setDeepCopy(child, true); mjs_setDeepCopy(child, true);
// attach gchild to child // attach gchild to child
mjs_attachBody(mjs_findFrame(child, "frame"), mjs_attach(mjs_findFrame(child, "frame")->element,
mjs_findBody(gchild, "body"), "gchild-", ""); mjs_findBody(gchild, "body")->element, "gchild-", "");
// compile required before further attachment // compile required before further attachment
mjModel* m_child = compile ? mj_compile(child, 0) : nullptr; mjModel* m_child = compile ? mj_compile(child, 0) : nullptr;
@@ -2243,8 +2256,8 @@ void AttachNestedKeyframe(bool compile) {
}; };
// attach child to parent // attach child to parent
mjs_attachBody(mjs_findFrame(parent, "frame"), mjs_attach(mjs_findFrame(parent, "frame")->element,
mjs_findBody(child, "body"), "child-", ""); mjs_findBody(child, "body")->element, "child-", "");
EXPECT_THAT(warning, HasSubstr(compile ? "" : "model has pending keyframes")); EXPECT_THAT(warning, HasSubstr(compile ? "" : "model has pending keyframes"));
@@ -2305,11 +2318,11 @@ TEST_F(MujocoTest, RepeatedAttachKeyframe) {
EXPECT_THAT(child, NotNull()) << er.data(); EXPECT_THAT(child, NotNull()) << er.data();
mjsBody* body_1 = mjs_findBody(parent, "body"); mjsBody* body_1 = mjs_findBody(parent, "body");
mjsFrame* attachment_frame = mjs_addFrame(body_1, 0); mjsElement* attachment_frame = mjs_addFrame(body_1, 0)->element;
mjs_attachBody(attachment_frame, mjs_findBody(child, "b1"), "b1-", ""); mjs_attach(attachment_frame, mjs_findBody(child, "b1")->element, "b1-", "");
mjModel* model_1 = mj_compile(parent, 0); mjModel* model_1 = mj_compile(parent, 0);
EXPECT_THAT(model_1, NotNull()); 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); mjModel* model_2 = mj_compile(parent, 0);
EXPECT_THAT(model_2, NotNull()); EXPECT_THAT(model_2, NotNull());
@@ -2374,8 +2387,8 @@ TEST_F(MujocoTest, ResizeParentKeyframe) {
mjSpec* child = mj_parseXMLString(xml_child, 0, er.data(), er.size()); mjSpec* child = mj_parseXMLString(xml_child, 0, er.data(), er.size());
EXPECT_THAT(child, NotNull()) << er.data(); EXPECT_THAT(child, NotNull()) << er.data();
mjs_attachBody(mjs_findFrame(parent, "frame"), mjs_findBody(child, "body"), mjs_attach(mjs_findFrame(parent, "frame")->element,
"child-", ""); mjs_findBody(child, "body")->element, "child-", "");
mjModel* model = mj_compile(parent, 0); mjModel* model = mj_compile(parent, 0);
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
@@ -2466,12 +2479,10 @@ TEST_F(MujocoTest, DifferentUnitsAllowed) {
mjSpec* child = mj_parseXMLString(child_xml, 0, error.data(), error.size()); mjSpec* child = mj_parseXMLString(child_xml, 0, error.data(), error.size());
mjSpec* spec = mj_parseXMLString(parent_xml, 0, error.data(), error.size()); mjSpec* spec = mj_parseXMLString(parent_xml, 0, error.data(), error.size());
ASSERT_THAT(spec, NotNull()) << error.data(); ASSERT_THAT(spec, NotNull()) << error.data();
mjs_attachBody(mjs_findFrame(child, "frame"), mjs_attach(mjs_findFrame(child, "frame")->element,
mjs_findBody(gchild, "gchild"), mjs_findBody(gchild, "gchild")->element, "gchild_", "");
"gchild_", ""); mjs_attach(mjs_findFrame(spec, "frame")->element,
mjs_attachBody(mjs_findFrame(spec, "frame"), mjs_findBody(child, "child")->element, "child_", "");
mjs_findBody(child, "child"),
"child_", "");
mjModel* model = mj_compile(spec, 0); mjModel* model = mj_compile(spec, 0);
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
@@ -2544,7 +2555,8 @@ TEST_F(MujocoTest, DifferentOptionsInAttachedFrame) {
EXPECT_THAT(world, NotNull()); EXPECT_THAT(world, NotNull());
mjsFrame* child_frame = mjs_findFrame(child, "child"); mjsFrame* child_frame = mjs_findFrame(child, "child");
EXPECT_THAT(child_frame, NotNull()); 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()); EXPECT_THAT(attached_frame, NotNull());
// wrap the child frame in the parent frame and compile // 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()); mjSpec* parent = mj_parseXMLString(xml_p, 0, err.data(), err.size());
EXPECT_THAT(parent, NotNull()) << err.data(); EXPECT_THAT(parent, NotNull()) << err.data();
mjsBody* attached = mjs_attachBody(mjs_findFrame(parent, "parent"), mjsElement* attached =
mjs_findBody(child, "body"), "child-", ""); mjs_attach(mjs_findFrame(parent, "parent")->element,
mjs_findBody(child, "body")->element, "child-", "");
EXPECT_THAT(attached, NotNull()); EXPECT_THAT(attached, NotNull());
mjModel* model = mj_compile(parent, vfs.get()); mjModel* model = mj_compile(parent, vfs.get());
@@ -2774,7 +2787,7 @@ TEST_F(MujocoTest, ErrorWhenCompilingOrphanedSpec) {
EXPECT_THAT(body, NotNull()); EXPECT_THAT(body, NotNull());
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), nullptr); mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), nullptr);
EXPECT_THAT(frame, NotNull()); EXPECT_THAT(frame, NotNull());
mjs_attachBody(frame, body, "child-", ""); mjs_attach(frame->element, body->element, "child-", "");
mj_deleteSpec(parent); mj_deleteSpec(parent);
mjModel* model = mj_compile(child, 0); mjModel* model = mj_compile(child, 0);
EXPECT_THAT(model, IsNull()); EXPECT_THAT(model, IsNull());
+1 -1
View File
@@ -623,7 +623,7 @@ TEST_F(MujocoTest, Modeldir) {
mjSpec* spec = mj_makeSpec(); mjSpec* spec = mj_makeSpec();
mjs_setDeepCopy(spec, true); mjs_setDeepCopy(spec, true);
mjs_setString(spec->meshdir, "asset"); 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()); mjModel* model = mj_compile(spec, vfs.get());
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());