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:
committed by
Copybara-Service
parent
3f8db4c17a
commit
790f8fac30
+645
-647
File diff suppressed because it is too large
Load Diff
@@ -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())
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user