diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 16ce21a0..0ce73814 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -2981,20 +2981,6 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui define the tendons). The "main" tendons are parallel to the axes of the grid. In addition one can create diagonal "shear" tendons, using the :el:`tendon` sub-element. This type is suitable for simulating strings as well as cloth. - The **rope** type creates a 1D grid of bodies, each having a geom with user-defined type (sphere, capsule or - ellipsoid) and 2 hinge joints with axes orthogonal to the grid, creating a universal joint with the previous body. - This corresponds to a kinematic chain which can bend but cannot stretch or twist. In addition, one can specify - stretch and twist joints (slide and hinge respectively) with the :el:`joint` sub-element. When specified, these extra - joints are equality-constrained, but the constraint is soft by default so that some stretch and twist are possible. - The rope can extend in one or both directions from the parent body. To specify the origin of the rope, the parent - body *must* be named so that it fits the automatic naming convention. For example, to make the parent be the first - body in the chain, and assuming we have prefix="C", the parent body should be named "CB0". When the parent is not at - the end, the rope consists of two kinematic chains starting at the parent and extending in opposite directions. - - The **loop** type is the same as the rope type except the elements are arranged in a circle, and the first and last - elements are equality-constrained to remain connected (using the "connect" constraint type). The softness of this - equality constraint is adjusted with the attributes solrefsmooth and solimpsmooth. - The **cable** type creates a 1D chain of bodies connected with ball joints, each having a geom with user-defined type (cylinder, capsule or box). The geometry can either be defined with an array of 3D vertex coordinates :at:`vertex` or with prescribed functions with the option :at:`curve`. Currently, only linear and trigonometric functions are @@ -3489,7 +3475,7 @@ saving the XML: .. _body-flexcomp-type: -:at:`type`: :at-val:`[grid, box, cylinder, ellipsoid, mesh, gmsh, direct], "grid"` +:at:`type`: :at-val:`[grid, box, cylinder, ellipsoid, disc, circle, mesh, gmsh, direct], "grid"` This attribute determines the type of :el:`flexcomp` object. The remaining attributes and sub-elements are then interpreted according to the type. Default settings are also adjusted depending on the type. Different types correspond to different methods for specifying the flexcomp points and the stretchable elements that connect them. @@ -3513,6 +3499,13 @@ saving the XML: **ellipsoid** is the same as **box**, except the points are projected on the surface of an ellipsoid. + **disc** is the same as **box**, except the points are projected on the surface of a disc. It is only compatible + with :at:`dim=2`. + + **circle** is the same as **grid**, except the points are sampled along a circle so that the first and last points + are the same. The radius of the circle is computed such that each segment has the requested spacing. It is only + compatible with :at:`dim=1`. + **mesh** loads the flexcomp points and elements (i.e. triangles) from a mesh file, in the same file formats as mesh assets. A mesh asset is not actually added to the model. Instead the vertex and face data from the mesh file are used to populate the point and element data of the flexcomp. :at:`dim` is automatically set to 2. Recall that a mesh asset diff --git a/doc/changelog.rst b/doc/changelog.rst index f148d26b..28819ba9 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -6,6 +6,11 @@ Changelog Upcoming version (not yet released) ----------------------------------- +General +^^^^^^^ +- Removed rope and loop from :ref:`composite`. The user is encouraged to instead use the :at:`cable` + plugin or :ref:`flexcomp`, respectively. + MJX ^^^ - Added muscle actuators. diff --git a/model/flex/pulley.xml b/model/flex/pulley.xml new file mode 100644 index 00000000..5e07430d --- /dev/null +++ b/model/flex/pulley.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/engine/engine_vis_init.c b/src/engine/engine_vis_init.c index 3e0d46a2..04d2079e 100644 --- a/src/engine/engine_vis_init.c +++ b/src/engine/engine_vis_init.c @@ -93,7 +93,7 @@ const char* mjVISSTRING[mjNVISFLAG][3] = { {"Static Body", "1", "D"}, {"Skin", "1", ";"}, {"Flex Vert", "0", ""}, - {"Flex Edge", "0", ""}, + {"Flex Edge", "1", ""}, {"Flex Face", "0", ""}, {"Flex Skin", "1", ""}, {"Body Tree", "0", "`"}, diff --git a/src/user/user_composite.cc b/src/user/user_composite.cc index 73c1227d..ab34b7b8 100644 --- a/src/user/user_composite.cc +++ b/src/user/user_composite.cc @@ -309,15 +309,15 @@ bool mjCComposite::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) case mjCOMPTYPE_ROPE: return comperr(error, - "The \"rope\" composite type is deprecated. Please use " - "\"cable\" instead.", - error_sz); + "The \"rope\" composite type is deprecated. Please use " + "\"cable\" instead.", + error_sz); case mjCOMPTYPE_LOOP: - mju_warning( - "The \"loop\" composite type is deprecated. Please use \"cable\" " - "instead."); - return MakeRope(model, body, error, error_sz); + return comperr(error, + "The \"loop\" composite type is deprecated. Please use " + "\"flexcomp\" instead.", + error_sz); case mjCOMPTYPE_CABLE: return MakeCable(model, body, error, error_sz); @@ -729,171 +729,6 @@ mjsBody* mjCComposite::AddCableBody(mjCModel* model, mjsBody* body, int ix, } -// make rope -bool mjCComposite::MakeRope(mjCModel* model, mjsBody* body, char* error, int error_sz) { - // check dim - if (dim!=1) { - return comperr(error, "Rope must be one-dimensional", error_sz); - } - - // check root body name prefix - char txt[200]; - mju::sprintf_arr(txt, "%sB", prefix.c_str()); - std::string body_name = mjs_getString(body->name); - if (std::strncmp(txt, body_name.substr(0, strlen(txt)).c_str(), mju::sizeof_arr(txt))) { - mju::strcat_arr(txt, " must be the beginning of root body name"); - return comperr(error, txt, error_sz); - } - - // read origin coordinate from root body - mju::strcpy_arr(txt, body_name.substr(strlen(txt)).c_str()); - int ox = -1; - if (sscanf(txt, "%d", &ox)!=1) { - return comperr(error, "Root body name must contain X coordinate", error_sz); - } - if (ox<0 || ox>=count[0]) { - return comperr(error, "Root body coordinate out of range", error_sz); - } - - // add origin - AddRopeBody(model, body, ox, ox); - - // add elements: right - mjsBody* pbody = body; - for (int ix=ox; ix0; ix--) { - pbody = AddRopeBody(model, pbody, ix, ix-1); - } - - // close loop - if (type==mjCOMPTYPE_LOOP) { - char txt2[200]; - - // add equality constraint - mjsEquality* eq = mjs_addEquality(&model->spec, 0); - eq->type = mjEQ_CONNECT; - mju::sprintf_arr(txt, "%sB0", prefix.c_str()); - mju::sprintf_arr(txt2, "%sB%d", prefix.c_str(), count[0]-1); - mjs_setString(eq->name1, txt); - mjs_setString(eq->name2, txt2); - mjuu_setvec(eq->data, -0.5*spacing, 0, 0); - mju_copy(eq->solref, solrefsmooth, mjNREF); - mju_copy(eq->solimp, solimpsmooth, mjNIMP); - - // remove contact between connected bodies - mjsExclude* pair = mjs_addExclude(&model->spec); - mjs_setString(pair->bodyname1, std::string(txt).c_str()); - mjs_setString(pair->bodyname2, std::string(txt2).c_str()); - } - - return true; -} - - - -// add child body for cloth -mjsBody* mjCComposite::AddRopeBody(mjCModel* model, mjsBody* body, int ix, int ix1) { - char txt[100]; - bool isroot = (ix==ix1); - double dx = spacing*(ix1-ix); - - // add child if not root - if (!isroot) { - body = mjs_addBody(body, 0); - mju::sprintf_arr(txt, "%sB%d", prefix.c_str(), ix1); - mjs_setString(body->name, txt); - - // loop - if (type==mjCOMPTYPE_LOOP) { - double alpha = 2*mjPI/count[0]; - double R = 0.5*spacing*sin(mjPI-alpha)/sin(0.5*alpha); - - if (ix1>ix) { - mjuu_setvec(body->pos, R*cos(0.5*alpha), R*sin(0.5*alpha), 0); - mjuu_setvec(body->quat, cos(0.5*alpha), 0, 0, sin(0.5*alpha)); - } else { - mjuu_setvec(body->pos, -R*cos(0.5*alpha), R*sin(0.5*alpha), 0); - mjuu_setvec(body->quat, cos(-0.5*alpha), 0, 0, sin(-0.5*alpha)); - } - } - - // no loop - else { - mjuu_setvec(body->pos, dx, 0, 0); - } - } - - // add geom - mjsGeom* geom = mjs_addGeom(body, &def[0].spec); - mjs_setDefault(geom->element, mjs_getDefault(body->element)); - mju::sprintf_arr(txt, "%sG%d", prefix.c_str(), ix1); - mjs_setString(geom->name, txt); - mjuu_setvec(geom->pos, 0, 0, 0); - mjuu_setvec(geom->quat, sqrt(0.5), 0, sqrt(0.5), 0); - - // root: no joints - if (isroot) { - return body; - } - - // add main joint - for (int i=0; i<2; i++) { - // add joint - mjsJoint* jnt = mjs_addJoint(body, &defjoint[mjCOMPKIND_JOINT][0].spec); - mjs_setDefault(jnt->element, mjs_getDefault(body->element)); - mju::sprintf_arr(txt, "%sJ%d_%d", prefix.c_str(), i, ix1); - mjs_setString(jnt->name, txt); - jnt->type = mjJNT_HINGE; - mjuu_setvec(jnt->pos, -0.5*dx, 0, 0); - mjuu_setvec(jnt->axis, 0, 0, 0); - jnt->axis[i+1] = 1; - } - - // add twist joint - if (add[mjCOMPKIND_TWIST]) { - // add joint - mjsJoint* jnt = mjs_addJoint(body, &defjoint[mjCOMPKIND_TWIST][0].spec); - mjs_setDefault(jnt->element, mjs_getDefault(body->element)); - mju::sprintf_arr(txt, "%sJT%d", prefix.c_str(), ix1); - mjs_setString(jnt->name, txt); - jnt->type = mjJNT_HINGE; - mjuu_setvec(jnt->pos, -0.5*dx, 0, 0); - mjuu_setvec(jnt->axis, 1, 0, 0); - - // add constraint - mjsEquality* eq = mjs_addEquality(&model->spec, &def[mjCOMPKIND_TWIST].spec); - mjs_setDefault(eq->element, &model->Default()->spec); - eq->type = mjEQ_JOINT; - mjs_setString(eq->name1, mjs_getString(jnt->name)); - } - - // add stretch joint - if (add[mjCOMPKIND_STRETCH]) { - // add joint - mjsJoint* jnt = mjs_addJoint(body, &defjoint[mjCOMPKIND_STRETCH][0].spec); - mjs_setDefault(jnt->element, mjs_getDefault(body->element)); - mju::sprintf_arr(txt, "%sJS%d", prefix.c_str(), ix1); - mjs_setString(jnt->name, txt); - jnt->type = mjJNT_SLIDE; - mjuu_setvec(jnt->pos, -0.5*dx, 0, 0); - mjuu_setvec(jnt->axis, 1, 0, 0); - - // add constraint - mjsEquality* eq = mjs_addEquality(&model->spec, &def[mjCOMPKIND_STRETCH].spec); - mjs_setDefault(eq->element, &model->Default()->spec); - eq->type = mjEQ_JOINT; - mjs_setString(eq->name1, mjs_getString(jnt->name)); - } - - return body; -} - - // add shear tendons to 2D void mjCComposite::MakeShear(mjCModel* model) { diff --git a/src/user/user_composite.h b/src/user/user_composite.h index add71636..9e42dba3 100644 --- a/src/user/user_composite.h +++ b/src/user/user_composite.h @@ -70,7 +70,6 @@ class mjCComposite { bool MakeParticle(mjCModel* model, mjsBody* body, char* error, int error_sz); bool MakeGrid(mjCModel* model, mjsBody* body, char* error, int error_sz); - bool MakeRope(mjCModel* model, mjsBody* body, char* error, int error_sz); bool MakeCable(mjCModel* model, mjsBody* body, char* error, int error_sz); void MakeShear(mjCModel* model); @@ -124,7 +123,6 @@ class mjCComposite { int dim; // dimensionality private: - mjsBody* AddRopeBody(mjCModel* model, mjsBody* body, int ix, int ix1); mjsBody* AddCableBody(mjCModel* model, mjsBody* body, int ix, double normal[3], double prev_quat[4]); // temporary skin vectors diff --git a/src/user/user_flexcomp.cc b/src/user/user_flexcomp.cc index 67443e22..58a50bcb 100644 --- a/src/user/user_flexcomp.cc +++ b/src/user/user_flexcomp.cc @@ -157,6 +157,7 @@ bool mjCFlexcomp::Make(mjsBody* body, char* error, int error_sz) { bool res; switch (type) { case mjFCOMPTYPE_GRID: + case mjFCOMPTYPE_CIRCLE: res = MakeGrid(error, error_sz); break; @@ -548,15 +549,30 @@ bool mjCFlexcomp::MakeGrid(char* error, int error_sz) { // 1D if (dim == 1) { for (int ix=0; ix < count[0]; ix++) { - // add point - point.push_back(spacing[0]*(ix - 0.5*(count[0]-1))); - point.push_back(0); - point.push_back(0); + if (type == mjFCOMPTYPE_CIRCLE) { + // add point + double theta = 2*mjPI/(count[0]-1); + double radius = spacing[0]/std::sin(theta/2)/2; + point.push_back(radius*std::cos(theta*ix)); + point.push_back(radius*std::sin(theta*ix)); + point.push_back(0); - // add element - if (ix < count[0]-1) { - element.push_back(ix); - element.push_back(ix+1); + // add element + if (ix < count[0]-1) { + element.push_back(ix); + element.push_back(ix == count[0]-2 ? 0 : ix+1); + } + } else { + // add point + point.push_back(spacing[0]*(ix - 0.5*(count[0]-1))); + point.push_back(0); + point.push_back(0); + + // add element + if (ix < count[0]-1) { + element.push_back(ix); + element.push_back(ix+1); + } } } } diff --git a/src/user/user_flexcomp.h b/src/user/user_flexcomp.h index 92ce884c..6621c650 100644 --- a/src/user/user_flexcomp.h +++ b/src/user/user_flexcomp.h @@ -31,6 +31,7 @@ typedef enum _mjtFcompType { mjFCOMPTYPE_ELLIPSOID, mjFCOMPTYPE_SQUARE, mjFCOMPTYPE_DISC, + mjFCOMPTYPE_CIRCLE, mjFCOMPTYPE_MESH, mjFCOMPTYPE_GMSH, mjFCOMPTYPE_DIRECT, diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 9a8ad20d..e25626d2 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -794,6 +794,7 @@ const mjMap fcomp_map[mjNFCOMPTYPES] = { {"ellipsoid", mjFCOMPTYPE_ELLIPSOID}, {"square", mjFCOMPTYPE_SQUARE}, {"disc", mjFCOMPTYPE_DISC}, + {"circle", mjFCOMPTYPE_CIRCLE}, {"mesh", mjFCOMPTYPE_MESH}, {"gmsh", mjFCOMPTYPE_GMSH}, {"direct", mjFCOMPTYPE_DIRECT}