Skip to content

Commit 1bf24e9

Browse files
quaglacopybara-github
authored andcommitted
Do not overwrite existing parent frames for worldbody elements when attaching an mjSpec to a frame or a site.
PiperOrigin-RevId: 737662046 Change-Id: I7028c21c803c01845ae1d53cb476f7b3df172313
1 parent da04688 commit 1bf24e9

File tree

9 files changed

+59
-4
lines changed

9 files changed

+59
-4
lines changed

doc/APIreference/functions.rst

+9
Original file line numberDiff line numberDiff line change
@@ -4202,6 +4202,15 @@ Find child body by name.
42024202

42034203
Get parent body.
42044204

4205+
.. _mjs_getFrame:
4206+
4207+
`mjs_getFrame <#mjs_getFrame>`__
4208+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4209+
4210+
.. mujoco-include:: mjs_getFrame
4211+
4212+
Get parent frame.
4213+
42054214
.. _mjs_findFrame:
42064215

42074216
`mjs_findFrame <#mjs_findFrame>`__

doc/changelog.rst

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Bug fixes
1515
^^^^^^^^^
1616
- :ref:`mj_jacDot` was missing a term that accounts for the motion of the point with respect to
1717
which the Jacobian is computed, now fixed.
18+
- Fixed a bug that caused the parent frame of elements in the child worldbody to be incorrectly set when attaching an
19+
mjSpec to a frame or a site.
1820

1921
Version 3.3.0 (Feb 26, 2025)
2022
----------------------------

doc/includes/references.h

+1
Original file line numberDiff line numberDiff line change
@@ -3649,6 +3649,7 @@ mjsBody* mjs_findBody(mjSpec* s, const char* name);
36493649
mjsElement* mjs_findElement(mjSpec* s, mjtObj type, const char* name);
36503650
mjsBody* mjs_findChild(mjsBody* body, const char* name);
36513651
mjsBody* mjs_getParent(mjsElement* element);
3652+
mjsFrame* mjs_getFrame(mjsElement* element);
36523653
mjsFrame* mjs_findFrame(mjSpec* s, const char* name);
36533654
mjsDefault* mjs_getDefault(mjsElement* element);
36543655
const mjsDefault* mjs_findDefault(mjSpec* s, const char* classname);

include/mujoco/mujoco.h

+3
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,9 @@ MJAPI mjsBody* mjs_findChild(mjsBody* body, const char* name);
15561556
// Get parent body.
15571557
MJAPI mjsBody* mjs_getParent(mjsElement* element);
15581558

1559+
// Get parent frame.
1560+
MJAPI mjsFrame* mjs_getFrame(mjsElement* element);
1561+
15591562
// Find frame by name.
15601563
MJAPI mjsFrame* mjs_findFrame(mjSpec* s, const char* name);
15611564

python/mujoco/introspect/functions.py

