Added support for GMSH22 surface loading

PiperOrigin-RevId: 629734063
Change-Id: I6a250b81de4e7c8f7520795bfe74a81d719dfb50
This commit is contained in:
Mohammad Hamid
2024-05-01 08:42:53 -07:00
committed by Copybara-Service
parent 57b1dbac65
commit 131b1745db
14 changed files with 403 additions and 42 deletions
+4 -3
View File
@@ -3452,9 +3452,10 @@ saving the XML:
.. _body-flexcomp-file:
:at:`file`: :at-val:`string, optional`
The name of the file from which a **mesh** or a **gmsh** is loaded. For mesh, the file extentsion is used to
determine the file format. Supported formats are the same as in :ref:`mesh assets<asset-mesh>`. For gmsh, the file is
expected to be in GMSH format 4.1 or 2.2, ascii or binary, see :ref:`here<gmsh-file-docs>`.
The name of the file from which a **surface** (triangular) or **volumetric** (tetrahedral) mesh is loaded. For
surface meshes, the file extension is used to determine the file format. Supported formats are the same as in
:ref:`mesh assets<asset-mesh>` and also including GMSH. Volumetric meshes are supported only in GMSH format.
See :ref:`here<gmsh-file-docs>` for more information on GMSH files.
.. _body-flexcomp-rigid:
+18 -17
View File
@@ -24,39 +24,40 @@ General
field.
4. The type of the ``size`` argument of :ref:`mj_stackAllocNum` and :ref:`mj_stackAllocInt` was changed from ``int``
to ``size_t``.
5. Added support for gmsh format version 2.2 surface meshes in :ref:`flexcomp<body-flexcomp-file>`.
MJX
^^^
.. admonition:: Breaking API changes
:class: attention
5. Removed deprecated ``mjx.device_get_into`` and ``mjx.device_put`` functions as they lack critical new
6. Removed deprecated ``mjx.device_get_into`` and ``mjx.device_put`` functions as they lack critical new
functionality.
**Migration:** Use ``mjx.get_data_into`` instead of ``mjx.device_get_into``, and ``mjx.put_data`` instead of
``mjx.device_put``.
6. Added cylinder plane collisions.
7. Added ``efc_type`` to ``mjx.Data`` and ``dim``, ``efc_address`` to ``mjx.Contact``.
8. Added ``geom`` to ``mjx.Contact`` and marked ``geom1``, ``geom2`` deprecated.
9. Added ``ne``, ``nf``, ``nl``, ``nefc``, and ``ncon`` to ``mjx.Data`` to match ``mujoco.MjData``.
10. Given the above added fields, removed ``mjx.get_params``, ``mjx.ncon``, and ``mjx.count_constraints``.
11. Changed the way meshes are organized on device to speed up collision detection when a mesh is replicated for many
7. Added cylinder plane collisions.
8. Added ``efc_type`` to ``mjx.Data`` and ``dim``, ``efc_address`` to ``mjx.Contact``.
9. Added ``geom`` to ``mjx.Contact`` and marked ``geom1``, ``geom2`` deprecated.
10. Added ``ne``, ``nf``, ``nl``, ``nefc``, and ``ncon`` to ``mjx.Data`` to match ``mujoco.MjData``.
11. Given the above added fields, removed ``mjx.get_params``, ``mjx.ncon``, and ``mjx.count_constraints``.
12. Changed the way meshes are organized on device to speed up collision detection when a mesh is replicated for many
geoms.
12. Fixed a bug where capsules might be ignored in broadphase colliision checking.
13. Added cylinder collisions using SDFs.
14. Added support for all :ref:`condim <coContact>`: 1, 3, 4, 6.
15. Add support functions for ``id2name`` and ``name2id``, MJX versions of :ref:`mj_id2name` and :ref:`mj_name2id`.
16. Added support for :ref:`gravcomp<body-gravcomp>` and :ref:`actuatorgravcomp<body-joint-actuatorgravcomp>`.
17. Fixed a bug in ``mjx.ray`` for sometimes allowed negative distances for ray-mesh tests.
13. Fixed a bug where capsules might be ignored in broadphase colliision checking.
14. Added cylinder collisions using SDFs.
15. Added support for all :ref:`condim <coContact>`: 1, 3, 4, 6.
16. Add support functions for ``id2name`` and ``name2id``, MJX versions of :ref:`mj_id2name` and :ref:`mj_name2id`.
17. Added support for :ref:`gravcomp<body-gravcomp>` and :ref:`actuatorgravcomp<body-joint-actuatorgravcomp>`.
18. Fixed a bug in ``mjx.ray`` for sometimes allowed negative distances for ray-mesh tests.
Bug fixes
^^^^^^^^^
18. Defaults of lights were not being saved, now fixed.
19. Prevent overwriting of frame names by body names when saving an XML. Bug introduced in 3.1.4.
20. Fixed bug in Python binding of :ref:`mj_saveModel`: ``buffer`` argument was documented as optional but was actually
19. Defaults of lights were not being saved, now fixed.
20. Prevent overwriting of frame names by body names when saving an XML. Bug introduced in 3.1.4.
21. Fixed bug in Python binding of :ref:`mj_saveModel`: ``buffer`` argument was documented as optional but was actually
not optional.
21. Fixed bug that prevented memory allocations larger than 2.15 GB.
22. Fixed bug that prevented memory allocations larger than 2.15 GB.
Version 3.1.4 (April 10th, 2024)
+54 -17
View File
@@ -62,7 +62,7 @@ static void ReadStrFromBuffer(char* dest, const char* src, int maxlen) {
std::strncpy(dest, src, maxlen);
}
bool IsValidElementHeader22(const std::string& line) {
bool IsValidElementOrNodeHeader22(const std::string& line) {
// making sure characters are numbers
for (char c : line) {
if (!std::isdigit(c)) {
@@ -1296,10 +1296,21 @@ void mjCFlexcomp::LoadGMSH41(char* buffer, int binary, int nodeend,
// load GMSH format 2.2
void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int nodebegin, int elemend, int elembegin) {
// number of nodes
size_t numNodes = 0;
// ascii nodes
if (binary == 0) {
// convert node char buffer to stringstream
stringstream ss(std::string(buffer + nodebegin, nodeend - nodebegin));
std::string line;
// checking header template
std::getline(ss, line);
if (!IsValidElementOrNodeHeader22(line)) {
throw mjCError(NULL, "Invalid node header");
}
ss.seekg(-(line.size()+1), std::ios::cur);
// read header
size_t maxNodeTag = 0;
@@ -1307,7 +1318,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
if (!ss.good()) {
throw mjCError(NULL, "Error reading Nodes header");
}
size_t numNodes = maxNodeTag;
numNodes = maxNodeTag;
if (numNodes < 0 || numNodes >= INT_MAX / 3) {
throw mjCError(NULL, "Invalid number of nodes.");
@@ -1349,7 +1360,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
ReadStrFromBuffer(maxNodeTagChar, buffer + nodebegin, std::min(10, nodeend - nodebegin));
size_t measuredHeaderSize = strnlen(maxNodeTagChar, 10) - 1;
size_t maxNodeTag = std::stoi(maxNodeTagChar);
size_t numNodes = maxNodeTag;
numNodes = maxNodeTag;
// check number of nodes is a positive number
if (numNodes < 0) {
@@ -1396,7 +1407,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
// checking header template
std::getline(ss, line);
if (!IsValidElementHeader22(line)) {
if (!IsValidElementOrNodeHeader22(line)) {
throw mjCError(NULL, "Invalid elements header");
}
ss.seekg(-(line.size()+1), std::ios::cur);
@@ -1404,7 +1415,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
size_t maxElementTag = 0;
ss >> maxElementTag;
if (!ss.good()) {
throw mjCError(NULL, "GetMaxElementTag::Error reading Elements header");
throw mjCError(NULL, "Error reading Elements header");
}
size_t numElements = maxElementTag;
@@ -1419,18 +1430,30 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
}
// reading first element's type
int tag = 0, type = 0, numTags = 0;
ss >> tag >> type >> numTags;
int tag = 0, elementType = 0, numTags = 0;
ss >> tag >> elementType >> numTags;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements");
}
int numNodeTags = type;
size_t entityDim = 0;
int numNodeTags = 0;
// surface
if (elementType == 2) {
entityDim = 2;
numNodeTags = 3;
}
// tetrahedral
else if (elementType == 4) {
entityDim = 3;
numNodeTags = 4;
}
if (numNodeTags < 1 || numNodeTags >4) {
throw mjCError(NULL, "Invalid number of node tags");
}
// setting entityDim
size_t entityDim = numNodeTags - 1;
def.spec.flex->dim = entityDim;
// read elements, discard all tags
@@ -1438,7 +1461,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
for (size_t i=0; i<numElements; i++) {
int nodeTag = 0, physicalEntityTag = 0, elmentModelEntityTag = 0;
if (i != 0) {
ss >> tag >> type >> numTags;
ss >> tag >> elementType >> numTags;
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements");
}
@@ -1454,7 +1477,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
if (!ss.good()) {
throw mjCError(NULL, "Error reading Elements");
}
if (nodeTag > numElements || nodeTag < 1) {
if (nodeTag > numNodes || nodeTag < 1) {
throw mjCError(NULL, "Invalid node tag");
}
element.push_back((int)(nodeTag-1));
@@ -1464,7 +1487,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
// binary elements
else {
// header size for gmshApp
constexpr int elementHeaderSizeGmshApp = 6;
constexpr int elementHeaderSizeGmshApp = 4;
// header size for Ftetwild
constexpr int elementHeaderSizeFtetwild = 17;
// check header size
@@ -1480,7 +1503,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int numElements = maxElementTag;
int tag, numTags;
int nodeTag;
int numNodeTags;
int elementType;
// check number of elements is a positive number
if (numElements < 0) {
@@ -1491,14 +1514,28 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
int componentSize = sizeof(int);
// element buffer
const char* elementsBuffer = buffer + elembegin + measuredHeaderSize;
ReadFromBuffer(&numNodeTags, elementsBuffer);
ReadFromBuffer(&elementType, elementsBuffer);
ReadFromBuffer(&numTags, elementsBuffer + componentSize*2);
ReadFromBuffer(&tag, elementsBuffer + componentSize*3);
// tetrahedral has 4 node tags and surface has 3
int numNodeTags = 0;
size_t entityDim = 0;
// surface
if (elementType == 2) {
entityDim = 2;
numNodeTags = 3;
}
// tetrahedral
else if (elementType == 4) {
entityDim = 3;
numNodeTags = 4;
}
if (numNodeTags < 1 || numNodeTags >4) {
throw mjCError(NULL, "Invalid number of node tags");
}
size_t entityDim = numNodeTags - 1;
def.spec.flex->dim = entityDim;
// element data(Ftetwild): tag and 4 nodeTag
@@ -1534,7 +1571,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
// read first element
for (int k =0; k<numNodeTags; k++) {
ReadFromBuffer(&nodeTag, elementsBuffer + componentSize*(6+k));
if (nodeTag > numElements || nodeTag < 1) {
if (nodeTag > numNodes || nodeTag < 1) {
throw mjCError(NULL, "Invalid node tag");
}
element.push_back(nodeTag-1);
@@ -1564,7 +1601,7 @@ void mjCFlexcomp::LoadGMSH22(char* buffer, int binary, int nodeend,
for (int k = 0; k < numNodeTags; k++) {
const char* nodeTagBuffer = elementsBuffer + componentSize*(4+k);
ReadFromBuffer(&nodeTag, nodeTagBuffer);
if (nodeTag > numElements || nodeTag < 1) {
if (nodeTag > numNodes || nodeTag < 1) {
throw mjCError(NULL, "Invalid node tag");
}
element.push_back(nodeTag-1);
+47
View File
@@ -0,0 +1,47 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
14
1 -0.5 -0.5 0
2 0.5 -0.5 0
3 0.5 0.5 0
4 -0.5 0.5 0
5 -0.5 -0.5 1
6 0.5 -0.5 1
7 0.5 0.5 1
8 -0.5 0.5 1
9 0 0 0
10 0 -0.5 0.5
11 0.5 0 0.5
12 0 0.5 0.5
13 -0.5 0 0.5
14 0 0 1
$EndNodes
$Elements
24
1 2 2 0 1 9 4 3
2 2 2 0 1 9 3 2
3 2 2 0 1 9 1 4
4 2 2 0 1 9 2 1
5 2 2 0 1 2 10 1
6 2 2 0 1 1 10 5
7 2 2 0 1 6 10 2
8 2 2 0 1 5 10 6
9 2 2 0 1 3 11 2
10 2 2 0 1 2 11 6
11 2 2 0 1 7 11 3
12 2 2 0 1 6 11 7
13 2 2 0 1 4 12 3
14 2 2 0 1 3 12 7
15 2 2 0 1 8 12 4
16 2 2 0 1 7 12 8
17 2 2 0 1 1 13 4
18 2 2 0 1 5 13 1
19 2 2 0 1 4 13 8
20 2 2 0 1 8 13 5
21 2 2 0 1 5 6 14
22 2 2 0 1 8 5 14
23 2 2 0 1 6 7 14
24 2 2 0 1 7 8 14
$EndElements
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_22_ascii_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_22_binary_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
+67
View File
@@ -0,0 +1,67 @@
$MeshFormat
4.1 0 8
$EndMeshFormat
$Entities
0 0 1 0
1 -0.5 -0.5 0 0.5 0.5 1 0 0
$EndEntities
$Nodes
1 14 1 14
2 1 0 14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-0.5 -0.5 0
0.5 -0.5 0
0.5 0.5 0
-0.5 0.5 0
-0.5 -0.5 1
0.5 -0.5 1
0.5 0.5 1
-0.5 0.5 1
0 0 0
0 -0.5 0.5
0.5 0 0.5
0 0.5 0.5
-0.5 0 0.5
0 0 1
$EndNodes
$Elements
1 24 1 24
2 1 2 24
1 9 4 3
2 9 3 2
3 9 1 4
4 9 2 1
5 2 10 1
6 1 10 5
7 6 10 2
8 5 10 6
9 3 11 2
10 2 11 6
11 7 11 3
12 6 11 7
13 4 12 3
14 3 12 7
15 8 12 4
16 7 12 8
17 1 13 4
18 5 13 1
19 4 13 8
20 8 13 5
21 5 6 14
22 8 5 14
23 6 7 14
24 7 8 14
$EndElements
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_41_ascii_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
<mujoco model="gmsh">
<option timestep="0.01" integrator="implicitfast" solver="CG"/>
<asset>
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="512" height="512"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<visual>
<headlight diffuse="0.9 0.9 0.9" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/>
<quality shadowsize="4096"/>
<global offwidth="800" offheight="800"/>
</visual>
<worldbody>
<geom name="floor" size="1 1 .05" type="plane" material="grid" condim="3"/>
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1"/>
<flexcomp name="cube" type="gmsh" dim="2" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 0"
mass="10" scale="1 1 1" file="cube_41_binary_surf_gmshApp.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
@@ -19,7 +19,7 @@
<worldbody>
<geom name="floor" size="20 20 .05" type="plane" material="grid" condim="3" />
<light directional="false" diffuse=".2 .2 .2" specular="0 0 0" pos="0 0 5" dir="0 0 -1" />
<flexcomp name="shark binary 2.2" type="gmsh" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4"
<flexcomp name="shark binary 2.2" type="gmsh" dim="3" rgba=".8 .2 .2 1" radius="0.001" pos="0 0 4"
mass="10" scale="1 1 1" file="shark_41_ascii_missing_node_index.msh">
<edge equality="true" />
</flexcomp>
@@ -8,7 +8,6 @@ $EndEntities
$Nodes
1 652 1 652
3 0 0 652
1
2
3
4
+108 -3
View File
@@ -23,6 +23,8 @@
#include <mujoco/mujoco.h>
#include "test/fixture.h"
namespace mujoco {
namespace {
@@ -32,6 +34,8 @@ using ::testing::HasSubstr;
using UserFlexTest = MujocoTest;
TEST_F(UserFlexTest, ParentMustHaveName) {
static constexpr char xml[] = R"(
<mujoco>
@@ -255,6 +259,56 @@ TEST_F(UserFlexTest, LoadMSHBinaryGMSH_22_Success) {
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHSurfaceBinaryGMSH_41_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_41_binary_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHSurfaceBinaryGMSH_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_22_binary_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHBinaryFTETWILD_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_binary_fTetWild.xml");
@@ -297,6 +351,56 @@ TEST_F(UserFlexTest, LoadMSHASCIIGMSH_22_Success) {
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHSurfaceASCIIGMSH_41_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_41_ascii_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHSurfaceASCIIGMSH_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/cube_22_ascii_surf_gmshApp.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
// first node x y z
EXPECT_EQ(m->flex_xvert0[0], -0.5 );
EXPECT_EQ(m->flex_xvert0[1], -0.5 );
EXPECT_EQ(m->flex_xvert0[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
EXPECT_EQ(m->flex_elem[1], 4-1 );
EXPECT_EQ(m->flex_elem[2], 3-1 );
mj_step(m, d);
mj_deleteModel(m);
mj_deleteData(d);
}
TEST_F(UserFlexTest, LoadMSHASCIIFTETWILD_22_Success) {
const std::string xml_path =
GetTestDataFilePath("user/testdata/shark_22_ascii_fTetWild.xml");
@@ -329,7 +433,7 @@ TEST_F(UserFlexTest, LoadMSHASCII_41_MissingNodeIndex_Fail) {
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Element size must be a multiple of dim+1"));
"XML Error: Error: Node tags must be sequential"));
mj_deleteModel(m);
}
@@ -361,8 +465,9 @@ TEST_F(UserFlexTest, LoadMSHASCII_22_MissingNumNodes_Fail) {
"user/testdata/malformed_shark_22_ascii_missing_num_nodes.xml");
std::array<char, 1024> error;
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr(
"XML Error: Element size must be a multiple of dim+1"));
// TODO(mohammadhamid): Replace with an assertion about the error message. For
// some reason, on Windows the error message is different on GH Actions
EXPECT_THAT(m, IsNull());
mj_deleteModel(m);
}