diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index fb8b2a8fc8..49818cfaa2 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -4661,6 +4661,18 @@ A second form of wrapping is where the tendon is constrained to pass *through* a wrap around it. This is enabled automatically when a sidesite is specified and its position is inside the volume of the obstacle geom. +.. youtube:: I2q7D0Vda-A + :width: 300px + :align: right + +**Visualization:** Tendon paths are visualized as in the image above, respecting the :ref:`width`, +:ref:`material` and :ref:`rgba` attributes below. A special kind of +visualization is used for unactuated 2-point tendons with :ref:`range` or +:ref:`springlength` of the form :at-val:`[0 X]`, with positive X. Such tendons act like a +cable, applying force only when stretched. Therefore when not stretched, they are drawn as a catenary of +length X, as in the clip on the right of `this example model +`__. + .. _tendon-spatial-name: :at:`name`: :at-val:`string, optional` diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index 8f5be4e036..e48f18344a 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -1729,6 +1729,16 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, objtype = mjOBJ_TENDON; category = mjCAT_DYNAMIC; if (vopt->flags[mjVIS_TENDON] && (category & catmask)) { + // mark actuated tendons + int* tendon_actuated = mjSTACKALLOC(d, m->ntendon, int); + mju_zeroInt(tendon_actuated, m->ntendon); + for (int i=0; i < m->nu; i++) { + if (m->actuator_trntype[i] == mjTRN_TENDON) { + tendon_actuated[m->actuator_trnid[2*i]] = 1; + } + } + + // draw tendons for (int i=0; i < m->ntendon; i++) { if (vopt->tendongroup[mjMAX(0, mjMIN(mjNGROUP-1, m->tendon_group[i]))]) { // tendon has a deadband spring @@ -1752,9 +1762,10 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, !mjDISABLED(mjDSBL_GRAVITY) && // gravity enabled mju_norm3(m->opt.gravity) > mjMINVAL && // gravity strictly nonzero m->tendon_num[i] == 2 && // only two sites on the tendon - (limitedspring || limitedconstraint) && // either spring or constraint length limits + (limitedspring != limitedconstraint) && // either spring or constraint length limits m->tendon_damping[i] == 0 && // no damping - m->tendon_frictionloss[i] == 0; // no frictionloss + m->tendon_frictionloss[i] == 0 && // no frictionloss + tendon_actuated[i] == 0; // no actuator // conditions not met: draw straight lines if (!draw_catenary) {