+16
Original file line numberDiff line numberDiff line change
@@ -9896,6 +9896,22 @@
98969896
),
98979897
doc='Get parent body.',
98989898
)),
9899+
('mjs_getFrame',
9900+
FunctionDecl(
9901+
name='mjs_getFrame',
9902+
return_type=PointerType(
9903+
inner_type=ValueType(name='mjsFrame'),
9904+
),
9905+
parameters=(
9906+
FunctionParameterDecl(
9907+
name='element',
9908+
type=PointerType(
9909+
inner_type=ValueType(name='mjsElement'),
9910+
),
9911+
),
9912+
),
9913+
doc='Get parent frame.',
9914+
)),
98999915
('mjs_findFrame',
99009916
FunctionDecl(
99019917
name='mjs_findFrame',

python/mujoco/specs.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ py::list FindAllImpl(raw::MjsBody& body, mjtObj objtype, bool recursive) {
272272
void SetFrame(raw::MjsBody* body, mjtObj objtype, raw::MjsFrame* frame) {
273273
mjsElement* el = mjs_firstChild(body, objtype, 0);
274274
while (el) {
275-
if (frame->element != el) {
275+
if (frame->element != el && mjs_getFrame(el) == nullptr) {
276276
mjs_setFrame(el, frame);
277277
}
278278
el = mjs_nextChild(body, el, 0);

python/mujoco/specs_test.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,7 @@ def test_attach_to_frame(self):
10721072
child2 = mujoco.MjSpec()
10731073
child2.assets = {'cube2.obj': 'cube2_content'}
10741074
body2 = child2.worldbody.add_body(name='body')
1075+
body2.set_frame(child2.worldbody.add_frame(pos=[-1, -1, 1]))
10751076
self.assertIsNotNone(parent.attach(child2, frame=frame, prefix='child-'))
10761077
self.assertIsNotNone(child2.worldbody)
10771078
self.assertEqual(child2.parent, parent)
@@ -1080,7 +1081,7 @@ def test_attach_to_frame(self):
10801081
self.assertIsNotNone(model2)
10811082
self.assertEqual(model2.nbody, 3)
10821083
np.testing.assert_array_equal(model2.body_pos[1], [0, 1, 4])
1083-
np.testing.assert_array_equal(model2.body_pos[2], [2, 3, 2])
1084+
np.testing.assert_array_equal(model2.body_pos[2], [3, 4, 3])
10841085
np.testing.assert_array_equal(model2.body_quat[1], [0, 0, 0, 1])
10851086
np.testing.assert_array_equal(model2.body_quat[2], [0, 0, 0, 1])
10861087
self.assertEqual(parent.assets['cube.obj'], 'cube_content')
@@ -1090,6 +1091,7 @@ def test_attach_to_frame(self):
10901091
child3 = mujoco.MjSpec()
10911092
child3.assets = {'cube2.obj': 'new_cube2_content'}
10921093
body3 = child3.worldbody.add_body(name='body')
1094+
body3.set_frame(child3.worldbody.add_frame(pos=[-1, -1, 1]))
10931095
self.assertIsNotNone(parent.attach(child3, frame='frame', prefix='child3-'))
10941096
self.assertIsNotNone(child3.worldbody)
10951097
self.assertEqual(child3.parent, parent)
@@ -1098,8 +1100,8 @@ def test_attach_to_frame(self):
10981100
self.assertIsNotNone(model3)
10991101
self.assertEqual(model3.nbody, 4)
11001102
np.testing.assert_array_equal(model3.body_pos[1], [0, 1, 4])
1101-
np.testing.assert_array_equal(model3.body_pos[2], [2, 3, 2])
1102-
np.testing.assert_array_equal(model3.body_pos[3], [3, 4, 1])
1103+
np.testing.assert_array_equal(model3.body_pos[2], [3, 4, 3])
1104+
np.testing.assert_array_equal(model3.body_pos[3], [4, 5, 2])
11031105
np.testing.assert_array_equal(model3.body_quat[1], [0, 0, 0, 1])
11041106
np.testing.assert_array_equal(model3.body_quat[2], [0, 0, 0, 1])
11051107
np.testing.assert_array_equal(model3.body_quat[3], [0, 0, 0, 1])

src/user/user_api.cc

+19
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,25 @@ mjsBody* mjs_getParent(mjsElement* element) {
711711

712712

713713

714+
// get parent frame
715+
mjsFrame* mjs_getFrame(mjsElement* element) {
716+
mjCBase* base = static_cast<mjCBase*>(element);
717+
switch (element->elemtype) {
718+
case mjOBJ_BODY:
719+
case mjOBJ_FRAME:
720+
case mjOBJ_JOINT:
721+
case mjOBJ_GEOM:
722+
case mjOBJ_SITE:
723+
case mjOBJ_CAMERA:
724+
case mjOBJ_LIGHT:
725+
return base->frame ? &(base->frame->spec) : nullptr;
726+
default:
727+
return nullptr;
728+
}
729+
}
730+
731+
732+
714733
// find frame by name
715734
mjsFrame* mjs_findFrame(mjSpec* s, const char* name) {
716735
mjsElement* frame = mjs_findElement(s, mjOBJ_FRAME, name);

src/user/user_api.h

+3
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ MJAPI mjsBody* mjs_findChild(mjsBody* body, const char* name);
211211
// Get parent body.
212212
MJAPI mjsBody* mjs_getParent(mjsElement* element);
213213

214+
// Get parent frame.
215+
MJAPI mjsFrame* mjs_getFrame(mjsElement* element);
216+
214217
// Find frame by name.
215218
MJAPI mjsFrame* mjs_findFrame(mjSpec* s, const char* name);
216219

0 commit comments

Comments
 (0)