Add mjs_getOriginSpec to retrieve the original spec of an element.
The new function mjs_getOriginSpec returns the mjSpec that was used to define a given mjsElement. Unlike mjs_getSpec, this value remains constant even after the element has been attached to a different model. PiperOrigin-RevId: 911930951 Change-Id: Ia9cd79d9dfabc513d6121eadcffc5d41075224c9
This commit is contained in:
committed by
Copybara-Service
parent
8b06fe4c16
commit
ec50260e26
@@ -4783,6 +4783,16 @@ Find and get utilities
|
||||
|
||||
Get spec from body.
|
||||
|
||||
.. _mjs_getOriginSpec:
|
||||
|
||||
`mjs_getOriginSpec <#mjs_getOriginSpec>`__
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. mujoco-include:: mjs_getOriginSpec
|
||||
|
||||
get spec that originally defined an element
|
||||
contrary to mjs_getSpec, this does not change after attachment
|
||||
|
||||
.. _mjs_getCompiler:
|
||||
|
||||
`mjs_getCompiler <#mjs_getCompiler>`__
|
||||
|
||||
@@ -19,6 +19,9 @@ General
|
||||
energy gain in the presence of contacts and in fluid media.
|
||||
- Added :ref:`mju_sym2dense`, converting a lower-triangular, implicitly symmetric CSR matrix to a dense
|
||||
symmetric matrix. The inertia matrix ``mjData.M`` is an example of such a matrix.
|
||||
- Added :ref:`mjs_getOriginSpec`, returning the spec that originally defined an element, prior to attachment. This is in
|
||||
contrast to :ref:`mjs_getSpec` which returns the spec currently owning the element. If the element is not the result
|
||||
of an attach operation, the functions are identical.
|
||||
|
||||
.. admonition:: Future breaking API changes
|
||||
:class: warning
|
||||
|
||||
@@ -3687,6 +3687,7 @@ mjsTexture* mjs_addTexture(mjSpec* s);
|
||||
mjsMaterial* mjs_addMaterial(mjSpec* s, const mjsDefault* def);
|
||||
int mjs_makeMesh(mjsMesh* mesh, mjtMeshBuiltin builtin, double* params, int nparams);
|
||||
mjSpec* mjs_getSpec(const mjsElement* element);
|
||||
mjSpec* mjs_getOriginSpec(const mjsElement* element);
|
||||
mjsCompiler* mjs_getCompiler(const mjsElement* element);
|
||||
mjSpec* mjs_findSpec(const mjSpec* spec, const char* name);
|
||||
mjsBody* mjs_findBody(const mjSpec* s, const char* name);
|
||||
|
||||
@@ -1776,6 +1776,10 @@ MJAPI int mjs_makeMesh(mjsMesh* mesh, mjtMeshBuiltin builtin, double* params, in
|
||||
// Get spec from body.
|
||||
MJAPI mjSpec* mjs_getSpec(const mjsElement* element);
|
||||
|
||||
// get spec that originally defined an element
|
||||
// contrary to mjs_getSpec, this does not change after attachment
|
||||
MJAPI mjSpec* mjs_getOriginSpec(const mjsElement* element);
|
||||
|
||||
// Get compiler associated with element's origin spec.
|
||||
MJAPI mjsCompiler* mjs_getCompiler(const mjsElement* element);
|
||||
|
||||
|
||||
@@ -11126,6 +11126,22 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
||||
),
|
||||
doc='Get spec from body.',
|
||||
)),
|
||||
('mjs_getOriginSpec',
|
||||
FunctionDecl(
|
||||
name='mjs_getOriginSpec',
|
||||
return_type=PointerType(
|
||||
inner_type=ValueType(name='mjSpec'),
|
||||
),
|
||||
parameters=(
|
||||
FunctionParameterDecl(
|
||||
name='element',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjsElement', is_const=True),
|
||||
),
|
||||
),
|
||||
),
|
||||
doc='get spec that originally defined an element contrary to mjs_getSpec, this does not change after attachment', # pylint: disable=line-too-long
|
||||
)),
|
||||
('mjs_getCompiler',
|
||||
FunctionDecl(
|
||||
name='mjs_getCompiler',
|
||||
|
||||
@@ -1308,6 +1308,16 @@ mjSpec* mjs_getSpec(const mjsElement* element) {
|
||||
|
||||
|
||||
|
||||
// get spec that originally defined an element
|
||||
// contrary to mjs_getSpec, this does not change after attachment
|
||||
mjSpec* mjs_getOriginSpec(const mjsElement* element) {
|
||||
const mjCModel* model = static_cast<const mjCBase*>(element)->model;
|
||||
const mjsCompiler* compiler = static_cast<const mjCBase*>(element)->compiler;
|
||||
return model->FindSpec(compiler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
mjsCompiler* mjs_getCompiler(const mjsElement* element) {
|
||||
return static_cast<const mjCBase*>(element)->compiler;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,10 @@ MJAPI int mjs_makeMesh(mjsMesh* mesh, mjtMeshBuiltin builtin, double* params, in
|
||||
// Get spec from body.
|
||||
MJAPI mjSpec* mjs_getSpec(const mjsElement* element);
|
||||
|
||||
// get spec that originally defined an element
|
||||
// contrary to mjs_getSpec, this does not change after attachment
|
||||
MJAPI mjSpec* mjs_getOriginSpec(const mjsElement* element);
|
||||
|
||||
// Find spec (model asset) by name.
|
||||
MJAPI mjSpec* mjs_findSpec(const mjSpec* spec, const char* name);
|
||||
|
||||
|
||||
@@ -205,6 +205,39 @@ TEST_F(MujocoTest, AttachAndChildDeletion) {
|
||||
mj_deleteSpec(parent_spec);
|
||||
}
|
||||
|
||||
TEST_F(MujocoTest, OriginSpecInvariantToAttachment) {
|
||||
mjSpec* child_spec = mj_makeSpec();
|
||||
mjsBody* child_world = mjs_findBody(child_spec, "world");
|
||||
mjsBody* child_body = mjs_addBody(child_world, 0);
|
||||
mjsJoint* freejoint = mjs_addJoint(child_body, 0);
|
||||
freejoint->type = mjJNT_FREE;
|
||||
mjs_setName(freejoint->element, "child_freejoint");
|
||||
|
||||
mjSpec* parent_spec = mj_makeSpec();
|
||||
mjsBody* parent_world = mjs_findBody(parent_spec, "world");
|
||||
mjsBody* parent_body = mjs_addBody(parent_world, 0);
|
||||
mjs_setName(parent_body->element, "parent_body");
|
||||
|
||||
// Attach child spec to parent_body
|
||||
mjsElement* attached =
|
||||
mjs_attach(parent_body->element, child_spec->element, "pre_", "");
|
||||
ASSERT_THAT(attached, NotNull());
|
||||
|
||||
// The freejoint should still be in parent_spec because deletion failed
|
||||
mjsElement* child_spec_joint =
|
||||
mjs_findElement(parent_spec, mjOBJ_JOINT, "pre_child_freejoint");
|
||||
EXPECT_EQ(mjs_getSpec(child_spec_joint), parent_spec);
|
||||
EXPECT_EQ(mjs_getOriginSpec(child_spec_joint), child_spec);
|
||||
|
||||
mjsElement* parent_spec_body =
|
||||
mjs_findElement(parent_spec, mjOBJ_BODY, "parent_body");
|
||||
EXPECT_EQ(mjs_getOriginSpec(parent_spec_body), parent_spec);
|
||||
|
||||
|
||||
mj_deleteSpec(child_spec);
|
||||
mj_deleteSpec(parent_spec);
|
||||
}
|
||||
|
||||
int open_mock(mjResource* resource) {
|
||||
static const char parent_xml[] = R"(
|
||||
<mujoco>
|
||||
|
||||
@@ -9793,6 +9793,14 @@ std::string mjs_getName_wrapper(MjsElement& element) {
|
||||
return *mjs_getName(element.get());
|
||||
}
|
||||
|
||||
std::optional<MjSpec> mjs_getOriginSpec_wrapper(const MjsElement& element) {
|
||||
mjSpec* result = mjs_getOriginSpec(element.get());
|
||||
if (result == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return MjSpec(result);
|
||||
}
|
||||
|
||||
std::optional<MjsBody> mjs_getParent_wrapper(const MjsElement& element) {
|
||||
mjsBody* result = mjs_getParent(element.get());
|
||||
if (result == nullptr) {
|
||||
@@ -13343,6 +13351,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
function("mjs_getFrame", &mjs_getFrame_wrapper);
|
||||
function("mjs_getId", &mjs_getId_wrapper);
|
||||
function("mjs_getName", &mjs_getName_wrapper);
|
||||
function("mjs_getOriginSpec", &mjs_getOriginSpec_wrapper);
|
||||
function("mjs_getParent", &mjs_getParent_wrapper);
|
||||
function("mjs_getSpec", &mjs_getSpec_wrapper);
|
||||
function("mjs_getSpecDefault", &mjs_getSpecDefault_wrapper);
|
||||
|
||||
Reference in New Issue
Block a user