Add mjs_makeFlex to the MuJoCo C and Python APIs.
- Add mjs_makeFlex C API that wraps mjCFlexcomp::Make(), creating a flex with auto-generated bodies, joints, and equality constraints from parameters (type, dim, dof, count, cellcount, spacing, scale, radius, mass, inertiabox, equality, rigid, flatskin, elastic2d, pos, quat, origin, file, vfs). - Add Python binding body.make_flex() in specs.cc with keyword args. - Refactor flex_spec_provision.py to use make_flex() instead of XML string templating + separate spec + attach. Eliminates textwrap dependency. - Merge user_flexcomp into user_model BUILD target to resolve circular dependency. PiperOrigin-RevId: 922725304 Change-Id: Ice13787c45f3173407fec659f9590a6453bc65dd
This commit is contained in:
committed by
Copybara-Service
parent
b9fb817af8
commit
4c381635e1
@@ -33,6 +33,7 @@
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "engine/engine_support.h"
|
||||
#include "user/user_cache.h"
|
||||
#include "user/user_flexcomp.h"
|
||||
#include "user/user_model.h"
|
||||
#include "user/user_objects.h"
|
||||
#include "user/user_resource.h"
|
||||
@@ -599,6 +600,121 @@ mjsFlex* mjs_addFlex(mjSpec* s) {
|
||||
|
||||
|
||||
|
||||
// helper: convert type string to mjtFcompType
|
||||
static mjtFcompType FlexcompTypeFromStr(const char* type) {
|
||||
if (!type || !strcmp(type, "grid")) return mjFCOMPTYPE_GRID;
|
||||
if (!strcmp(type, "box")) return mjFCOMPTYPE_BOX;
|
||||
if (!strcmp(type, "cylinder")) return mjFCOMPTYPE_CYLINDER;
|
||||
if (!strcmp(type, "ellipsoid")) return mjFCOMPTYPE_ELLIPSOID;
|
||||
if (!strcmp(type, "square")) return mjFCOMPTYPE_SQUARE;
|
||||
if (!strcmp(type, "disc")) return mjFCOMPTYPE_DISC;
|
||||
if (!strcmp(type, "circle")) return mjFCOMPTYPE_CIRCLE;
|
||||
if (!strcmp(type, "mesh")) return mjFCOMPTYPE_MESH;
|
||||
if (!strcmp(type, "gmsh")) return mjFCOMPTYPE_GMSH;
|
||||
if (!strcmp(type, "direct")) return mjFCOMPTYPE_DIRECT;
|
||||
return mjFCOMPTYPE_GRID; // default
|
||||
}
|
||||
|
||||
// helper: convert dof string to mjtDof
|
||||
static mjtDof FlexcompDofFromStr(const char* dof) {
|
||||
if (!dof || !strcmp(dof, "full")) return mjFCOMPDOF_FULL;
|
||||
if (!strcmp(dof, "radial")) return mjFCOMPDOF_RADIAL;
|
||||
if (!strcmp(dof, "trilinear")) return mjFCOMPDOF_TRILINEAR;
|
||||
if (!strcmp(dof, "quadratic")) return mjFCOMPDOF_QUADRATIC;
|
||||
if (!strcmp(dof, "2d")) return mjFCOMPDOF_2D;
|
||||
return mjFCOMPDOF_FULL; // default
|
||||
}
|
||||
|
||||
|
||||
// add flexcomp: create flex with auto-generated bodies/joints
|
||||
mjsFlex* mjs_makeFlex(mjsBody* body, const char* name, const char* type, int dim,
|
||||
const char* dof, const int count[3], const int cellcount[3],
|
||||
const double spacing[3], const double scale[3], double radius,
|
||||
double mass, double inertiabox, int equality, int rigid, int flatskin,
|
||||
int elastic2d, const double pos[3], const double quat[4],
|
||||
const double origin[3], const char* file, const mjVFS* vfs) {
|
||||
if (!body || !name) {
|
||||
mju_error("mjs_makeFlex: body and name must not be null");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mjCModel* model = static_cast<mjCBody*>(body->element)->model;
|
||||
|
||||
// create temporary flexcomp with defaults
|
||||
mjCFlexcomp fcomp;
|
||||
fcomp.name = name;
|
||||
fcomp.type = FlexcompTypeFromStr(type);
|
||||
fcomp.doftype = FlexcompDofFromStr(dof);
|
||||
|
||||
// topology
|
||||
if (count) {
|
||||
fcomp.count[0] = count[0];
|
||||
fcomp.count[1] = count[1];
|
||||
fcomp.count[2] = count[2];
|
||||
}
|
||||
if (cellcount) {
|
||||
fcomp.cellcount[0] = cellcount[0];
|
||||
fcomp.cellcount[1] = cellcount[1];
|
||||
fcomp.cellcount[2] = cellcount[2];
|
||||
}
|
||||
if (spacing) {
|
||||
fcomp.spacing[0] = spacing[0];
|
||||
fcomp.spacing[1] = spacing[1];
|
||||
fcomp.spacing[2] = spacing[2];
|
||||
}
|
||||
if (scale) {
|
||||
fcomp.scale[0] = scale[0];
|
||||
fcomp.scale[1] = scale[1];
|
||||
fcomp.scale[2] = scale[2];
|
||||
}
|
||||
if (origin) {
|
||||
fcomp.origin[0] = origin[0];
|
||||
fcomp.origin[1] = origin[1];
|
||||
fcomp.origin[2] = origin[2];
|
||||
}
|
||||
|
||||
// physics
|
||||
fcomp.def.spec.flex->dim = dim;
|
||||
fcomp.def.spec.flex->radius = radius;
|
||||
if (mass > 0) fcomp.mass = mass;
|
||||
if (inertiabox > 0) fcomp.inertiabox = inertiabox;
|
||||
fcomp.equality = equality;
|
||||
fcomp.rigid = rigid;
|
||||
fcomp.def.spec.flex->flatskin = flatskin;
|
||||
fcomp.def.spec.flex->elastic2d = elastic2d;
|
||||
|
||||
// pose
|
||||
if (pos) {
|
||||
fcomp.pos[0] = pos[0];
|
||||
fcomp.pos[1] = pos[1];
|
||||
fcomp.pos[2] = pos[2];
|
||||
}
|
||||
if (quat) {
|
||||
fcomp.quat[0] = quat[0];
|
||||
fcomp.quat[1] = quat[1];
|
||||
fcomp.quat[2] = quat[2];
|
||||
fcomp.quat[3] = quat[3];
|
||||
}
|
||||
|
||||
// file
|
||||
if (file) {
|
||||
fcomp.file = file;
|
||||
}
|
||||
|
||||
// call Make
|
||||
char error[500] = "";
|
||||
if (!fcomp.Make(body, error, sizeof(error), vfs)) {
|
||||
model->SetError(mjCError(nullptr, "%s", error));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// return the flex that was created (last flex in model)
|
||||
mjCFlex* flex = model->Flexes().back();
|
||||
return &flex->spec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// add frame to body
|
||||
mjsFrame* mjs_addFrame(mjsBody* bodyspec, mjsFrame* parentframe) {
|
||||
mjCFrame* parentframeC = 0;
|
||||
|
||||
Reference in New Issue
Block a user