Generate the MJCF grammar table and enforce its constraints.

The hand-written MJCF[] table in xml_native_reader.cc is replaced by
mjcf_table.inc, emitted from mjcf.schema by generate_mjcf_table.py and
checked for freshness by doc_test. nMJCF is now self-sizing. The two
tables are identical as trees of (tag, cardinality, attribute-set);
within-row attribute order changes where the schema factors shared
groups and projects default-context rows, and top-level rows follow
the schema's dependency order -- neither affects validation, which is
set-based, nor XMLschema.rst, whose generator orders sections itself
(regenerated here, reading the .inc instead of the reader source).

The schema's constraint declarations become enforcement: the emitter
writes a companion MJCF_constraints[] array (row-indexed into MJCF[]),
and mjXSchema::Check evaluates each element's constraints after its
attribute check, with uniform messages derived from the declaration:
"at most one of 'fovy', 'sensorsize' can be specified", "attributes
'reftype', 'refname' must be specified together", and so on.
Multi-attribute bundles render as ('site1', 'site2').

Fifteen hand-written co-occurrence checks across fourteen elements are
deleted -- connect/weld semantics mixing and completeness, the actuator
transmission mutex, camera fovy/sensorsize, light directional/type,
inertial fullinertia-versus-orientation, rangefinder and the distance
family, contact's matching criteria, user-sensor pairing, the frame
family's reftype/refname, size memory exclusivities, mesh builtin
exclusions, and attach body/frame (newly declared). Tests assert the
uniform messages.

Two findings along the way: sensorsize-requires-resolution is a
value-level compiler rule (positive resolution), not a presence rule --
a presence constraint would be wrong and is not declared; and Size()'s
nstack/njmax range checks tested the spec value before assignment, so
they never validated the parsed value -- now they do.

Verified by compiling all 81 models in the model/ corpus.

