Clean up X macros and usages for mjOption, mjStatistic, and mjVisual.

* Replace various MJOPTION_ macros with a single MJOPTION_FIELDS.
* Use XVEC macro to denote vector members where there is a mixture of
  vector and scalar members in a given struct type.
* Split up MJVISUAL_FIELDS into separate macros for each substruct.
* Modify engine_print.c and Python bindings struct.h/cc to use the
  new X macros.

PiperOrigin-RevId: 869760513
Change-Id: Idac4fe9aa99f0258e7c79802ca9023a0d4486e2d
This commit is contained in:
Saran Tunyasuvunakool
2026-02-13 09:09:45 -08:00
committed by Copybara-Service
parent 6d8b6028e2
commit 5274e5f720
7 changed files with 521 additions and 359 deletions
+51 -118
View File
@@ -103,50 +103,53 @@ PYBIND11_MODULE(_structs, m) {
});
mjOption.def_property_readonly_static("_all_fields", [](py::object) {
std::vector<std::string> fields;
#define X(dtype, name) fields.push_back(#name);
MJOPTION_FLOATS
#undef X
#define X(name, dim0) fields.push_back(#name);
MJOPTION_VECTORS
#undef X
#define X(dtype, name) fields.push_back(#name);
MJOPTION_INTS
#define X(type, var, dim) fields.push_back(#var);
#define XVEC X
MJOPTION_FIELDS
#undef XVEC
#undef X
return py::tuple(py::cast(fields));
});
DefineStructFunctions(mjOption);
#define X(type, var) \
#define X(type, var, dim) \
mjOption.def_property( \
#var, [](const MjOptionWrapper& c) { return c.get()->var; }, \
[](MjOptionWrapper& c, type rhs) { c.get()->var = rhs; });
MJOPTION_SCALARS
#undef X
#define X(var, dim) DefinePyArray(mjOption, #var, &MjOptionWrapper::var);
MJOPTION_VECTORS
#define XVEC(type, var, dim) \
DefinePyArray(mjOption, #var, &MjOptionWrapper::var);
MJOPTION_FIELDS
#undef XVEC
#undef X
mjOption.def_property_readonly_static("_float_fields", [](py::object) {
std::vector<std::string> field_names;
#define X(type, var) field_names.push_back(#var);
MJOPTION_FLOATS
#define X(type, var, dim) \
if constexpr (std::is_floating_point_v<type>) field_names.push_back(#var);
#define XVEC(type, var, dim)
MJOPTION_FIELDS
#undef XVEC
#undef X
return py::tuple(py::cast(field_names));
});
mjOption.def_property_readonly_static("_int_fields", [](py::object) {
std::vector<std::string> field_names;
#define X(type, var) field_names.push_back(#var);
MJOPTION_INTS
#define X(type, var, dim) \
if constexpr (std::is_integral_v<type>) field_names.push_back(#var);
#define XVEC(type, var, dim)
MJOPTION_FIELDS
#undef XVEC
#undef X
return py::tuple(py::cast(field_names));
});
mjOption.def_property_readonly_static("_floatarray_fields", [](py::object) {
std::vector<std::string> field_names;
#define X(var, sz) field_names.push_back(#var);
MJOPTION_VECTORS
#define X(type, var, dim)
#define XVEC(type, var, dim) field_names.push_back(#var);
MJOPTION_FIELDS
#undef XVEC
#undef X
return py::tuple(py::cast(field_names));
});
@@ -192,20 +195,9 @@ PYBIND11_MODULE(_structs, m) {
return raw::MjVisualGlobal(other);
});
DefineStructFunctions(mjVisualGlobal);
#define X(var) mjVisualGlobal.def_readwrite(#var, &raw::MjVisualGlobal::var)
X(cameraid);
X(orthographic);
X(fovy);
X(ipd);
X(azimuth);
X(elevation);
X(linewidth);
X(glow);
X(realtime);
X(offwidth);
X(offheight);
X(ellipsoidinertia);
X(bvactive);
#define X(type, var) \
mjVisualGlobal.def_readwrite(#var, &raw::MjVisualGlobal::var);
MJVISUAL_GLOBAL_FIELDS
#undef X
py::class_<raw::MjVisualQuality> mjVisualQuality(mjVisual, "Quality");
@@ -217,12 +209,8 @@ PYBIND11_MODULE(_structs, m) {
return raw::MjVisualQuality(other);
});
DefineStructFunctions(mjVisualQuality);
#define X(var) mjVisualQuality.def_readwrite(#var, &raw::MjVisualQuality::var)
X(shadowsize);
X(offsamples);
X(numslices);
X(numstacks);
X(numquads);
#define X(var) mjVisualQuality.def_readwrite(#var, &raw::MjVisualQuality::var);
MJVISUAL_QUALITY_FIELDS
#undef X
py::class_<MjVisualHeadlightWrapper> mjVisualHeadlight(mjVisual, "Headlight");
@@ -234,18 +222,17 @@ PYBIND11_MODULE(_structs, m) {
return MjVisualHeadlightWrapper(other);
});
DefineStructFunctions(mjVisualHeadlight);
#define X(var) \
DefinePyArray(mjVisualHeadlight, #var, &MjVisualHeadlightWrapper::var)
X(ambient);
X(diffuse);
X(specular);
#undef X
mjVisualHeadlight.def_property(
"active",
[](const MjVisualHeadlightWrapper& c) { return c.get()->active; },
[](MjVisualHeadlightWrapper& c, int rhs) {
return c.get()->active = rhs;
#define X(type, var, dim) \
mjVisualHeadlight.def_property( \
#var, [](const MjVisualHeadlightWrapper& c) { return c.get()->var; }, \
[](MjVisualHeadlightWrapper& c, type rhs) { \
return c.get()->var = rhs; \
});
#define XVEC(type, var, dim) \
DefinePyArray(mjVisualHeadlight, #var, &MjVisualHeadlightWrapper::var);
MJVISUAL_HEADLIGHT_FIELDS
#undef XVEC
#undef X
py::class_<raw::MjVisualMap> mjVisualMap(mjVisual, "Map");
mjVisualMap.def("__copy__", [](const raw::MjVisualMap& other) {
@@ -255,20 +242,8 @@ PYBIND11_MODULE(_structs, m) {
return raw::MjVisualMap(other);
});
DefineStructFunctions(mjVisualMap);
#define X(var) mjVisualMap.def_readwrite(#var, &raw::MjVisualMap::var)
X(stiffness);
X(stiffnessrot);
X(force);
X(torque);
X(alpha);
X(fogstart);
X(fogend);
X(znear);
X(zfar);
X(haze);
X(shadowclip);
X(shadowscale);
X(actuatortendon);
#define X(var) mjVisualMap.def_readwrite(#var, &raw::MjVisualMap::var);
MJVISUAL_MAP_FIELDS
#undef X
py::class_<raw::MjVisualScale> mjVisualScale(mjVisual, "Scale");
@@ -280,24 +255,8 @@ PYBIND11_MODULE(_structs, m) {
return raw::MjVisualScale(other);
});
DefineStructFunctions(mjVisualScale);
#define X(var) mjVisualScale.def_readwrite(#var, &raw::MjVisualScale::var)
X(forcewidth);
X(contactwidth);
X(contactheight);
X(connect);
X(com);
X(camera);
X(light);
X(selectpoint);
X(jointlength);
X(jointwidth);
X(actuatorlength);
X(actuatorwidth);
X(framelength);
X(framewidth);
X(constraint);
X(slidercrank);
X(frustum);
#define X(var) mjVisualScale.def_readwrite(#var, &raw::MjVisualScale::var);
MJVISUAL_SCALE_FIELDS
#undef X
py::class_<MjVisualRgbaWrapper> mjVisualRgba(mjVisual, "Rgba");
@@ -309,32 +268,8 @@ PYBIND11_MODULE(_structs, m) {
return MjVisualRgbaWrapper(other);
});
DefineStructFunctions(mjVisualRgba);
#define X(var) DefinePyArray(mjVisualRgba, #var, &MjVisualRgbaWrapper::var)
X(fog);
X(haze);
X(force);
X(inertia);
X(joint);
X(actuator);
X(actuatornegative);
X(actuatorpositive);
X(com);
X(camera);
X(light);
X(selectpoint);
X(connect);
X(contactpoint);
X(contactforce);
X(contactfriction);
X(contacttorque);
X(contactgap);
X(rangefinder);
X(constraint);
X(slidercrank);
X(crankbroken);
X(frustum);
X(bv);
X(bvactive);
#define X(var) DefinePyArray(mjVisualRgba, #var, &MjVisualRgbaWrapper::var);
MJVISUAL_RGBA_FIELDS
#undef X
#define X(var) \
@@ -927,20 +862,18 @@ This is useful for example when the MJB is not available as a file on disk.)"));
});
DefineStructFunctions(mjStatistic);
#define X(var) \
#define X(var, dim) \
mjStatistic.def_property( \
#var, [](const MjStatisticWrapper& c) { return c.get()->var; }, \
[](MjStatisticWrapper& c, decltype(raw::MjStatistic::var) rhs) { \
c.get()->var = rhs; \
})
X(meaninertia);
X(meanmass);
X(meansize);
X(extent);
#undef X
});
#define XVEC(var, dim) \
DefinePyArray(mjStatistic, #var, &MjStatisticWrapper::var);
#define X(var) DefinePyArray(mjStatistic, #var, &MjStatisticWrapper::var)
X(center);
MJSTATISTIC_FIELDS
#undef XVEC
#undef X
// ==================== MJLROPT ==============================================
+16 -36
View File
@@ -217,11 +217,13 @@ class MjWrapper<raw::MjOption> : public WrapperBase<raw::MjOption> {
MjWrapper(raw::MjOption* ptr, pybind11::handle owner);
~MjWrapper() = default;
#define X(var, dim) \
#define X(type, var, dim)
#define XVEC(type, var, dim) \
py_array_or_tuple_t< \
std::remove_all_extents_t<decltype(raw::MjOption::var)>> \
var;
MJOPTION_VECTORS
MJOPTION_FIELDS
#undef XVEC
#undef X
};
@@ -241,13 +243,13 @@ class MjWrapper<raw::MjVisualHeadlight>
MjWrapper(raw::MjVisualHeadlight* ptr, pybind11::handle owner);
~MjWrapper() = default;
#define X(var) \
#define X(type, var, dim)
#define XVEC(type, var, dim) \
py_array_or_tuple_t< \
std::remove_all_extents_t<decltype(raw::MjVisualHeadlight::var)>> \
var
X(ambient);
X(diffuse);
X(specular);
var;
MJVISUAL_HEADLIGHT_FIELDS
#undef XVEC
#undef X
};
@@ -268,32 +270,8 @@ class MjWrapper<raw::MjVisualRgba> : public WrapperBase<raw::MjVisualRgba> {
#define X(var) \
py_array_or_tuple_t< \
std::remove_all_extents_t<decltype(raw::MjVisualRgba::var)>> \
var
X(fog);
X(haze);
X(force);
X(inertia);
X(joint);
X(actuator);
X(actuatornegative);
X(actuatorpositive);
X(com);
X(camera);
X(light);
X(selectpoint);
X(connect);
X(contactpoint);
X(contactforce);
X(contactfriction);
X(contacttorque);
X(contactgap);
X(rangefinder);
X(constraint);
X(slidercrank);
X(crankbroken);
X(frustum);
X(bv);
X(bvactive);
var;
MJVISUAL_RGBA_FIELDS
#undef X
};
@@ -330,11 +308,13 @@ class MjWrapper<raw::MjStatistic> : public WrapperBase<raw::MjStatistic> {
MjWrapper(raw::MjStatistic* ptr, pybind11::handle owner);
~MjWrapper() = default;
#define X(var) \
#define X(var, dim)
#define XVEC(var, dim) \
py_array_or_tuple_t< \
std::remove_all_extents_t<decltype(raw::MjStatistic::var)>> \
var
X(center);
var;
MJSTATISTIC_FIELDS
#undef XVEC
#undef X
};
+7 -3
View File
@@ -83,16 +83,20 @@ inline std::size_t NConMax(const mjData* d) {
} // namespace
// ==================== MJOPTION ===============================================
#define X(var, dim) , var(InitPyArray(std::array{dim}, ptr_->var, owner_))
#define X(type, var, dim)
#define XVEC(type, var, dim) \
, var(InitPyArray(std::array{dim}, ptr_->var, owner_))
MjOptionWrapper::MjWrapper()
: WrapperBase([]() {
raw::MjOption* const opt = new raw::MjOption;
mj_defaultOption(opt);
return opt;
}()) MJOPTION_VECTORS {}
}()) MJOPTION_FIELDS {}
MjOptionWrapper::MjWrapper(raw::MjOption* ptr, py::handle owner)
: WrapperBase(ptr, owner) MJOPTION_VECTORS {}
: WrapperBase(ptr, owner) MJOPTION_FIELDS {}
#undef XVEC
#undef X
MjOptionWrapper::MjWrapper(const MjOptionWrapper& other) : MjOptionWrapper() {