Skip to content

Commit 4a3654b

Browse files
committed
Add loader class
1 parent 54db1a1 commit 4a3654b

File tree

4 files changed

+132
-123
lines changed

4 files changed

+132
-123
lines changed

src/controls.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
import { GUI } from "dat.gui";
22
import { Color } from "three";
3+
import { URDFJoint } from "urdf-loader";
34

45
interface Joints {
5-
[name: string]: {
6-
limit: {
7-
lower: number,
8-
upper: number
9-
},
10-
jointType: string,
11-
jointValue: Array<1>
12-
}
6+
[name: string]: URDFJoint
137
}
148

159
export class URDFControls extends GUI {
@@ -113,8 +107,8 @@ export class URDFControls extends GUI {
113107
return;
114108
}
115109

116-
const limitMin = joints[name].limit.lower;
117-
const limitMax = joints[name].limit.upper;
110+
const limitMin = Number(joints[name].limit.lower);
111+
const limitMax = Number(joints[name].limit.upper);
118112

119113
// Skip joint if the limits are not defined
120114
if ( limitMin === 0 && limitMax === 0 ) {

src/layout.ts

Lines changed: 31 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
11
import { Message } from '@lumino/messaging';
22
import { PanelLayout, Widget } from '@lumino/widgets';
3+
import { DocumentRegistry, DocumentModel } from '@jupyterlab/docregistry';
34

4-
import {
5-
DocumentRegistry,
6-
DocumentModel
7-
} from '@jupyterlab/docregistry';
8-
9-
import { PageConfig } from '@jupyterlab/coreutils';
10-
11-
import {
12-
LoadingManager,
13-
Vector2,
14-
Color
15-
} from 'three';
16-
17-
import URDFLoader from 'urdf-loader';
18-
import { XacroLoader } from 'xacro-parser';
19-
5+
import { Vector2, Color } from 'three';
206
import { URDFControls } from './controls';
217
import { URDFRenderer } from './renderer';
8+
import { URDFLoadingManager } from './robot';
229

2310
interface URDFColors {
2411
sky: Color,
@@ -30,14 +17,10 @@ interface URDFColors {
3017
*/
3118
export class URDFLayout extends PanelLayout {
3219
private _host: HTMLElement;
33-
private _robotModel: any = null;
3420
private _controlsPanel: URDFControls;
3521
private _renderer: URDFRenderer;
3622
private _colors: URDFColors;
37-
private _manager: LoadingManager;
38-
private _loader: URDFLoader;
39-
private _workingPath: string;
40-
private _urdfString: string;
23+
private _loader: URDFLoadingManager;
4124

4225
/**
4326
* Construct a `URDFLayout`
@@ -56,11 +39,7 @@ export class URDFLayout extends PanelLayout {
5639

5740
this._renderer = new URDFRenderer(this._colors.sky, this._colors.ground);
5841
this._controlsPanel = new URDFControls();
59-
60-
this._urdfString = '';
61-
this._workingPath = '';
62-
this._manager = new LoadingManager;
63-
this._loader = new URDFLoader(this._manager);
42+
this._loader = new URDFLoadingManager();
6443
}
6544

6645
/**
@@ -98,62 +77,25 @@ export class URDFLayout extends PanelLayout {
9877
}
9978

10079
updateURDF(urdfString: string): void {
101-
this._robotModel = this._loader.parse(urdfString);
102-
this._robotModel.rotation.x = -Math.PI / 2;
103-
this._renderer.setRobot(this._robotModel);
80+
this._loader.setRobot(urdfString);
81+
this._renderer.setRobot(this._loader.robotModel);
10482
}
10583

10684
setURDF(context: DocumentRegistry.IContext<DocumentModel>): void {
10785
// Default to parent directory of URDF file
108-
if (!this._workingPath) {
109-
const filePath = context.path;
110-
const parentDir = filePath.substring(0, filePath.lastIndexOf('/'));
111-
this.changeWorkingPath(parentDir);
112-
}
113-
114-
this._urdfString = context.model.toString();
115-
116-
let robotXML;
117-
118-
119-
if (context.path.endsWith('xacro')) {
120-
const xacroLoader: any = new XacroLoader();
121-
xacroLoader.workingPath = PageConfig.getBaseUrl() + 'files/';
122-
123-
xacroLoader.parse(
124-
context.model.toString(),
125-
(xml: XMLDocument) => {
126-
robotXML = xml;
127-
console.log("XML", xml);
128-
this._robotModel = this._loader.parse(robotXML);
129-
this._robotModel.rotation.x = -Math.PI / 2;
130-
131-
this._renderer.setRobot(this._robotModel);
132-
this._setControls();
133-
134-
},
135-
(error: Error) => console.log(error)
136-
);
137-
} else {
138-
139-
// Load robot model
140-
this._robotModel = this._loader.parse(context.model.toString());
141-
142-
143-
this._robotModel.rotation.x = -Math.PI / 2;
86+
const filePath = context.path;
87+
const parentDir = filePath.substring(0, filePath.lastIndexOf('/'));
88+
this._loader.setWorkingPath(parentDir);
14489

145-
// TODO: redundant but necessary for files without any meshes
146-
this._renderer.setRobot(this._robotModel);
147-
148-
this._manager.onLoad = () => {
149-
this._renderer.setRobot(this._robotModel);
150-
};
151-
152-
this._renderer.setSize(
153-
this._renderer.domElement.clientWidth,
154-
this._renderer.domElement.clientHeight
155-
);
90+
this._loader.onLoad = () => {
91+
this._renderer.setRobot(this._loader.robotModel);
92+
this._setControls();
93+
};
15694

95+
this._loader.setRobot(context.model.toString());
96+
97+
if (this._loader.isReady) {
98+
this._renderer.setRobot(this._loader.robotModel);
15799
this._setControls();
158100
}
159101
}
@@ -188,7 +130,7 @@ export class URDFLayout extends PanelLayout {
188130
* Set the callback functions for each of item in the controls panel
189131
*/
190132
private _setControls(): void {
191-
if (!this._robotModel) return;
133+
if (!this._loader.isReady) return;
192134

193135
this._setPathControls();
194136
this._setSceneControls();
@@ -200,12 +142,12 @@ export class URDFLayout extends PanelLayout {
200142
* render again.
201143
*/
202144
private _setPathControls(): void {
203-
const pathControl = this._controlsPanel.createWorkspaceControls(this._workingPath);
145+
const pathControl = this._controlsPanel
146+
.createWorkspaceControls(this._loader.workingPath);
204147
pathControl.onChange(
205148
(newPath: string = pathControl.object['Path']) => {
206-
this.changeWorkingPath(newPath);
207-
this.updateURDF(this._urdfString);
208-
this._renderer.redraw();
149+
this._loader.setWorkingPath(newPath);
150+
this.updateURDF('');
209151
}
210152
);
211153
}
@@ -228,7 +170,8 @@ export class URDFLayout extends PanelLayout {
228170
* Set callback for each joint when the value changes in the controls panel.
229171
*/
230172
private _setJointControls(): void {
231-
const jointControl = this._controlsPanel.createJointControls(this._robotModel.joints);
173+
const jointControl = this._controlsPanel
174+
.createJointControls(this._loader.robotModel.joints);
232175
Object.keys(jointControl).forEach(
233176
(jointName: string) => {
234177
jointControl[jointName].onChange(
@@ -244,36 +187,8 @@ export class URDFLayout extends PanelLayout {
244187
* @param jointName - The name of the joint to be set
245188
*/
246189
private _setJointValue(jointName: string, newValue: number): void {
247-
this._robotModel.setJointValue(jointName, newValue);
248-
this._renderer.redraw();
249-
}
250-
251-
/**
252-
* Changes the path to find mesh files described in the URDF
253-
*
254-
* @param workingPath Directory path containing robot description folders
255-
*/
256-
changeWorkingPath(workingPath: string): void {
257-
if (!workingPath) return;
258-
259-
// To match '/this/format/path'
260-
workingPath = (workingPath[0] !== '/') ? ('/' + workingPath) : workingPath;
261-
workingPath = (workingPath[workingPath.length - 1] === '/') ?
262-
workingPath.slice(0, -1) : workingPath;
263-
264-
console.debug('[Manager]: Modify URL with prefix ', workingPath);
265-
this._workingPath = workingPath;
266-
267-
this._manager.setURLModifier((url: string) => {
268-
if (url.startsWith(workingPath)) {
269-
console.debug('[Loader]:', url);
270-
return '/files' + url;
271-
} else {
272-
const modifiedURL = '/files' + workingPath + url;
273-
console.debug('[Loader]:', modifiedURL);
274-
return modifiedURL;
275-
}
276-
});
190+
this._loader.robotModel.setJointValue(jointName, newValue);
191+
this._renderer.setRobot(this._loader.robotModel);
277192
}
278193

279194
/**
@@ -303,6 +218,10 @@ export class URDFLayout extends PanelLayout {
303218
protected onAfterAttach(msg: Message): void {
304219
this._renderer.redraw();
305220
this._host.appendChild(this._renderer.domElement);
221+
this._renderer.setSize(
222+
this._renderer.domElement.clientWidth,
223+
this._renderer.domElement.clientHeight
224+
);
306225
this._host.appendChild(this._controlsPanel.domElement);
307226
}
308227

src/renderer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as THREE from 'three';
22
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
3+
import { URDFRobot } from 'urdf-loader';
34

45
/**
56
* THREE.js ROS URDF
@@ -146,7 +147,7 @@ export class URDFRenderer extends THREE.WebGLRenderer {
146147
this.redraw();
147148
}
148149

149-
setRobot(robot: any): void {
150+
setRobot(robot: URDFRobot): void {
150151
if (this._robotIndex < 0) {
151152
this._scene.add(robot);
152153
this._robotIndex = this._scene.children.map(i => i.name).indexOf(robot.name);

src/robot.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import URDFLoader, { URDFRobot } from 'urdf-loader';
2+
import { XacroLoader } from 'xacro-parser';
3+
import { PageConfig } from '@jupyterlab/coreutils';
4+
import { LoadingManager } from 'three';
5+
6+
/**
7+
* THREE.js ROS URDF
8+
* Y Z
9+
* | | Y
10+
* | | /
11+
* .-----X .-----X
12+
* /
13+
* Z
14+
*/
15+
16+
class XacroLoaderWithPath extends XacroLoader {
17+
workingPath = '';
18+
19+
constructor() {
20+
super();
21+
}
22+
}
23+
24+
export class URDFLoadingManager extends LoadingManager {
25+
private _urdfLoader: URDFLoader;
26+
private _xacroLoader: XacroLoaderWithPath;
27+
private _workingPath: string = '';
28+
private _robotString: string = '';
29+
private _robotModel = {} as URDFRobot;
30+
private _isReady = false;
31+
32+
constructor() {
33+
super();
34+
this._urdfLoader = new URDFLoader(this);
35+
this._xacroLoader = new XacroLoaderWithPath();
36+
}
37+
38+
setWorkingPath(workingPath: string): void {
39+
// To match '/this/format/path'
40+
workingPath = (workingPath[0] !== '/') ? ('/' + workingPath) : workingPath;
41+
workingPath = (workingPath[workingPath.length - 1] === '/') ?
42+
workingPath.slice(0, -1) : workingPath;
43+
44+
console.debug('[Manager]: Modify URL with prefix ', workingPath);
45+
this._workingPath = workingPath;
46+
47+
this.setURLModifier((url: string) => {
48+
if (url.startsWith(this._workingPath)) {
49+
console.debug('[Loader]:', url);
50+
return '/files' + url;
51+
} else {
52+
const modifiedURL = '/files' + this._workingPath + url;
53+
console.debug('[Loader]:', modifiedURL);
54+
return modifiedURL;
55+
}
56+
});
57+
58+
// TODO: needs review
59+
this._xacroLoader.workingPath = PageConfig.getBaseUrl()
60+
+ '/files' + this._workingPath;
61+
}
62+
63+
setRobot(robotString: string = ''): void {
64+
this._robotString = robotString || this._robotString;
65+
66+
if (robotString.includes('xacro')) {
67+
this._xacroLoader.parse(
68+
this._robotString,
69+
(xml: XMLDocument) => {
70+
this._robotModel = this._urdfLoader.parse(xml);
71+
this._robotModel.rotation.x = -Math.PI / 2;
72+
},
73+
(err: Error) => console.error(err)
74+
)
75+
} else {
76+
this._robotModel = this._urdfLoader.parse(this._robotString);
77+
this._robotModel.rotation.x = -Math.PI / 2;
78+
}
79+
80+
}
81+
82+
get robotModel() {
83+
return this._robotModel;
84+
}
85+
86+
get workingPath() {
87+
return this._workingPath;
88+
}
89+
90+
get isReady() {
91+
this._isReady = !(Object.keys(this._robotModel).length === 0);
92+
return this._isReady;
93+
}
94+
95+
}

0 commit comments

Comments
 (0)