1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
// Copyright 2019 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.
using System;
using System.Linq;
using System.Xml;
using UnityEngine;
namespace Mujoco {
// This class helps us manipulate MuJoCo-bound XML files. It is only intended to be used
// within the context of MuJoCo - elsewhere the preferred format is JSON.
public static class XmlElementExtensions {
public static bool GetBoolAttribute(
this XmlElement element, string name, bool defaultValue = false) {
if (!element.HasAttribute(name)) {
return defaultValue;
}
var strValue = element.GetAttribute(name);
bool parsedValue;
if (bool.TryParse(strValue, out parsedValue)) {
return parsedValue;
} else {
throw new ArgumentException($"'{strValue}' is not a bool.");
}
}
public static float GetFloatAttribute(
this XmlElement element, string name, float defaultValue = 0.0f) {
if (!element.HasAttribute(name)) {
return defaultValue;
}
var strValue = element.GetAttribute(name);
float parsedValue;
if (float.TryParse(strValue, out parsedValue)) {
return parsedValue;
} else {
throw new ArgumentException($"'{strValue}' is not a float.");
}
}
// The MuJoCo parser is case-sensitive.
public static T GetEnumAttribute<T>(
this XmlElement element, string name, T defaultValue,
bool ignoreCase = false) where T : struct, IConvertible {
if (!typeof(T).IsEnum) {
throw new ArgumentException("T must be an enumerated type.");
}
if (!element.HasAttribute(name)) {
return defaultValue;
}
var strValue = element.GetAttribute(name);
T parsedValue;
if (Enum.TryParse<T>(strValue, ignoreCase, out parsedValue)) {
return parsedValue;
} else {
throw new ArgumentException(
$"Reading attribute {name}: '{strValue}' is not a value of enum {typeof(T)}.");
}
}
public static string GetStringAttribute(
this XmlElement element, string name, string defaultValue = null) {
if (!element.HasAttribute(name)) {
return defaultValue;
}
return element.GetAttribute(name);
}
public static T GetObjectReferenceAttribute<T>(
this XmlElement mjcf, string attributeName) where T : MjComponent {
var objectName = mjcf.GetStringAttribute(attributeName);
if (string.IsNullOrEmpty(objectName)) {
return null;
}
T foundObject = MjHierarchyTool.FindComponentOfTypeAndName<T>(objectName);
if (foundObject == null) {
throw new ArgumentException(
$"Object {objectName} of type {typeof(T).ToString()} referred by {attributeName} " +
"doesn't exist.");
}
return foundObject;
}
public static Vector2 GetVector2Attribute(
this XmlElement element, string name, Vector2 defaultValue) {
var defaultValues = new float[] {defaultValue.x, defaultValue.y};
var components = element.GetFloatArrayAttribute(name, defaultValues, fillMissingValues: false);
if (components.Length != 2) {
throw new ArgumentException("Invalid Vector2 string representation.");
}
return new Vector2(components[0], components[1]);
}
public static Vector3 GetVector3Attribute(
this XmlElement element, string name, Vector3 defaultValue) {
var defaultValues = new float[] {
defaultValue.x, defaultValue.y, defaultValue.z};
var components = element.GetFloatArrayAttribute(name, defaultValues, fillMissingValues: false);
if (components.Length != 3) {
throw new ArgumentException("Invalid Vector3 string representation.");
}
return new Vector3(components[0], components[1], components[2]);
}
public static Quaternion GetQuaternionAttribute(
this XmlElement element, string name, Quaternion defaultValue) {
var mjDefault = new float[] {
defaultValue.w, defaultValue.x, defaultValue.y, defaultValue.z };
var components = element.GetFloatArrayAttribute(name, mjDefault, fillMissingValues: false);
if (components.Length != 4) {
throw new ArgumentException("Invalid Quaternion string representation.");
}
return new Quaternion(w:components[0], x:components[1], y:components[2], z:components[3]);
}
// Parses an array of whitespace separated floating points.
//
// Args:
// . element: XmlElement that contains the attribute to be parsed.
// . name: Name of the attribute to be parsed.
// . defaultValue: An array of floats, or null. A default value to be returned in case the
// the attribute is missing.
// . fillMissingValues: If a default value was provided, and it has more components than the value
// parsed from the attribute, the missing components will be copied from the defaultValue.
public static float[] GetFloatArrayAttribute(
this XmlElement element, string name, float[] defaultValue, bool fillMissingValues = true) {
if (!element.HasAttribute(name)) {
return defaultValue;
}
var strValue = element.GetAttribute(name);
var components = strValue.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
var resultLength = components.Length;
if (fillMissingValues && defaultValue != null) {
// If filling of the missing values was enabled, and a default value was provided,
// allocate an array large enough to store a value of this length, in case when the parsed
// value has fewer components.
resultLength = Math.Max(resultLength, defaultValue.Length);
}
var result = new float[resultLength];
for (var i = 0; i < components.Length; ++i) {
float componentValue;
if (float.TryParse(components[i], out componentValue)) {
result[i] = componentValue;
} else {
throw new ArgumentException($"'{components[i]}' is not a float.");
}
}
for (var i = components.Length; i < resultLength; ++i) {
// Fill the missing values with defaults.
result[i] = defaultValue[i];
}
return result;
}
}
}
|