Skip to content

Commit 8d84634

Browse files
committed
refactor(UrdfModel): Convert UrdfModel to TypeScript, replace loops with for-of, and improve type safety
Signed-off-by: Drew Hoener <[email protected]>
1 parent ee520b0 commit 8d84634

File tree

2 files changed

+72
-55
lines changed

2 files changed

+72
-55
lines changed

src/urdf/UrdfModel.ts

Lines changed: 71 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,94 +4,111 @@
44
* @author Russell Toris - [email protected]
55
*/
66

7+
import { DOMParser, MIME_TYPE } from '@xmldom/xmldom';
78
import UrdfMaterial from './UrdfMaterial.js';
89
import UrdfLink from './UrdfLink.js';
910
import UrdfJoint from './UrdfJoint.js';
10-
import { DOMParser, MIME_TYPE } from '@xmldom/xmldom';
11+
import { isElement } from './UrdfUtils.js';
12+
import { UrdfAttrs } from './UrdfTypes.js';
1113

1214
// See https://developer.mozilla.org/docs/XPathResult#Constants
13-
var XPATH_FIRST_ORDERED_NODE_TYPE = 9;
15+
// const XPATH_FIRST_ORDERED_NODE_TYPE = 9;
16+
17+
export interface UrdfModelOptions {
18+
/**
19+
* The XML element to parse.
20+
*/
21+
xml?: Element;
22+
/**
23+
* The XML element to parse as a string.
24+
*/
25+
string: string;
26+
}
1427

1528
/**
1629
* A URDF Model can be used to parse a given URDF into the appropriate elements.
1730
*/
1831
export default class UrdfModel {
19-
materials = {};
20-
links = {};
21-
joints = {};
22-
/**
23-
* @param {Object} options
24-
* @param {Element | null} [options.xml] - The XML element to parse.
25-
* @param {string} [options.string] - The XML element to parse as a string.
26-
*/
27-
constructor(options) {
28-
var xmlDoc = options.xml;
29-
var string = options.string;
32+
33+
name: string | null;
34+
materials: Record<string, UrdfMaterial> = {};
35+
links: Record<string, UrdfLink> = {};
36+
joints: Record<string, UrdfJoint> = {};
37+
38+
constructor({ xml, string }: UrdfModelOptions) {
39+
let xmlDoc = xml;
3040

3141
// Check if we are using a string or an XML element
3242
if (string) {
3343
// Parse the string
34-
var parser = new DOMParser();
35-
xmlDoc = parser.parseFromString(string, MIME_TYPE.XML_TEXT).documentElement;
44+
xmlDoc = new DOMParser().parseFromString(string, MIME_TYPE.XML_TEXT).documentElement;
3645
}
46+
3747
if (!xmlDoc) {
3848
throw new Error('No URDF document parsed!');
3949
}
4050

41-
// Initialize the model with the given XML node.
42-
// Get the robot tag
43-
var robotXml = xmlDoc;
44-
4551
// Get the robot name
46-
this.name = robotXml.getAttribute('name');
52+
this.name = xmlDoc.getAttribute(UrdfAttrs.Name);
4753

54+
const childNodes = xmlDoc.childNodes;
4855
// Parse all the visual elements we need
49-
for (var nodes = robotXml.childNodes, i = 0; i < nodes.length; i++) {
50-
/** @type {Element} */
51-
// @ts-expect-error -- unknown why this doesn't work properly.
52-
var node = nodes[i];
53-
if (node.tagName === 'material') {
54-
var material = new UrdfMaterial({
55-
xml: node
56-
});
57-
// Make sure this is unique
58-
if (this.materials[material.name] !== void 0) {
56+
for (const node of childNodes) {
57+
58+
// Safety check to make sure we're working with an element.
59+
if (!isElement(node)) {
60+
continue;
61+
}
62+
63+
switch (node.tagName) {
64+
case 'material': {
65+
const material = new UrdfMaterial({ xml: node });
66+
// Make sure this is unique
67+
if (!Object.hasOwn(this.materials, material.name)) {
68+
this.materials[material.name] = material;
69+
break;
70+
}
71+
5972
if (this.materials[material.name].isLink()) {
6073
this.materials[material.name].assign(material);
6174
} else {
62-
console.warn('Material ' + material.name + 'is not unique.');
75+
console.warn(`Material ${material.name} is not unique.`);
6376
}
64-
} else {
65-
this.materials[material.name] = material;
77+
78+
break;
6679
}
67-
} else if (node.tagName === 'link') {
68-
var link = new UrdfLink({
69-
xml: node
70-
});
71-
// Make sure this is unique
72-
if (this.links[link.name] !== void 0) {
73-
console.warn('Link ' + link.name + ' is not unique.');
74-
} else {
80+
case 'link': {
81+
const link = new UrdfLink({ xml: node });
82+
// Make sure this is unique
83+
if (Object.hasOwn(this.links, link.name)) {
84+
console.warn(`Link ${link.name} is not unique.`);
85+
break;
86+
}
87+
7588
// Check for a material
76-
for (var j = 0; j < link.visuals.length; j++) {
77-
var mat = link.visuals[j].material;
78-
if (mat !== null && mat.name) {
79-
if (this.materials[mat.name] !== void 0) {
80-
link.visuals[j].material = this.materials[mat.name];
81-
} else {
82-
this.materials[mat.name] = mat;
83-
}
89+
for (const item of link.visuals) {
90+
const mat = item.material;
91+
if (!mat?.name) {
92+
continue;
93+
}
94+
95+
if (Object.hasOwn(this.materials, mat.name)) {
96+
item.material = this.materials[mat.name];
97+
} else {
98+
this.materials[mat.name] = mat;
8499
}
85100
}
86101

87102
// Add the link
88103
this.links[link.name] = link;
104+
105+
break;
106+
}
107+
case 'joint': {
108+
const joint = new UrdfJoint({ xml: node });
109+
this.joints[joint.name] = joint;
110+
break;
89111
}
90-
} else if (node.tagName === 'joint') {
91-
var joint = new UrdfJoint({
92-
xml: node
93-
});
94-
this.joints[joint.name] = joint;
95112
}
96113
}
97114
}

src/urdf/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export { default as UrdfCylinder } from './UrdfCylinder.js';
44
export { default as UrdfLink } from './UrdfLink.js';
55
export { default as UrdfMaterial } from './UrdfMaterial.js';
66
export { default as UrdfMesh } from './UrdfMesh.js';
7-
export { default as UrdfModel } from './UrdfModel.js';
7+
export { default as UrdfModel, type UrdfModelOptions } from './UrdfModel.js';
88
export { default as UrdfSphere } from './UrdfSphere.js';
99
export { default as UrdfVisual, type UrdfGeometryLike } from './UrdfVisual.js';
1010

0 commit comments

Comments
 (0)