PiperOrigin-RevId: 958064622
Change-Id: I802cf5c0aee08a62926e36a281320ff9e34c0668
This commit is contained in:
Yuval Tassa
2026-08-02 16:29:32 -07:00
committed by Copybara-Service
parent 3f8db4c17a
commit 790f8fac30
12 changed files with 1600 additions and 1261 deletions
+645 -647
View File
File diff suppressed because it is too large Load Diff
+181
View File
@@ -0,0 +1,181 @@
# Copyright 2026 DeepMind Technologies Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Generates the MJCF[] grammar table from src/xml/mjcf.schema.
The table (src/xml/mjcf_table.inc) is the element tree consumed by the
mjXSchema validator: rows of {name, cardinality, attributes...} with
{"<"}/{">"} nesting markers. It is checked in and gated by
test/doc/doc_test.py, which regenerates it from the schema and diffs.
"""
import sys
import os
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, _SCRIPT_DIR)
import mjcf_schema
_REPO_ROOT = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
SCHEMA_PATH = os.path.join(_REPO_ROOT, 'src', 'xml', 'mjcf.schema')
_HEADER = '''\
// Copyright 2026 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// GENERATED FILE, DO NOT EDIT. Generated from src/xml/mjcf.schema by
// doc/generate/generate_mjcf_table.py; test/doc/doc_test.py checks freshness.
//
// The MJCF grammar table consumed by mjXSchema: rows of {name, cardinality,
// attributes...} with {"<"}/{">"} nesting markers. worldbody, frame and
// replicate have no rows: mjXSchema::NameMatch validates them against the
// body row (see the alias= facets in mjcf.schema).
// clang-format off
'''
_WIDTH = 100
def _wrap_row(parts: list[str], indent: int) -> list[str]:
"""One {...}, initializer, wrapped at _WIDTH with hanging indent."""
lines = []
line = ' ' * indent + '{' + parts[0]
for part in parts[1:]:
candidate = f'{line}, {part}'
if len(candidate) + 2 > _WIDTH: # room for "},"
lines.append(line + ',')
line = ' ' * (indent + 4) + part
else:
line = candidate
lines.append(line + '},')
return lines
KIND_CHAR = {'exclusive': 'e', 'together': 't', 'requires': 'r',
'oneof': 'o'}
def _element_constraints(schema, element):
"""Element's own constraints plus those of transitively used groups."""
cons = list(element.constraints())
visited = set()
stack = [m.group for m in element.members
if isinstance(m, mjcf_schema.Use)]
while stack:
name = stack.pop()
if name in visited:
continue
visited.add(name)
group = schema.groups[name]
for member in group.members:
if isinstance(member, mjcf_schema.Constraint):
cons.append(member)
elif isinstance(member, mjcf_schema.Use):
stack.append(member.group)
return cons
def generate() -> str:
"""Generates the C++ grammar table header file contents from mjcf.schema."""
schema = mjcf_schema.parse_file(SCHEMA_PATH)
out = []
constraints = []
count = 0
def emit_entry(lines: list[str]):
nonlocal count
out.extend(lines)
count += 1
def visit(element: mjcf_schema.Element, card: str, indent: int,
project: bool):
# a row in default context is the element's defaultable projection
attrs = [a for a in schema.expanded_attrs(element)]
if project:
attrs = [a for a in attrs
if a.name not in ('name', 'class')
and not a.facets.get('nodefault')]
parts = [f'"{element.xml_name()}"', f'"{card}"']
parts += [f'"{a.name}"' for a in attrs]
row_index = count
emit_entry(_wrap_row(parts, indent))
# presence constraints whose attributes all survive in this row
row_attrs = {a.name for a in attrs}
for con in _element_constraints(schema, element):
if all(all(n in row_attrs for n in b) for b in con.bundles):
spec = '|'.join(' '.join(b) for b in con.bundles)
constraints.append(f" {{{row_index}, '{KIND_CHAR[con.kind]}', "
f'"{spec}"}},')
children = [c for c in element.children() if c.name != element.name]
if project:
# plugin configuration is not settable per-class
children = [c for c in children if c.name != 'plugin']
if not children:
return
emit_entry([' ' * indent + '{"<"},'])
for child in children:
decl = schema.elements[child.name]
child_project = project or (
element.name == 'default' and not child.name.startswith('default_'))
visit(decl, child.card, indent + 4, child_project)
if indent == 0:
out.append('')
emit_entry([' ' * indent + '{">"},'])
visit(schema.elements['mujoco'], '!', 0, False)
body = '\n'.join(out)
con_body = '\n'.join(constraints)
return (_HEADER +
'std::vector<const char*> MJCF[] = {\n' + body + '\n};\n'
'// clang-format on\n\n'
'const int nMJCF = sizeof(MJCF) / sizeof(MJCF[0]);\n\n'
'// presence constraints, indexed into MJCF[]; enforced by\n'
'// mjXSchema::Check. spec: attribute bundles, space-joined,\n'
"// '|'-separated; kind: e=exclusive t=together r=requires o=oneof\n"
'// clang-format off\n'
'const mjXConstraintDef MJCF_constraints[] = {\n' + con_body +
'\n};\n'
'// clang-format on\n\n'
'const int nMJCF_constraints = '
'sizeof(MJCF_constraints) / sizeof(MJCF_constraints[0]);\n')
def main() -> int:
if len(sys.argv) > 2:
sys.exit('usage: generate_mjcf_table.py [output.inc]')
text = generate()
if len(sys.argv) == 2:
with open(sys.argv[1], 'w', encoding='utf-8') as file:
file.write(text)
else:
sys.stdout.write(text)
return 0
if __name__ == '__main__':
sys.exit(main())
+8 -7
View File
@@ -113,18 +113,18 @@ def generate_dropdown(
def generate() -> str:
"""Generates XMLschema.rst by parsing xml_native_reader.cc.
"""Generates XMLschema.rst by parsing mjcf_table.inc.
The schema is defined in xml_native_reader.cc as a nested structure called
MJCF[nMJCF]. This function parses that structure and generates nested
dropdown directives with list-tables for attributes.
The schema is defined in mjcf_table.inc (generated from mjcf.schema) as a
nested structure called MJCF[]. This function parses that structure and
generates nested dropdown directives with list-tables for attributes.
Returns:
RST content with nested dropdown directives for the MJCF schema.
"""
script_dir = os.path.dirname(os.path.abspath(__file__))
repo_root = os.path.dirname(os.path.dirname(script_dir))
filepath = os.path.join(repo_root, 'src', 'xml', 'xml_native_reader.cc')
filepath = os.path.join(repo_root, 'src', 'xml', 'mjcf_table.inc')
xmlfile = os.path.join(repo_root, 'doc', 'XMLreference.rst')
# Collect all link targets from XMLreference.rst for validation.
@@ -159,7 +159,7 @@ def generate() -> str:
# Skip to the MJCF schema definition in the C++ source.
for line in file:
if 'std::vector<const char*> MJCF[nMJCF] = {' in line.strip():
if 'std::vector<const char*> MJCF[] = {' in line.strip():
break
# Parse the schema structure.
@@ -224,7 +224,8 @@ def generate() -> str:
parent[level + 1] = element[0]
element = []
return output
# single newline at end of file
return output.rstrip('\n') + '\n'
def main() -> int:
+542
View File
@@ -0,0 +1,542 @@
// Copyright 2026 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// GENERATED FILE, DO NOT EDIT. Generated from src/xml/mjcf.schema by
// doc/generate/generate_mjcf_table.py; test/doc/doc_test.py checks freshness.
//
// The MJCF grammar table consumed by mjXSchema: rows of {name, cardinality,
// attributes...} with {"<"}/{">"} nesting markers. worldbody, frame and
// replicate have no rows: mjXSchema::NameMatch validates them against the
// body row (see the alias= facets in mjcf.schema).
// clang-format off
std::vector<const char*> MJCF[] = {
{"mujoco", "!", "model"},
{"<"},
{"compiler", "*", "autolimits", "boundmass", "boundinertia", "settotalmass", "balanceinertia",
"strippath", "coordinate", "angle", "fitaabb", "eulerseq", "meshdir", "texturedir",
"discardvisual", "usethread", "fusestatic", "inertiafromgeom", "inertiagrouprange",
"saveinertial", "assetdir", "alignfree", "conflict"},
{"<"},
{"lengthrange", "?", "mode", "useexisting", "uselimit", "accel", "maxforce", "timeconst",
"timestep", "inttotal", "interval", "tolrange"},
{">"},
{"option", "*", "timestep", "impratio", "tolerance", "ls_tolerance", "noslip_tolerance",
"ccd_tolerance", "sleep_tolerance", "gravity", "wind", "magnetic", "density", "viscosity",
"o_margin", "o_solref", "o_solimp", "o_friction", "integrator", "cone", "jacobian",
"solver", "iterations", "ls_iterations", "noslip_iterations", "ccd_iterations",
"sdf_iterations", "sdf_initpoints", "actuatorgroupdisable"},
{"<"},
{"flag", "?", "constraint", "equality", "frictionloss", "limit", "contact", "spring",
"damper", "gravity", "clampctrl", "warmstart", "filterparent", "actuation", "refsafe",
"sensor", "midphase", "eulerdamp", "autoreset", "nativeccd", "island", "multiccd",
"override", "energy", "fwdinv", "invdiscrete", "sleep", "diagexact"},
{">"},
{"size", "*", "memory", "njmax", "nconmax", "nstack", "nuserdata", "nkey", "nuser_body",
"nuser_jnt", "nuser_geom", "nuser_site", "nuser_cam", "nuser_tendon", "nuser_actuator",
"nuser_sensor"},
{"statistic", "*", "meaninertia", "meanmass", "meansize", "extent", "center"},
{"visual", "*"},
{"<"},
{"global", "?", "cameraid", "orthographic", "fovy", "ipd", "azimuth", "elevation",
"linewidth", "glow", "offwidth", "offheight", "realtime", "ellipsoidinertia",
"bvactive"},
{"quality", "?", "shadowsize", "offsamples", "numslices", "numstacks", "numquads"},
{"headlight", "?", "ambient", "diffuse", "specular", "active"},
{"map", "?", "stiffness", "stiffnessrot", "force", "torque", "alpha", "fogstart", "fogend",
"znear", "zfar", "haze", "shadowclip", "shadowscale", "actuatortendon"},
{"scale", "?", "forcewidth", "contactwidth", "contactheight", "connect", "com", "camera",
"light", "selectpoint", "jointlength", "jointwidth", "actuatorlength", "actuatorwidth",
"framelength", "framewidth", "constraint", "slidercrank", "frustum"},
{"rgba", "?", "fog", "haze", "force", "inertia", "joint", "actuator", "actuatornegative",
"actuatorpositive", "com", "camera", "light", "selectpoint", "connect", "contactpoint",
"contactforce", "contactfriction", "contacttorque", "contactgap", "rangefinder",
"constraint", "slidercrank", "crankbroken", "frustum", "bv", "bvactive"},
{">"},
{"default", "R", "class"},
{"<"},
{"mesh", "?", "scale", "maxhullvert", "inertia"},
{"material", "?", "texture", "texrepeat", "texuniform", "emission", "specular",
"shininess", "reflectance", "metallic", "roughness", "rgba"},
{"<"},
{"layer", "*", "texture", "role"},
{">"},
{"joint", "?", "type", "group", "pos", "axis", "springdamper", "limited",
"actuatorfrclimited", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"stiffness", "range", "actuatorfrcrange", "actuatorgravcomp", "margin", "ref",
"springref", "armature", "damping", "frictionloss", "user"},
{"geom", "?", "type", "contype", "conaffinity", "condim", "group", "priority", "size",
"material", "friction", "mass", "density", "shellinertia", "solmix", "solref",
"solimp", "margin", "gap", "surfacevel", "adhesion", "fromto", "pos", "quat",
"axisangle", "xyaxes", "zaxis", "euler", "hfield", "mesh", "fitscale", "rgba",
"fluidshape", "fluidcoef", "user"},
{"site", "?", "type", "group", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler",
"material", "size", "fromto", "rgba", "user"},
{"camera", "?", "projection", "fovy", "ipd", "resolution", "output", "pos", "quat",
"axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel", "principal",
"principalpixel", "sensorsize", "user"},
{"light", "?", "directional", "type", "castshadow", "active", "pos", "dir", "bulbradius",
"intensity", "range", "attenuation", "cutoff", "exponent", "ambient", "diffuse",
"specular", "mode"},
{"pair", "?", "condim", "friction", "solref", "solreffriction", "solimp", "gap", "margin",
"adhesion"},
{"equality", "?", "active", "solref", "solimp"},
{"tendon", "?", "group", "limited", "range", "solreflimit", "solimplimit",
"solreffriction", "solimpfriction", "frictionloss", "springlength", "width",
"material", "margin", "stiffness", "damping", "rgba", "user"},
{"general", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user", "ctrllimited",
"forcelimited", "actlimited", "forcerange", "actrange", "gear", "damping", "armature",
"cranklength", "actdim", "input", "velrange", "ffrange", "dyntype", "gaintype",
"biastype", "dynprm", "gainprm", "biasprm", "actearly"},
{"motor", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user", "ctrllimited",
"forcelimited", "forcerange", "gear", "damping", "armature", "cranklength"},
{"position", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"ctrllimited", "forcelimited", "inheritrange", "forcerange", "gear", "damping",
"armature", "cranklength", "kp", "kv", "dampratio", "timeconst"},
{"velocity", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"ctrllimited", "forcelimited", "forcerange", "gear", "damping", "armature",
"cranklength", "kv"},
{"intvelocity", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"ctrllimited", "forcelimited", "actlimited", "forcerange", "actrange", "inheritrange",
"gear", "damping", "armature", "cranklength", "kp", "kv", "dampratio"},
{"orientation", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"forcelimited", "forcerange", "kp", "kv", "dampratio", "input"},
{"pid", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user", "ctrllimited",
"forcelimited", "posrange", "velrange", "ffrange", "forcerange", "inheritrange",
"gear", "damping", "armature", "cranklength", "kp", "kv", "dampratio", "ki", "imax",
"slewmax", "input"},
{"damper", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user", "forcelimited",
"forcerange", "gear", "damping", "armature", "cranklength", "kv"},
{"cylinder", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"ctrllimited", "forcelimited", "forcerange", "gear", "damping", "armature",
"cranklength", "timeconst", "area", "diameter", "bias"},
{"muscle", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user", "ctrllimited",
"forcelimited", "forcerange", "gear", "damping", "armature", "cranklength",
"timeconst", "range", "force", "scale", "lmin", "lmax", "vmax", "fpmax", "fvmax"},
{"adhesion", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"forcelimited", "forcerange", "gain"},
{"dcmotor", "?", "group", "nsample", "interp", "delay", "ctrlrange", "user", "ctrllimited",
"gear", "damping", "armature", "cranklength", "motorconst", "resistance", "nominal",
"saturation", "inductance", "cogging", "controller", "thermal", "lugre", "input"},
{">"},
{"extension", "*"},
{"<"},
{"plugin", "*", "plugin"},
{"<"},
{"instance", "*", "name"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{">"},
{"asset", "*"},
{"<"},
{"mesh", "*", "name", "class", "content_type", "file", "vertex", "normal", "texcoord",
"face", "refpos", "refquat", "scale", "smoothnormal", "maxhullvert", "inertia",
"builtin", "params", "material"},
{"<"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"hfield", "*", "name", "content_type", "file", "nrow", "ncol", "size", "elevation"},
{"skin", "*", "name", "file", "material", "rgba", "inflate", "vertex", "texcoord", "face",
"group"},
{"<"},
{"bone", "*", "body", "bindpos", "bindquat", "vertid", "vertweight"},
{">"},
{"texture", "*", "name", "type", "colorspace", "content_type", "file", "gridsize",
"gridlayout", "fileright", "fileleft", "fileup", "filedown", "filefront", "fileback",
"builtin", "rgb1", "rgb2", "mark", "markrgb", "random", "width", "height", "hflip",
"vflip", "nchannel"},
{"material", "*", "name", "class", "texture", "texrepeat", "texuniform", "emission",
"specular", "shininess", "reflectance", "metallic", "roughness", "rgba"},
{"<"},
{"layer", "*", "texture", "role"},
{">"},
{"model", "*", "name", "file", "content_type"},
{">"},
{"body", "R", "name", "childclass", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler",
"mocap", "gravcomp", "sleep", "simple", "user"},
{"<"},
{"inertial", "?", "pos", "quat", "mass", "diaginertia", "axisangle", "xyaxes", "zaxis",
"euler", "fullinertia"},
{"joint", "*", "name", "class", "type", "group", "pos", "axis", "springdamper", "limited",
"actuatorfrclimited", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"stiffness", "range", "actuatorfrcrange", "actuatorgravcomp", "margin", "ref",
"springref", "armature", "damping", "frictionloss", "user"},
{"freejoint", "*", "name", "group", "align"},
{"geom", "*", "name", "class", "type", "contype", "conaffinity", "condim", "group",
"priority", "size", "material", "friction", "mass", "density", "shellinertia",
"solmix", "solref", "solimp", "margin", "gap", "surfacevel", "adhesion", "fromto",
"pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "hfield", "mesh", "fitscale",
"rgba", "fluidshape", "fluidcoef", "user"},
{"<"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"attach", "*", "model", "body", "frame", "prefix"},
{"site", "*", "name", "class", "type", "group", "pos", "quat", "axisangle", "xyaxes",
"zaxis", "euler", "material", "size", "fromto", "rgba", "user"},
{"camera", "*", "name", "class", "projection", "fovy", "ipd", "resolution", "output",
"pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target", "focal",
"focalpixel", "principal", "principalpixel", "sensorsize", "user"},
{"light", "*", "name", "class", "directional", "type", "castshadow", "active", "pos",
"dir", "bulbradius", "intensity", "range", "attenuation", "cutoff", "exponent",
"ambient", "diffuse", "specular", "mode", "target", "texture"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{"composite", "*", "prefix", "type", "count", "offset", "vertex", "initial", "curve",
"size", "quat"},
{"<"},
{"joint", "*", "kind", "group", "stiffness", "damping", "armature", "solreffix",
"solimpfix", "type", "axis", "limited", "range", "margin", "solreflimit",
"solimplimit", "frictionloss", "solreffriction", "solimpfriction"},
{"skin", "?", "texcoord", "material", "group", "rgba", "inflate", "subgrid"},
{"geom", "?", "type", "contype", "conaffinity", "condim", "group", "priority", "size",
"material", "rgba", "friction", "mass", "density", "solmix", "solref", "solimp",
"margin", "gap", "surfacevel", "adhesion"},
{"site", "?", "group", "size", "material", "rgba"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"flexcomp", "*", "name", "type", "group", "dim", "dof", "count", "cellcount", "spacing",
"radius", "rigid", "mass", "inertiabox", "scale", "file", "point", "element",
"texcoord", "material", "rgba", "flatskin", "pos", "quat", "axisangle", "xyaxes",
"zaxis", "euler", "origin"},
{"<"},
{"edge", "?", "equality", "solref", "solimp", "stiffness", "damping"},
{"elasticity", "?", "young", "poisson", "damping", "thickness", "elastic2d"},
{"contact", "?", "contype", "conaffinity", "condim", "priority", "friction", "solmix",
"solref", "solimp", "margin", "gap", "internal", "selfcollide", "activelayers",
"passive"},
{"pin", "*", "id", "range", "grid", "gridrange"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{">"},
{"deformable", "*"},
{"<"},
{"flex", "*", "name", "group", "dim", "radius", "material", "rgba", "flatskin", "body",
"vertex", "element", "texcoord", "elemtexcoord", "node", "cellcount", "dof"},
{"<"},
{"contact", "?", "contype", "conaffinity", "condim", "priority", "friction", "solmix",
"solref", "solimp", "margin", "gap", "internal", "selfcollide", "activelayers",
"passive"},
{"edge", "?", "stiffness", "damping"},
{"elasticity", "?", "young", "poisson", "damping", "thickness", "elastic2d"},
{">"},
{"skin", "*", "name", "file", "material", "rgba", "inflate", "vertex", "texcoord", "face",
"group"},
{"<"},
{"bone", "*", "body", "bindpos", "bindquat", "vertid", "vertweight"},
{">"},
{">"},
{"contact", "*"},
{"<"},
{"pair", "*", "name", "class", "geom1", "geom2", "condim", "friction", "solref",
"solreffriction", "solimp", "gap", "margin", "adhesion"},
{"exclude", "*", "name", "body1", "body2"},
{">"},
{"tendon", "*"},
{"<"},
{"spatial", "*", "name", "class", "group", "limited", "actuatorfrclimited", "range",
"actuatorfrcrange", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "width", "material", "margin", "stiffness", "damping",
"armature", "rgba", "user"},
{"<"},
{"site", "*", "site"},
{"geom", "*", "geom", "sidesite"},
{"pulley", "*", "divisor"},
{">"},
{"fixed", "*", "name", "class", "group", "limited", "actuatorfrclimited", "range",
"actuatorfrcrange", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "margin", "stiffness", "damping", "armature", "user"},
{"<"},
{"joint", "*", "joint", "coef"},
{">"},
{">"},
{"equality", "*"},
{"<"},
{"connect", "*", "name", "class", "active", "solref", "solimp", "body1", "body2", "anchor",
"site1", "site2"},
{"weld", "*", "name", "class", "active", "solref", "solimp", "body1", "body2", "relpose",
"anchor", "site1", "site2", "torquescale"},
{"joint", "*", "name", "class", "active", "solref", "solimp", "joint1", "joint2",
"polycoef"},
{"tendon", "*", "name", "class", "active", "solref", "solimp", "tendon1", "tendon2",
"polycoef"},
{"flex", "*", "name", "class", "active", "solref", "solimp", "flex"},
{"flexvert", "*", "name", "class", "active", "solref", "solimp", "flex"},
{"flexstrain", "*", "name", "class", "active", "solref", "solimp", "flex", "cell"},
{">"},
{"actuator", "*"},
{"<"},
{"general", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "forcelimited", "actlimited", "forcerange", "actrange",
"lengthrange", "gear", "damping", "armature", "cranklength", "joint", "jointinparent",
"tendon", "slidersite", "cranksite", "site", "refsite", "body", "actdim", "input",
"velrange", "ffrange", "dyntype", "gaintype", "biastype", "dynprm", "gainprm",
"biasprm", "actearly"},
{"motor", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"ctrllimited", "forcelimited", "forcerange", "lengthrange", "gear", "damping",
"armature", "cranklength", "joint", "jointinparent", "tendon", "slidersite",
"cranksite", "site", "refsite"},
{"position", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "forcelimited", "inheritrange", "forcerange", "lengthrange",
"gear", "damping", "armature", "cranklength", "joint", "jointinparent", "tendon",
"slidersite", "cranksite", "site", "refsite", "kp", "kv", "dampratio", "timeconst"},
{"velocity", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "forcelimited", "forcerange", "lengthrange", "gear", "damping",
"armature", "cranklength", "joint", "jointinparent", "tendon", "slidersite",
"cranksite", "site", "refsite", "kv"},
{"intvelocity", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "forcelimited", "actlimited", "forcerange", "actrange",
"inheritrange", "lengthrange", "gear", "damping", "armature", "cranklength", "joint",
"jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "kp", "kv",
"dampratio"},
{"orientation", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "forcelimited", "forcerange", "joint", "site", "refsite", "kp", "kv",
"dampratio", "input"},
{"pid", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange", "user",
"ctrllimited", "forcelimited", "posrange", "velrange", "ffrange", "forcerange",
"inheritrange", "lengthrange", "gear", "damping", "armature", "cranklength", "joint",
"jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite", "kp", "kv",
"dampratio", "ki", "imax", "slewmax", "input"},
{"damper", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "forcelimited", "forcerange", "lengthrange", "gear", "damping", "armature",
"cranklength", "joint", "jointinparent", "tendon", "slidersite", "cranksite", "site",
"refsite", "kv"},
{"cylinder", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "forcelimited", "forcerange", "lengthrange", "gear", "damping",
"armature", "cranklength", "joint", "jointinparent", "tendon", "slidersite",
"cranksite", "site", "refsite", "timeconst", "area", "diameter", "bias"},
{"muscle", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "forcelimited", "forcerange", "lengthrange", "gear", "damping",
"armature", "cranklength", "joint", "jointinparent", "tendon", "slidersite",
"cranksite", "timeconst", "tausmooth", "range", "force", "scale", "lmin", "lmax",
"vmax", "fpmax", "fvmax"},
{"adhesion", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "forcelimited", "forcerange", "body", "gain"},
{"dcmotor", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "ctrllimited", "lengthrange", "gear", "damping", "armature", "cranklength",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"motorconst", "resistance", "nominal", "saturation", "inductance", "cogging",
"controller", "thermal", "lugre", "input"},
{"plugin", "*", "name", "class", "group", "nsample", "interp", "delay", "ctrlrange",
"user", "plugin", "instance", "ctrllimited", "forcelimited", "actlimited",
"forcerange", "actrange", "lengthrange", "gear", "damping", "armature", "cranklength",
"joint", "jointinparent", "site", "actdim", "dyntype", "dynprm", "tendon", "cranksite",
"slidersite", "actearly"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"sensor", "*"},
{"<"},
{"touch", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user",
"site"},
{"accelerometer", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site"},
{"velocimeter", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site"},
{"gyro", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user",
"site"},
{"force", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user",
"site"},
{"torque", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site"},
{"magnetometer", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site"},
{"camprojection", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site", "camera"},
{"rangefinder", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site", "camera", "data"},
{"jointpos", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"jointvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"tendonpos", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "tendon"},
{"tendonvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "tendon"},
{"actuatorpos", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "actuator"},
{"actuatorvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "actuator"},
{"actuatorfrc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "actuator"},
{"jointactuatorfrc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff",
"noise", "user", "joint"},
{"tendonactuatorfrc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff",
"noise", "user", "tendon"},
{"ballquat", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"ballangvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"jointlimitpos", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"jointlimitvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"jointlimitfrc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "joint"},
{"tendonlimitpos", "*", "name", "nsample", "interp", "delay", "interval", "cutoff",
"noise", "user", "tendon"},
{"tendonlimitvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff",
"noise", "user", "tendon"},
{"tendonlimitfrc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff",
"noise", "user", "tendon"},
{"framepos", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"framequat", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"framexaxis", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"frameyaxis", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"framezaxis", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"framelinvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"frameangvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname", "reftype", "refname"},
{"framelinacc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname"},
{"frameangacc", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "objtype", "objname"},
{"subtreecom", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "body"},
{"subtreelinvel", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "body"},
{"subtreeangmom", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "body"},
{"insidesite", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "site", "objtype", "objname"},
{"distance", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "geom1", "geom2", "body1", "body2"},
{"normal", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "geom1", "geom2", "body1", "body2"},
{"fromto", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "geom1", "geom2", "body1", "body2"},
{"contact", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user", "geom1", "geom2", "body1", "body2", "subtree1", "subtree2", "site", "num",
"data", "reduce"},
{"e_potential", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user"},
{"e_kinetic", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise",
"user"},
{"clock", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tactile", "*", "name", "geom", "mesh", "nsample", "interp", "delay", "interval", "user"},
{"user", "*", "name", "objtype", "objname", "datatype", "needstage", "dim", "cutoff",
"noise", "user"},
{"plugin", "*", "name", "plugin", "instance", "cutoff", "objtype", "objname", "reftype",
"refname", "user"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"custom", "*"},
{"<"},
{"numeric", "*", "name", "size", "data"},
{"text", "*", "name", "data"},
{"tuple", "*", "name"},
{"<"},
{"element", "*", "objtype", "objname", "prm"},
{">"},
{">"},
{"keyframe", "*"},
{"<"},
{"key", "*", "name", "time", "qpos", "qvel", "act", "mpos", "mquat", "ctrl"},
{">"},
{">"},
};
// clang-format on
const int nMJCF = sizeof(MJCF) / sizeof(MJCF[0]);
// presence constraints, indexed into MJCF[]; enforced by
// mjXSchema::Check. spec: attribute bundles, space-joined,
// '|'-separated; kind: e=exclusive t=together r=requires o=oneof
// clang-format off
const mjXConstraintDef MJCF_constraints[] = {
{10, 'e', "memory|nstack"},
{10, 'e', "memory|njmax"},
{31, 'e', "fovy|sensorsize"},
{32, 'e', "directional|type"},
{61, 'e', "builtin|file"},
{61, 'e', "builtin|vertex"},
{82, 'e', "fullinertia|quat|axisangle|xyaxes|zaxis|euler"},
{92, 'e', "body|frame"},
{94, 'e', "fovy|sensorsize"},
{95, 'e', "directional|type"},
{156, 'e', "site1 site2|body1 body2 anchor"},
{156, 'o', "site1 site2|body1 anchor"},
{156, 't', "site1|site2"},
{157, 'e', "site1 site2|body1 body2 anchor relpose"},
{157, 'o', "site1 site2|body1"},
{157, 't', "site1|site2"},
{193, 'e', "site|camera"},
{193, 'o', "site|camera"},
{211, 't', "reftype|refname"},
{212, 't', "reftype|refname"},
{213, 't', "reftype|refname"},
{214, 't', "reftype|refname"},
{215, 't', "reftype|refname"},
{216, 't', "reftype|refname"},
{217, 't', "reftype|refname"},
{224, 'e', "geom1|body1"},
{224, 'o', "geom1|body1"},
{224, 'e', "geom2|body2"},
{224, 'o', "geom2|body2"},
{225, 'e', "geom1|body1"},
{225, 'o', "geom1|body1"},
{225, 'e', "geom2|body2"},
{225, 'o', "geom2|body2"},
{226, 'e', "geom1|body1"},
{226, 'o', "geom1|body1"},
{226, 'e', "geom2|body2"},
{226, 'o', "geom2|body2"},
{227, 'e', "geom1|body1|subtree1|site"},
{227, 'e', "geom2|body2|subtree2"},
{232, 't', "objtype|objname"},
};
// clang-format on
const int nMJCF_constraints = sizeof(MJCF_constraints) / sizeof(MJCF_constraints[0]);
+17 -579
View File
@@ -165,457 +165,7 @@ static void UpdateString(string& psuffix, int count, int i) {
//---------------------------------- MJCF schema ---------------------------------------------------
// clang-format off
std::vector<const char*> MJCF[nMJCF] = {
{"mujoco", "!", "model"},
{"<"},
{"compiler", "*", "autolimits", "boundmass", "boundinertia", "settotalmass",
"balanceinertia", "strippath", "coordinate", "angle", "fitaabb", "eulerseq",
"meshdir", "texturedir", "discardvisual", "usethread", "fusestatic", "inertiafromgeom",
"inertiagrouprange", "saveinertial", "assetdir", "alignfree", "conflict"},
{"<"},
{"lengthrange", "?", "mode", "useexisting", "uselimit",
"accel", "maxforce", "timeconst", "timestep",
"inttotal", "interval", "tolrange"},
{">"},
{"option", "*",
"timestep", "impratio", "tolerance", "ls_tolerance", "noslip_tolerance",
"ccd_tolerance", "sleep_tolerance", "gravity", "wind", "magnetic", "density", "viscosity",
"o_margin", "o_solref", "o_solimp", "o_friction",
"integrator", "cone", "jacobian",
"solver", "iterations", "ls_iterations", "noslip_iterations", "ccd_iterations",
"sdf_iterations", "sdf_initpoints", "actuatorgroupdisable"},
{"<"},
{"flag", "?", "constraint", "equality", "frictionloss", "limit", "contact",
"spring", "damper", "gravity", "clampctrl", "warmstart", "filterparent", "actuation",
"refsafe", "sensor", "midphase", "eulerdamp", "autoreset", "nativeccd", "island",
"override", "energy", "fwdinv", "invdiscrete", "multiccd", "sleep",
"diagexact"},
{">"},
{"size", "*", "memory", "njmax", "nconmax", "nstack", "nuserdata", "nkey",
"nuser_body", "nuser_jnt", "nuser_geom", "nuser_site", "nuser_cam",
"nuser_tendon", "nuser_actuator", "nuser_sensor"},
{"visual", "*"},
{"<"},
{"global", "?", "cameraid", "orthographic", "fovy", "ipd", "azimuth", "elevation",
"linewidth", "glow", "offwidth", "offheight", "realtime", "ellipsoidinertia",
"bvactive"},
{"quality", "?", "shadowsize", "offsamples", "numslices", "numstacks",
"numquads"},
{"headlight", "?", "ambient", "diffuse", "specular", "active"},
{"map", "?", "stiffness", "stiffnessrot", "force", "torque", "alpha",
"fogstart", "fogend", "znear", "zfar", "haze", "shadowclip", "shadowscale",
"actuatortendon"},
{"scale", "?", "forcewidth", "contactwidth", "contactheight", "connect", "com",
"camera", "light", "selectpoint", "jointlength", "jointwidth", "actuatorlength",
"actuatorwidth", "framelength", "framewidth", "constraint", "slidercrank", "frustum"},
{"rgba", "?", "fog", "haze", "force", "inertia", "joint",
"actuator", "actuatornegative", "actuatorpositive", "com",
"camera", "light", "selectpoint", "connect", "contactpoint", "contactforce",
"contactfriction", "contacttorque", "contactgap", "rangefinder",
"constraint", "slidercrank", "crankbroken", "frustum", "bv", "bvactive"},
{">"},
{"statistic", "*", "meaninertia", "meanmass", "meansize", "extent", "center"},
{"default", "R", "class"},
{"<"},
{"mesh", "?", "scale", "maxhullvert", "inertia"},
{"material", "?", "texture", "emission", "specular", "shininess",
"reflectance", "metallic", "roughness", "rgba", "texrepeat", "texuniform"},
{"<"},
{"layer", "*", "texture", "role"},
{">"},
{"joint", "?", "type", "group", "pos", "axis", "springdamper",
"limited", "actuatorfrclimited", "solreflimit", "solimplimit",
"solreffriction", "solimpfriction", "stiffness", "range", "actuatorfrcrange",
"actuatorgravcomp", "margin", "ref", "springref", "armature", "damping",
"frictionloss", "user"},
{"geom", "?", "type", "pos", "quat", "contype", "conaffinity", "condim",
"group", "priority", "size", "material", "friction", "mass", "density",
"shellinertia", "solmix", "solref", "solimp",
"margin", "gap", "surfacevel", "adhesion", "fromto", "axisangle", "xyaxes", "zaxis",
"euler",
"hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"},
{"site", "?", "type", "group", "pos", "quat", "material",
"size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
{"camera", "?", "projection", "fovy", "ipd", "resolution", "output", "pos", "quat",
"axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel",
"principal", "principalpixel", "sensorsize", "user"},
{"light", "?", "pos", "dir", "bulbradius", "intensity", "range",
"directional", "type", "castshadow", "active", "attenuation", "cutoff", "exponent",
"ambient", "diffuse", "specular", "mode"},
{"pair", "?", "condim", "friction", "solref", "solreffriction", "solimp",
"gap", "margin", "adhesion"},
{"equality", "?", "active", "solref", "solimp"},
{"tendon", "?", "group", "limited", "range",
"solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "width", "material",
"margin", "stiffness", "damping", "rgba", "user"},
{"general", "?", "ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange",
"actrange", "gear", "damping", "armature", "cranklength", "user", "group", "nsample",
"interp", "delay", "actdim", "input", "velrange", "ffrange", "dyntype", "gaintype", "biastype", "dynprm", "gainprm",
"biasprm", "actearly"},
{"motor", "?", "ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"gear", "damping", "armature", "cranklength", "user", "group", "nsample", "interp", "delay"},
{"position", "?", "ctrllimited", "forcelimited", "ctrlrange", "inheritrange", "forcerange",
"gear", "damping", "armature", "cranklength", "user", "group", "nsample", "interp",
"delay", "kp", "kv", "dampratio", "timeconst"},
{"velocity", "?", "ctrllimited", "forcelimited", "ctrlrange", "forcerange", "gear",
"damping", "armature", "cranklength", "user", "group", "nsample", "interp", "delay", "kv"},
{"intvelocity", "?", "ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange",
"actrange", "inheritrange", "gear", "damping", "armature", "cranklength", "user", "group",
"nsample", "interp", "delay", "kp", "kv", "dampratio"},
{"orientation", "?", "forcelimited", "ctrlrange", "forcerange", "user", "group",
"nsample", "interp", "delay", "kp", "kv", "dampratio", "input"},
{"pid", "?", "ctrllimited", "forcelimited", "ctrlrange", "posrange", "velrange", "ffrange",
"forcerange", "inheritrange", "gear", "damping", "armature", "cranklength", "user",
"group", "nsample", "interp", "delay", "kp", "kv", "dampratio", "ki", "imax", "slewmax",
"input"},
{"damper", "?", "forcelimited", "ctrlrange", "forcerange",
"gear", "damping", "armature", "cranklength", "user", "group", "nsample", "interp", "delay", "kv"},
{"cylinder", "?", "ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"gear", "damping", "armature", "cranklength", "user", "group", "nsample", "interp", "delay",
"timeconst", "area", "diameter", "bias"},
{"muscle", "?", "ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"gear", "damping", "armature", "cranklength", "user", "group", "nsample", "interp", "delay",
"timeconst", "range", "force", "scale",
"lmin", "lmax", "vmax", "fpmax", "fvmax"},
{"adhesion", "?", "forcelimited", "ctrlrange", "forcerange",
"gain", "user", "group", "nsample", "interp", "delay"},
{"dcmotor", "?", "ctrllimited", "ctrlrange",
"gear", "damping", "armature", "cranklength", "user", "group", "nsample", "interp", "delay",
"motorconst", "resistance", "nominal", "saturation",
"inductance", "cogging", "controller", "input", "thermal", "lugre"},
{">"},
{"extension", "*"},
{"<"},
{"plugin", "*", "plugin"},
{"<"},
{"instance", "*", "name"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{">"},
{"custom", "*"},
{"<"},
{"numeric", "*", "name", "size", "data"},
{"text", "*", "name", "data"},
{"tuple", "*", "name"},
{"<"},
{"element", "*", "objtype", "objname", "prm"},
{">"},
{">"},
{"asset", "*"},
{"<"},
{"mesh", "*", "name", "class", "content_type", "file", "vertex", "normal",
"texcoord", "face", "refpos", "refquat", "scale", "smoothnormal",
"maxhullvert", "inertia", "builtin", "params", "material"},
{"<"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"hfield", "*", "name", "content_type", "file", "nrow", "ncol", "size", "elevation"},
{"skin", "*", "name", "file", "material", "rgba", "inflate",
"vertex", "texcoord", "face", "group"},
{"<"},
{"bone", "*", "body", "bindpos", "bindquat", "vertid", "vertweight"},
{">"},
{"texture", "*", "name", "type", "colorspace", "content_type", "file", "gridsize",
"gridlayout", "fileright", "fileleft", "fileup", "filedown", "filefront", "fileback",
"builtin", "rgb1", "rgb2", "mark", "markrgb", "random", "width", "height",
"hflip", "vflip", "nchannel"},
{"material", "*", "name", "class", "texture", "texrepeat", "texuniform",
"emission", "specular", "shininess", "reflectance", "metallic", "roughness", "rgba"},
{"<"},
{"layer", "*", "texture", "role"},
{">"},
{"model", "*", "name", "file", "content_type"},
{">"},
{"body", "R", "name", "childclass", "pos", "quat", "mocap", "axisangle",
"xyaxes", "zaxis", "euler", "gravcomp", "sleep", "simple", "user"},
{"<"},
{"inertial", "?", "pos", "quat", "mass", "diaginertia",
"axisangle", "xyaxes", "zaxis", "euler", "fullinertia"},
{"joint", "*", "name", "class", "type", "group", "pos", "axis",
"springdamper", "limited", "actuatorfrclimited",
"solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"stiffness", "range", "actuatorfrcrange", "actuatorgravcomp", "margin", "ref",
"springref", "armature", "damping", "frictionloss", "user"},
{"freejoint", "*", "name", "group", "align"},
{"geom", "*", "name", "class", "type", "contype", "conaffinity", "condim",
"group", "priority", "size", "material", "friction", "mass", "density",
"shellinertia", "solmix", "solref", "solimp",
"margin", "gap", "surfacevel", "adhesion", "fromto", "pos", "quat", "axisangle",
"xyaxes", "zaxis", "euler", "hfield", "mesh", "fitscale", "rgba", "fluidshape",
"fluidcoef", "user"},
{"<"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"attach", "*", "model", "body", "frame", "prefix"},
{"site", "*", "name", "class", "type", "group", "pos", "quat",
"material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
{"camera", "*", "name", "class", "projection", "fovy", "ipd", "resolution", "output", "pos",
"quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target",
"focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"},
{"light", "*", "name", "class", "directional", "type", "castshadow", "active",
"pos", "dir", "bulbradius", "intensity", "range", "attenuation", "cutoff",
"exponent", "ambient", "diffuse", "specular", "mode", "target", "texture"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{"composite", "*", "prefix", "type", "count", "offset",
"vertex", "initial", "curve", "size", "quat"},
{"<"},
{"joint", "*", "kind", "group", "stiffness", "damping", "armature",
"solreffix", "solimpfix", "type", "axis",
"limited", "range", "margin", "solreflimit", "solimplimit",
"frictionloss", "solreffriction", "solimpfriction"},
{"skin", "?", "texcoord", "material", "group", "rgba", "inflate", "subgrid"},
{"geom", "?", "type", "contype", "conaffinity", "condim",
"group", "priority", "size", "material", "rgba", "friction", "mass",
"density", "solmix", "solref", "solimp", "margin", "gap", "surfacevel",
"adhesion"},
{"site", "?", "group", "size", "material", "rgba"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"flexcomp", "*", "name", "type", "group", "dim", "dof",
"count", "cellcount", "spacing", "radius", "rigid", "mass", "inertiabox",
"scale", "file", "point", "element", "texcoord", "material", "rgba",
"flatskin", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "origin"},
{"<"},
{"edge", "?", "equality", "solref", "solimp", "stiffness", "damping"},
{"elasticity", "?", "young", "poisson", "damping", "thickness", "elastic2d"},
{"contact", "?", "contype", "conaffinity", "condim", "priority",
"friction", "solmix", "solref", "solimp", "margin", "gap",
"internal", "selfcollide", "activelayers", "passive"},
{"pin", "*", "id", "range", "grid", "gridrange"},
{"plugin", "*", "plugin", "instance"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{">"},
{"deformable", "*"},
{"<"},
{"flex", "*", "name", "group", "dim", "radius", "material", "rgba", "flatskin", "body",
"vertex", "element", "texcoord", "elemtexcoord", "node", "cellcount", "dof"},
{"<"},
{"contact", "?", "contype", "conaffinity", "condim", "priority",
"friction", "solmix", "solref", "solimp", "margin", "gap",
"internal", "selfcollide", "activelayers", "passive"},
{"edge", "?", "stiffness", "damping"},
{"elasticity", "?", "young", "poisson", "damping", "thickness", "elastic2d"},
{">"},
{"skin", "*", "name", "file", "material", "rgba", "inflate",
"vertex", "texcoord", "face", "group"},
{"<"},
{"bone", "*", "body", "bindpos", "bindquat", "vertid", "vertweight"},
{">"},
{">"},
{"contact", "*"},
{"<"},
{"pair", "*", "name", "class", "geom1", "geom2", "condim", "friction",
"solref", "solreffriction", "solimp", "gap", "margin", "adhesion"},
{"exclude", "*", "name", "body1", "body2"},
{">"},
{"equality", "*"},
{"<"},
{"connect", "*", "name", "class", "body1", "body2", "anchor",
"site1", "site2", "active", "solref", "solimp"},
{"weld", "*", "name", "class", "body1", "body2", "relpose", "anchor",
"site1", "site2", "active", "solref", "solimp", "torquescale"},
{"joint", "*", "name", "class", "joint1", "joint2", "polycoef",
"active", "solref", "solimp"},
{"tendon", "*", "name", "class", "tendon1", "tendon2", "polycoef",
"active", "solref", "solimp"},
{"flex", "*", "name", "class", "flex",
"active", "solref", "solimp"},
{"flexvert", "*", "name", "class", "flex",
"active", "solref", "solimp"},
{"flexstrain", "*", "name", "class", "flex", "cell",
"active", "solref", "solimp"},
{">"},
{"tendon", "*"},
{"<"},
{"spatial", "*", "name", "class", "group", "limited", "actuatorfrclimited", "range",
"actuatorfrcrange", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "width", "material",
"margin", "stiffness", "damping", "armature", "rgba", "user"},
{"<"},
{"site", "*", "site"},
{"geom", "*", "geom", "sidesite"},
{"pulley", "*", "divisor"},
{">"},
{"fixed", "*", "name", "class", "group", "limited", "actuatorfrclimited", "range",
"actuatorfrcrange", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "margin", "stiffness", "damping", "armature", "user"},
{"<"},
{"joint", "*", "joint", "coef"},
{">"},
{">"},
{"actuator", "*"},
{"<"},
{"general", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange", "actrange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"body", "actdim", "input", "velrange", "ffrange", "dyntype", "gaintype", "biastype", "dynprm", "gainprm", "biasprm",
"actearly"},
{"motor", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite"},
{"position", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "ctrlrange", "inheritrange", "forcerange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"kp", "kv", "dampratio", "timeconst"},
{"velocity", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"kv"},
{"intvelocity", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "actlimited",
"ctrlrange", "forcerange", "actrange", "inheritrange", "lengthrange",
"gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"kp", "kv", "dampratio"},
{"orientation", "*", "name", "class", "group", "nsample", "interp", "delay",
"forcelimited", "ctrlrange", "forcerange", "user",
"joint", "site", "refsite",
"kp", "kv", "dampratio", "input"},
{"pid", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "ctrlrange", "posrange", "velrange", "ffrange",
"forcerange", "inheritrange", "lengthrange", "gear", "damping", "armature",
"cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"kp", "kv", "dampratio", "ki", "imax", "slewmax", "input"},
{"damper", "*", "name", "class", "group", "nsample", "interp", "delay",
"forcelimited", "ctrlrange", "forcerange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"kv"},
{"cylinder", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"timeconst", "area", "diameter", "bias"},
{"muscle", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "ctrlrange", "forcerange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite",
"timeconst", "tausmooth", "range", "force", "scale",
"lmin", "lmax", "vmax", "fpmax", "fvmax"},
{"adhesion", "*", "name", "class", "group", "nsample", "interp", "delay",
"forcelimited", "ctrlrange", "forcerange", "user", "body", "gain"},
{"dcmotor", "*", "name", "class", "group", "nsample", "interp", "delay",
"ctrllimited", "ctrlrange",
"lengthrange", "gear", "damping", "armature", "cranklength", "user",
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "refsite",
"motorconst", "resistance", "nominal", "saturation",
"inductance", "cogging", "controller", "thermal", "lugre", "input"},
{"plugin", "*", "name", "class", "plugin", "instance", "group", "nsample", "interp", "delay",
"ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange", "actrange",
"lengthrange", "gear", "damping", "armature", "cranklength", "joint", "jointinparent",
"site", "actdim", "dyntype", "dynprm", "tendon", "cranksite", "slidersite", "user",
"actearly"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"sensor", "*"},
{"<"},
{"touch", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"accelerometer", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"velocimeter", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"gyro", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"force", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"torque", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"magnetometer", "*", "name", "site", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"camprojection", "*", "name", "site", "camera", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"rangefinder", "*", "name", "site", "camera", "data", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"jointpos", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"jointvel", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tendonpos", "*", "name", "tendon", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tendonvel", "*", "name", "tendon", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"actuatorpos", "*", "name", "actuator", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"actuatorvel", "*", "name", "actuator", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"actuatorfrc", "*", "name", "actuator", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"jointactuatorfrc", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tendonactuatorfrc", "*", "name", "tendon", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"ballquat", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"ballangvel", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"jointlimitpos", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"jointlimitvel", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"jointlimitfrc", "*", "name", "joint", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tendonlimitpos", "*", "name", "tendon", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tendonlimitvel", "*", "name", "tendon", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tendonlimitfrc", "*", "name", "tendon", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"framepos", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"framequat", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"framexaxis", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"frameyaxis", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"framezaxis", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"framelinvel", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"frameangvel", "*", "name", "objtype", "objname", "reftype", "refname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"framelinacc", "*", "name", "objtype", "objname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"frameangacc", "*", "name", "objtype", "objname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"subtreecom", "*", "name", "body", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"subtreelinvel", "*", "name", "body", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"subtreeangmom", "*", "name", "body", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"insidesite", "*", "name", "site", "objtype", "objname", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"distance", "*", "name", "geom1", "geom2", "body1", "body2", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"normal", "*", "name", "geom1", "geom2", "body1", "body2", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"fromto", "*", "name", "geom1", "geom2", "body1", "body2", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"contact", "*", "name", "geom1", "geom2", "body1", "body2", "subtree1", "subtree2", "site",
"num", "data", "reduce", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"e_potential", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"e_kinetic", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"clock", "*", "name", "nsample", "interp", "delay", "interval", "cutoff", "noise", "user"},
{"tactile", "*", "name", "geom", "mesh", "nsample", "interp", "delay", "interval", "user"},
{"user", "*", "name", "objtype", "objname", "datatype", "needstage",
"dim", "cutoff", "noise", "user"},
{"plugin", "*", "name", "plugin", "instance", "cutoff", "objtype", "objname", "reftype", "refname",
"user"},
{"<"},
{"config", "*", "key", "value"},
{">"},
{">"},
{"keyframe", "*"},
{"<"},
{"key", "*", "name", "time", "qpos", "qvel", "act", "mpos", "mquat", "ctrl"},
{">"},
{">"}
};
// clang-format on
#include "mjcf_table.inc"
//---------------------------------- MJCF keywords used in attributes ------------------------------
@@ -1121,7 +671,8 @@ const mjMap flexeq_map[4] = {
//---------------------------------- class mjXReader implementation --------------------------------
// constructor
mjXReader::mjXReader() : schema(MJCF, nMJCF) {
mjXReader::mjXReader()
: schema(MJCF, nMJCF, MJCF_constraints, nMJCF_constraints) {
readingdefaults = false;
}
@@ -1541,33 +1092,16 @@ void mjXReader::Size(XMLElement* section, mjSpec* s) {
ReadAttrInt(section, "nconmax", &s->nconmax);
if (s->nconmax < -1) throw mjXError(section, "nconmax must be >= -1");
// memory/nstack and memory/njmax exclusivity is enforced by the schema
{
int nstack = -1;
const bool has_nstack = ReadAttrInt(section, "nstack", &nstack);
if (has_nstack) {
if (s->nstack < -1) {
throw mjXError(section, "nstack must be >= -1");
}
if (s->memory != -1 && nstack != -1) {
throw mjXError(section,
"either 'memory' and 'nstack' attribute can be specified, not both");
}
if (ReadAttrInt(section, "nstack", &nstack)) {
if (nstack < -1) throw mjXError(section, "nstack must be >= -1");
s->nstack = nstack;
}
}
{
int njmax = -1;
const bool has_njmax = ReadAttrInt(section, "njmax", &njmax);
if (has_njmax) {
if (s->njmax < -1) {
throw mjXError(section, "njmax must be >= -1");
}
if (s->memory != -1 && njmax != -1) {
throw mjXError(section,
"either 'memory' and 'njmax' attribute can be specified, not both");
}
s->njmax = njmax;
}
if (ReadAttrInt(section, "njmax", &s->njmax)) {
if (s->njmax < -1) throw mjXError(section, "njmax must be >= -1");
}
ReadAttrInt(section, "nuser_body", &s->nuser_body);
@@ -1794,12 +1328,6 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* mesh, const mjVFS* vfs) {
if (MapValue(elem, "builtin", &n, meshbuiltin_map, meshbuiltin_sz)) {
std::vector<double> params;
int nparams = ReadVector(elem, "params", params, text, /*required*/ true);
if (file) {
throw mjXError(elem, "builtin cannot be used with a mesh file");
}
if (!mesh->uservert->empty()) {
throw mjXError(elem, "builtin mesh cannot be used with user vertex data");
}
if (mjs_makeMesh(mesh, (mjtMeshBuiltin)n, params.data(), nparams)) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
@@ -2166,11 +1694,8 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* camera) {
}
}
bool sensorsize = ReadAttr(elem, "sensorsize", 2, camera->sensor_size, text);
bool fovy = ReadAttr(elem, "fovy", 1, &camera->fovy, text);
if (fovy && sensorsize) {
throw mjXError(elem, "either 'fovy' or 'sensorsize' attribute can be specified, not both");
}
ReadAttr(elem, "sensorsize", 2, camera->sensor_size, text);
ReadAttr(elem, "fovy", 1, &camera->fovy, text);
// read userdata
ReadVector(elem, "user", userdata, text);
@@ -2185,7 +1710,6 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* camera) {
// light element parser
void mjXReader::OneLight(XMLElement* elem, mjsLight* light) {
int n;
bool has_directional = false;
string text, name, texture, targetbody;
// read attributes
@@ -2205,12 +1729,8 @@ void mjXReader::OneLight(XMLElement* elem, mjsLight* light) {
}
if (MapValue(elem, "directional", &n, bool_map, 2)) {
light->type = (n == 1) ? mjLIGHT_DIRECTIONAL : mjLIGHT_SPOT;
has_directional = true;
}
if (MapValue(elem, "type", &n, lighttype_map, lighttype_sz)) {
if (has_directional) {
throw mjXError(elem, "type and directional cannot both be defined");
}
light->type = (mjtLightType)n;
}
if (MapValue(elem, "castshadow", &n, bool_map, 2)) {
@@ -2297,19 +1817,7 @@ void mjXReader::OneEquality(XMLElement* elem, mjsEquality* equality) {
auto maybe_body2 = ReadAttrStr(elem, "body2");
bool has_anchor = ReadAttr(elem, "anchor", 3, equality->data, text);
bool maybe_site = maybe_site1.has_value() || maybe_site2.has_value();
bool maybe_body = maybe_body1.has_value() || maybe_body2.has_value() || has_anchor;
if (maybe_site && maybe_body) {
throw mjXError(elem, "body and site semantics cannot be mixed");
}
bool site_semantic = maybe_site1.has_value() && maybe_site2.has_value();
bool body_semantic = maybe_body1.has_value() && has_anchor;
if (site_semantic == body_semantic) {
throw mjXError(elem, "either both body1 and anchor must be defined,"
" or both site1 and site2 must be defined");
}
if (body_semantic) {
name1 = maybe_body1.value();
@@ -2331,28 +1839,10 @@ void mjXReader::OneEquality(XMLElement* elem, mjsEquality* equality) {
auto maybe_body1 = ReadAttrStr(elem, "body1");
auto maybe_body2 = ReadAttrStr(elem, "body2");
bool has_anchor = ReadAttr(elem, "anchor", 3, equality->data, text);
bool has_relpose = ReadAttr(elem, "relpose", 7, equality->data+3, text);
ReadAttr(elem, "relpose", 7, equality->data+3, text);
bool maybe_site = maybe_site1.has_value() || maybe_site2.has_value();
bool maybe_body = maybe_body1.has_value() ||
maybe_body2.has_value() ||
has_anchor ||
has_relpose;
if (maybe_site && maybe_body) {
throw mjXError(elem, "body and site semantics cannot be mixed");
}
bool site_semantic = maybe_site1.has_value() && maybe_site2.has_value();
bool body_semantic = maybe_body1.has_value();
if (site_semantic == body_semantic) {
throw mjXError(
elem,
"either body1 must be defined and optionally {body2, anchor, relpose},"
" or site1 and site2 must be defined");
}
if (body_semantic) {
name1 = maybe_body1.value();
if (maybe_body2.has_value()) {
@@ -2496,42 +1986,30 @@ void mjXReader::OneActuator(XMLElement* elem, mjsActuator* actuator) {
ReadAttr(elem, "armature", 1, &actuator->armature, text, false, false);
// transmission target and type
int cnt = 0;
if (ReadAttrTxt(elem, "joint", target)) {
mjs_setString(actuator->target, target.c_str());
actuator->trntype = mjTRN_JOINT;
cnt++;
}
if (ReadAttrTxt(elem, "jointinparent", target)) {
mjs_setString(actuator->target, target.c_str());
actuator->trntype = mjTRN_JOINTINPARENT;
cnt++;
}
if (ReadAttrTxt(elem, "tendon", target)) {
mjs_setString(actuator->target, target.c_str());
actuator->trntype = mjTRN_TENDON;
cnt++;
}
if (ReadAttrTxt(elem, "cranksite", target)) {
mjs_setString(actuator->target, target.c_str());
actuator->trntype = mjTRN_SLIDERCRANK;
cnt++;
}
if (ReadAttrTxt(elem, "site", target)) {
mjs_setString(actuator->target, target.c_str());
actuator->trntype = mjTRN_SITE;
cnt++;
}
if (ReadAttrTxt(elem, "body", target)) {
mjs_setString(actuator->target, target.c_str());
actuator->trntype = mjTRN_BODY;
cnt++;
}
// check for repeated transmission
if (cnt > 1) {
throw mjXError(elem, "actuator can have at most one of transmission target");
}
// slidercrank-specific parameters
int r1 = ReadAttr(elem, "cranklength", 1, &actuator->cranklength, text);
int r2 = ReadAttrTxt(elem, "slidersite", slidersite);
@@ -3843,11 +3321,8 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
ReadQuat(elem, "quat", body->iquat, text);
ReadAttr(elem, "mass", 1, &body->mass, text, true);
ReadAttr(elem, "diaginertia", 3, body->inertia, text);
bool alt = ReadAlternative(elem, body->ialt);
bool full = ReadAttr(elem, "fullinertia", 6, body->fullinertia, text);
if (alt && full) {
throw mjXError(elem, "fullinertia and inertial orientation cannot both be specified");
}
ReadAlternative(elem, body->ialt);
ReadAttr(elem, "fullinertia", 6, body->fullinertia, text);
}
// joint sub-element
@@ -4100,9 +3575,6 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
bool has_frame = ReadAttrTxt(elem, "frame", child_name, /*required=*/false);
ReadAttrTxt(elem, "prefix", prefix, /*required=*/true);
if (has_body && has_frame) {
throw mjXError(elem, "only one of body or frame can be specified in attach");
}
mjtObj type = mjOBJ_UNKNOWN;
if (has_body) type = mjOBJ_BODY;
else if (has_frame) type = mjOBJ_FRAME;
@@ -4444,10 +3916,7 @@ void mjXReader::Sensor(XMLElement* section) {
} else if (type == "rangefinder") {
sensor->type = mjSENS_RANGEFINDER;
bool use_site = ReadAttrTxt(elem, "site", objname, false);
bool use_camera = ReadAttrTxt(elem, "camera", objname, false);
if (use_site == use_camera) {
throw mjXError(elem, "rangefinder requires exactly one of 'site' or 'camera'");
}
ReadAttrTxt(elem, "camera", objname, false);
sensor->objtype = use_site ? mjOBJ_SITE : mjOBJ_CAMERA;
// process data specification (intprm[0])
@@ -4559,8 +4028,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "framequat") {
sensor->type = mjSENS_FRAMEQUAT;
@@ -4570,8 +4037,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "framexaxis") {
sensor->type = mjSENS_FRAMEXAXIS;
@@ -4581,8 +4046,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "frameyaxis") {
sensor->type = mjSENS_FRAMEYAXIS;
@@ -4592,8 +4055,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "framezaxis") {
sensor->type = mjSENS_FRAMEZAXIS;
@@ -4603,8 +4064,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "framelinvel") {
sensor->type = mjSENS_FRAMELINVEL;
@@ -4614,8 +4073,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "frameangvel") {
sensor->type = mjSENS_FRAMEANGVEL;
@@ -4625,8 +4082,6 @@ void mjXReader::Sensor(XMLElement* section) {
if (ReadAttrTxt(elem, "reftype", text)) {
sensor->reftype = (mjtObj)mju_str2Type(text.c_str());
ReadAttrTxt(elem, "refname", refname, true);
} else if (ReadAttrTxt(elem, "refname", text)) {
throw mjXError(elem, "refname '%s' given but reftype is missing", text.c_str());
}
} else if (type == "framelinacc") {
sensor->type = mjSENS_FRAMELINACC;
@@ -4665,16 +4120,10 @@ void mjXReader::Sensor(XMLElement* section) {
// sensors for geometric distance; attached to geoms or bodies
else if (type == "distance" || type == "normal" || type == "fromto") {
bool has_body1 = ReadAttrTxt(elem, "body1", objname);
bool has_geom1 = ReadAttrTxt(elem, "geom1", objname);
if (has_body1 == has_geom1) {
throw mjXError(elem, "exactly one of (geom1, body1) must be specified");
}
ReadAttrTxt(elem, "geom1", objname);
sensor->objtype = has_body1 ? mjOBJ_BODY : mjOBJ_GEOM;
bool has_body2 = ReadAttrTxt(elem, "body2", refname);
bool has_geom2 = ReadAttrTxt(elem, "geom2", refname);
if (has_body2 == has_geom2) {
throw mjXError(elem, "exactly one of (geom2, body2) must be specified");
}
ReadAttrTxt(elem, "geom2", refname);
sensor->reftype = has_body2 ? mjOBJ_BODY : mjOBJ_GEOM;
if (type == "distance") {
sensor->type = mjSENS_GEOMDIST;
@@ -4692,9 +4141,6 @@ void mjXReader::Sensor(XMLElement* section) {
bool has_body1 = ReadAttrTxt(elem, "body1", objname);
bool has_subtree1 = ReadAttrTxt(elem, "subtree1", objname);
bool has_geom1 = ReadAttrTxt(elem, "geom1", objname);
if (has_site + has_body1 + has_subtree1 + has_geom1 > 1) {
throw mjXError(elem, "at most one of (geom1, body1, subtree1, site) can be specified");
}
if (has_site) { sensor->objtype = mjOBJ_SITE; }
else if (has_body1) { sensor->objtype = mjOBJ_BODY; }
else if (has_subtree1) { sensor->objtype = mjOBJ_XBODY; }
@@ -4705,9 +4151,6 @@ void mjXReader::Sensor(XMLElement* section) {
bool has_body2 = ReadAttrTxt(elem, "body2", refname);
bool has_subtree2 = ReadAttrTxt(elem, "subtree2", refname);
bool has_geom2 = ReadAttrTxt(elem, "geom2", refname);
if (has_body2 + has_subtree2 + has_geom2 > 1) {
throw mjXError(elem, "at most one of (geom2, body2, subtree2) can be specified");
}
if (has_body2) { sensor->reftype = mjOBJ_BODY; }
else if (has_subtree2) { sensor->reftype = mjOBJ_XBODY; }
else if (has_geom2) { sensor->reftype = mjOBJ_GEOM; }
@@ -4767,14 +4210,9 @@ void mjXReader::Sensor(XMLElement* section) {
// user-defined sensor
else if (type == "user") {
sensor->type = mjSENS_USER;
bool objname_given = ReadAttrTxt(elem, "objname", objname);
ReadAttrTxt(elem, "objname", objname);
if (ReadAttrTxt(elem, "objtype", text)) {
if (!objname_given) {
throw mjXError(elem, "objtype '%s' given but objname is missing", text.c_str());
}
sensor->objtype = (mjtObj)mju_str2Type(text.c_str());
} else if (objname_given) {
throw mjXError(elem, "objname '%s' given but objtype is missing", objname.c_str());
}
ReadAttrInt(elem, "dim", &sensor->dim, true);
+5 -3
View File
@@ -101,8 +101,10 @@ class mjXReader : public mjXBase {
mujoco::user::FilePath texturedir_;
};
// MJCF schema
#define nMJCF 252
extern std::vector<const char*> MJCF[nMJCF];
// MJCF schema table, generated from mjcf.schema into mjcf_table.inc
extern const int nMJCF;
extern std::vector<const char*> MJCF[];
extern const mjXConstraintDef MJCF_constraints[];
extern const int nMJCF_constraints;
#endif // MUJOCO_SRC_XML_XML_NATIVE_READER_H_
+113 -2
View File
@@ -333,11 +333,20 @@ XMLElement* NextSiblingElement(XMLElement* e, const char* name) {
}
// constructor
mjXSchema::mjXSchema(std::vector<const char*> schema[], unsigned nrow) {
mjXSchema::mjXSchema(std::vector<const char*> schema[], unsigned nrow,
const mjXConstraintDef* constraints, int nconstraint,
int first_row) {
// set name and type
name_ = schema[0][0];
type_ = schema[0][1][0];
// adopt the presence constraints declared for this row
for (int i = 0; i < nconstraint; i++) {
if (constraints[i].row == first_row) {
constraints_.push_back(&constraints[i]);
}
}
// set attributes
int nattr = schema[0].size() - 2;
for (int i = 0; i < nattr; i++) {
@@ -370,7 +379,8 @@ mjXSchema::mjXSchema(std::vector<const char*> schema[], unsigned nrow) {
}
// add child element
subschema_.emplace_back(schema+start, end-start+1);
subschema_.emplace_back(schema+start, end-start+1, constraints,
nconstraint, first_row+start);
// proceed with next subelement
start = end+1;
@@ -380,6 +390,102 @@ mjXSchema::mjXSchema(std::vector<const char*> schema[], unsigned nrow) {
// quoted list of constraint bundles: 'a' or ('a', 'b'), comma-joined
static std::string BundleList(const std::vector<std::vector<std::string>>& bundles) {
std::string out;
for (size_t i = 0; i < bundles.size(); i++) {
if (i) {
out += ", ";
}
if (bundles[i].size() == 1) {
out += "'" + bundles[i][0] + "'";
} else {
out += '(';
for (size_t j = 0; j < bundles[i].size(); j++) {
out += (j ? ", '" : "'") + bundles[i][j] + "'";
}
out += ')';
}
}
return out;
}
// enforce the presence constraints declared for this element
XMLElement* mjXSchema::CheckConstraints(XMLElement* elem) {
for (const mjXConstraintDef* con : constraints_) {
// split the spec into bundles of attribute names
std::vector<std::vector<std::string>> bundles(1);
std::string token;
for (const char* c = con->spec;; c++) {
if (*c == ' ' || *c == '|' || *c == '\0') {
if (!token.empty()) {
bundles.back().push_back(token);
token.clear();
}
if (*c == '|') {
bundles.emplace_back();
}
if (*c == '\0') {
break;
}
} else {
token += *c;
}
}
// per-bundle presence: any member present / all members present
int n_any = 0, n_all = 0, n_attr = 0, n_present = 0;
for (const auto& bundle : bundles) {
bool any = false, all = true;
for (const std::string& attr : bundle) {
bool present = elem->Attribute(attr.c_str()) != nullptr;
any |= present;
all &= present;
n_attr++;
n_present += present;
}
n_any += any;
n_all += all;
}
switch (con->kind) {
case 'e': // at most one bundle may be present
if (n_any > 1) {
error = "at most one of " + BundleList(bundles) +
" can be specified";
return elem;
}
break;
case 't': // all listed attributes appear together or not at all
if (n_present != 0 && n_present != n_attr) {
error = "attributes " + BundleList(bundles) +
" must be specified together";
return elem;
}
break;
case 'r': // first attribute requires the second
if (n_any && elem->Attribute(bundles[0][0].c_str()) &&
!elem->Attribute(bundles[1][0].c_str())) {
error = "attribute '" + bundles[0][0] + "' requires attribute '" +
bundles[1][0] + "'";
return elem;
}
break;
case 'o': // at least one bundle must be complete
if (n_all == 0) {
error = "one of " + BundleList(bundles) + " must be specified";
return elem;
}
break;
}
}
return nullptr;
}
// get pointer to error message
std::string mjXSchema::GetError() {
return error;
@@ -525,6 +631,11 @@ XMLElement* mjXSchema::Check(XMLElement* elem, int level) {
}
}
// check presence constraints
if ((bad = CheckConstraints(elem))) {
return bad;
}
// handle recursion
if (type_ == 'R') {
// check child elements with same name
+19 -1
View File
@@ -49,10 +49,25 @@ class [[nodiscard]] mjXError {
};
// presence constraint over an element's attributes, generated from the
// schema into the grammar table's companion array. spec holds attribute
// bundles, space-joined within a bundle and '|'-separated between them;
// kind: e=exclusive (at most one bundle present), t=together (all or
// none), r=requires (first needs second), o=oneof (at least one bundle
// complete).
struct mjXConstraintDef {
int row; // index of the element's row in the grammar table
char kind;
const char* spec;
};
// Custom XML file validation
class mjXSchema {
public:
mjXSchema(std::vector<const char*> schema[], unsigned nrow);
mjXSchema(std::vector<const char*> schema[], unsigned nrow,
const mjXConstraintDef* constraints = nullptr,
int nconstraint = 0, int first_row = 0);
std::string GetError(); // return error
void Print(std::stringstream& str, int level) const; // print schema
@@ -67,6 +82,9 @@ class mjXSchema {
std::set<std::string> attr_; // allowed attributes
std::vector<mjXSchema> subschema_; // allowed child elements
std::vector<const mjXConstraintDef*> constraints_; // constraints here
tinyxml2::XMLElement* CheckConstraints(tinyxml2::XMLElement* elem);
int refcnt_ = 0; // refcount used for validation
std::string error; // error from constructor or Check
};
+43 -3
View File
@@ -24,7 +24,9 @@ _REPO_ROOT = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
sys.path.insert(0, os.path.join(_REPO_ROOT, 'doc', 'generate'))
import generate_api_header
import generate_functions
import generate_mjcf_table
import generate_schema
import mjcf_schema
# Functions in headers that are intentionally not in functions.rst.
_FUNCTIONS_TO_SKIP = set()
@@ -81,6 +83,14 @@ class DocTest(googletest.TestCase):
if source != file.read():
self.fail("The file 'references.h' needs to be updated.")
def test_mjcf_table(self):
"""Checks that mjcf_table.inc matches the schema-generated output."""
table_file = os.path.join(_REPO_ROOT, 'src', 'xml', 'mjcf_table.inc')
source = generate_mjcf_table.generate()
with open(table_file, 'r', encoding='utf-8') as file:
if source != file.read():
self.fail("The file 'mjcf_table.inc' needs to be updated.")
def test_schema(self):
"""Checks that XMLschema.rst matches the generated output."""
schema_file = os.path.join(_REPO_ROOT, 'doc', 'XMLschema.rst')
@@ -91,7 +101,9 @@ class DocTest(googletest.TestCase):
def test_functions(self):
"""Checks that functions.rst matches the generated output."""
functions_file = os.path.join(_REPO_ROOT, 'doc', 'APIreference', 'functions.rst')
functions_file = os.path.join(
_REPO_ROOT, 'doc', 'APIreference', 'functions.rst'
)
source = generate_functions.generate()
with open(functions_file, 'r', encoding='utf-8') as file:
if source != file.read():
@@ -100,7 +112,9 @@ class DocTest(googletest.TestCase):
def test_all_functions_included(self):
"""Checks that every public C function has an entry in functions.rst."""
functions_file = os.path.join(_REPO_ROOT, 'doc', 'APIreference', 'functions.rst')
functions_file = os.path.join(
_REPO_ROOT, 'doc', 'APIreference', 'functions.rst'
)
with open(functions_file, 'r', encoding='utf-8') as file:
content = file.read()
@@ -125,7 +139,9 @@ class DocTest(googletest.TestCase):
def test_all_types_included(self):
"""Checks that every public struct and enum has an entry in APItypes.rst."""
types_file = os.path.join(_REPO_ROOT, 'doc', 'APIreference', 'APItypes.rst')
types_file = os.path.join(
_REPO_ROOT, 'doc', 'APIreference', 'APItypes.rst'
)
with open(types_file, 'r', encoding='utf-8') as file:
content = file.read()
@@ -151,6 +167,30 @@ class DocTest(googletest.TestCase):
msg = 'APItypes.rst mismatches:\n' + '\n'.join(errors)
self.fail(msg)
def test_element_constraints_diamond_inheritance(self):
con = mjcf_schema.Constraint(
kind='exclusive', bundles=(('a',), ('b',)), doc=None, line=1)
common_group = mjcf_schema.Group(
name='common', variant=False, members=[con], doc=None, line=1)
group1 = mjcf_schema.Group(
name='group1', variant=False,
members=[mjcf_schema.Use(group='common', line=1)], doc=None, line=1)
group2 = mjcf_schema.Group(
name='group2', variant=False,
members=[mjcf_schema.Use(group='common', line=1)], doc=None, line=1)
elem = mjcf_schema.Element(
name='elem', spec=None, facets={},
members=[mjcf_schema.Use(group='group1', line=1),
mjcf_schema.Use(group='group2', line=1)],
doc=None, line=1)
schema = mjcf_schema.Schema(
enums={},
groups={'common': common_group, 'group1': group1, 'group2': group2},
elements={'elem': elem},
path='<test>')
cons = generate_mjcf_table._element_constraints(schema, elem)
self.assertEqual(len(cons), 1) # pylint: disable=g-generic-assert
if __name__ == '__main__':
googletest.main()
+2 -2
View File
@@ -698,9 +698,9 @@ TEST_F(SensorTest, BadContact) {
{"geom1='sphere1' geom2='sphere2' num='-3'",
"'num' must be positive in sensor"},
{"geom1='sphere1' geom2='sphere2' site='site'",
"at most one of (geom1, body1, subtree1, site) can be specified"},
"at most one of 'geom1', 'body1', 'subtree1', 'site' can be specified"},
{"geom2='sphere1' body2='body'",
"at most one of (geom2, body2, subtree2) can be specified"},
"at most one of 'geom2', 'body2', 'subtree2' can be specified"},
};
for (const auto& test : test_cases) {
+9 -9
View File
@@ -2461,8 +2461,8 @@ TEST_F(UserObjectsTest, BadConnect) {
EXPECT_THAT(AsVector(m->eq_data, 6), ElementsAre(0, 0, 0, 0, 0, 0));
char error_missing[] =
"either both body1 and anchor must be defined,"
" or both site1 and site2 must be defined\nElement 'connect', line 12";
"one of ('site1', 'site2'), ('body1', 'anchor') must be specified"
"\nElement 'connect', line 12";
// bad model (missing anchor)
xml = base.replace(pos, len, "<connect body1='1'/>");
@@ -2471,8 +2471,8 @@ TEST_F(UserObjectsTest, BadConnect) {
EXPECT_THAT(error, HasSubstr(error_missing));
char error_mixed[] =
"body and site semantics cannot be mixed"
"\nElement 'connect', line 12";
"at most one of ('site1', 'site2'), ('body1', 'body2', 'anchor')"
" can be specified\nElement 'connect', line 12";
// bad model (mixing body and site)
xml = base.replace(pos, len, "<connect body1='1' site1='1'/>");
@@ -2540,8 +2540,8 @@ TEST_F(UserObjectsTest, BadWeld) {
ElementsAre(0, 1, 0, 0, 0, 0, 0, 0, 0, 0));
char error_mixed[] =
"body and site semantics cannot be mixed"
"\nElement 'weld', line 12";
"at most one of ('site1', 'site2'), ('body1', 'body2', 'anchor', "
"'relpose') can be specified\nElement 'weld', line 12";
// bad model (mixing body and site)
xml = base.replace(pos, len, "<weld body1='1' site1='1'/>");
@@ -2568,8 +2568,8 @@ TEST_F(UserObjectsTest, BadWeld) {
EXPECT_THAT(error, HasSubstr(error_mixed));
char error_underspecified[] =
"either body1 must be defined and optionally {body2, anchor, "
"relpose}, or site1 and site2 must be defined\nElement 'weld', line 12";
"one of ('site1', 'site2'), 'body1' must be specified"
"\nElement 'weld', line 12";
// bad model (underspecified body semantics)
xml = base.replace(pos, len, "<weld anchor='0 0 1'/>");
@@ -2663,7 +2663,7 @@ TEST_F(UserObjectsTest, Inertial) {
)";
m = LoadModelFromString(bad_xml2.c_str(), error, sizeof(error));
ASSERT_THAT(m.get(), IsNull());
EXPECT_THAT(error, HasSubstr("fullinertia and inertial orientation cannot"));
EXPECT_THAT(error, HasSubstr("at most one of 'fullinertia', 'quat'"));
}
// Merged COM must be correct when a fused-static child has a non-identity
+16 -8
View File
@@ -2024,7 +2024,7 @@ TEST_F(XMLReaderTest, CameraInvalidFovyAndSensorsize) {
std::array<char, 1024> error;
MjModelPtr m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m.get(), IsNull());
EXPECT_THAT(error.data(), HasSubstr("either 'fovy' or 'sensorsize'"));
EXPECT_THAT(error.data(), HasSubstr("at most one of 'fovy', 'sensorsize'"));
EXPECT_THAT(error.data(), HasSubstr("line 6"));
}
@@ -2081,8 +2081,8 @@ TEST_F(XMLReaderTest, InvalidInertialOrientation) {
ASSERT_THAT(model.get(), IsNull());
EXPECT_THAT(
error.data(),
HasSubstr(
"fullinertia and inertial orientation cannot both be specified"));
HasSubstr("at most one of 'fullinertia', 'quat', 'axisangle', "
"'xyaxes', 'zaxis', 'euler' can be specified"));
}
TEST_F(XMLReaderTest, ReadShellParameter) {
@@ -2137,7 +2137,7 @@ TEST_F(XMLReaderTest, BuiltinAndFile) {
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model.get(), IsNull());
EXPECT_THAT(error.data(),
HasSubstr("builtin mesh cannot be used with user vertex data"));
HasSubstr("at most one of 'builtin', 'vertex' can be specified"));
}
TEST_F(XMLReaderTest, MakePlateNoParameters) {
@@ -2422,7 +2422,9 @@ TEST_F(RelativeFrameSensorParsingTest, RefNameButNoType) {
)";
std::array<char, 1024> error;
LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr("but reftype is missing"));
EXPECT_THAT(
error.data(),
HasSubstr("attributes 'reftype', 'refname' must be specified together"));
EXPECT_THAT(error.data(), HasSubstr("line 8"));
}
@@ -2440,7 +2442,9 @@ TEST_F(RelativeFrameSensorParsingTest, RefTypeButNoName) {
)";
std::array<char, 1024> error;
LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(error.data(), HasSubstr("attribute missing: 'refname'"));
EXPECT_THAT(
error.data(),
HasSubstr("attributes 'reftype', 'refname' must be specified together"));
EXPECT_THAT(error.data(), HasSubstr("line 8"));
}
@@ -3563,7 +3567,9 @@ TEST_F(SensorParseTest, UserObjTypeNoName) {
std::array<char, 1024> error;
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model.get(), IsNull());
EXPECT_THAT(error.data(), HasSubstr("objtype 'site' given but"));
EXPECT_THAT(
error.data(),
HasSubstr("attributes 'objtype', 'objname' must be specified together"));
EXPECT_THAT(error.data(), HasSubstr("line 4"));
}
@@ -3578,7 +3584,9 @@ TEST_F(SensorParseTest, UserObjNameNoType) {
std::array<char, 1024> error;
MjModelPtr model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model.get(), IsNull());
EXPECT_THAT(error.data(), HasSubstr("objname 'kevin' given but"));
EXPECT_THAT(
error.data(),
HasSubstr("attributes 'objtype', 'objname' must be specified together"));
EXPECT_THAT(error.data(), HasSubstr("line 4"));
}