urdf-loader
Advanced tools
Comparing version 0.3.5 to 0.4.0
@@ -7,2 +7,19 @@ # Changelog | ||
## Unreleased | ||
### Changed | ||
- Add "path" variable to parse function signature | ||
- URDF paths are no longer resolved relative to the package path | ||
- `parse` function signature changed | ||
- `parse` returns the robot now | ||
- `meshLoadCb` and `fetchOptions` have been moved into an `options` argument object | ||
- Moved all fields from `Object3D.urdf` to the object itself | ||
- Changed `type` to `jointType` | ||
### Added | ||
- Add `isURDFRobot`, `isURDFJoint`, `isURDFLink` fields to the robot, joints, and links | ||
- Set the `type` field of the `Object3D` to `URDFRobot`, `URDFJoint`, and `URDFLink` | ||
### Removed | ||
- `node` field from the urdf info on joints, links, and the robot | ||
## [0.3.5] - 2018-08-07 | ||
@@ -9,0 +26,0 @@ ### Added |
@@ -1,2 +0,2 @@ | ||
/* globals animToggle viewer THREE */ | ||
/* globals viewer THREE */ | ||
@@ -7,3 +7,2 @@ // declare these globally for the sake of the example. | ||
viewer = document.querySelector('urdf-viewer'); | ||
animToggle = document.getElementById('do-animate'); | ||
@@ -15,3 +14,3 @@ const limitsToggle = document.getElementById('ignore-joint-limits'); | ||
const controlsToggle = document.getElementById('toggle-controls'); | ||
const DEG2RAD = Math.PI / 180; | ||
var DEG2RAD = Math.PI / 180; | ||
const RAD2DEG = 1 / DEG2RAD; | ||
@@ -28,48 +27,4 @@ let sliders = {}; | ||
// Functions | ||
const lerp = (from, to, ratio) => from + (to - from) * ratio; | ||
const updateAngles = () => { | ||
if (!viewer.setAngle) return; | ||
// reset everything to 0 first | ||
const resetangles = viewer.angles; | ||
for (const name in resetangles) resetangles[name] = 0; | ||
viewer.setAngles(resetangles); | ||
// animate the legs | ||
const time = Date.now() / 3e2; | ||
for (let i = 1; i <= 6; i++) { | ||
const offset = i * Math.PI / 3; | ||
const ratio = Math.max(0, Math.sin(time + offset)); | ||
viewer.setAngle(`HP${ i }`, lerp(30, 0, ratio) * DEG2RAD); | ||
viewer.setAngle(`KP${ i }`, lerp(90, 150, ratio) * DEG2RAD); | ||
viewer.setAngle(`AP${ i }`, lerp(-30, -60, ratio) * DEG2RAD); | ||
viewer.setAngle(`TC${ i }A`, lerp(0, 0.065, ratio)); | ||
viewer.setAngle(`TC${ i }B`, lerp(0, 0.065, ratio)); | ||
viewer.setAngle(`W${ i }`, window.performance.now() * 0.001); | ||
} | ||
}; | ||
const updateLoop = () => { | ||
if (animToggle.classList.contains('checked')) { | ||
updateAngles(); | ||
} | ||
requestAnimationFrame(updateLoop); | ||
}; | ||
// Events | ||
// toggle checkbox | ||
animToggle.addEventListener('click', () => animToggle.classList.toggle('checked')); | ||
limitsToggle.addEventListener('click', () => { | ||
@@ -124,4 +79,2 @@ limitsToggle.classList.toggle('checked'); | ||
// stop the animation if user tried to manipulate the model | ||
animToggle.classList.remove('checked'); | ||
const j = document.querySelector(`li[joint-name="${ e.detail }"]`); | ||
@@ -140,3 +93,3 @@ if (j) { | ||
Object | ||
.keys(r.urdf.joints) | ||
.keys(r.joints) | ||
.sort((a, b) => { | ||
@@ -157,3 +110,3 @@ | ||
}) | ||
.map(key => r.urdf.joints[key]) | ||
.map(key => r.joints[key]) | ||
.forEach(joint => { | ||
@@ -164,8 +117,8 @@ | ||
` | ||
<span title="${ joint.urdf.name }">${ joint.urdf.name }</span> | ||
<span title="${ joint.name }">${ joint.name }</span> | ||
<input type="range" value="0" step="0.0001"/> | ||
<input type="number" step="0.0001" /> | ||
`; | ||
li.setAttribute('joint-type', joint.urdf.type); | ||
li.setAttribute('joint-name', joint.urdf.name); | ||
li.setAttribute('joint-type', joint.jointType); | ||
li.setAttribute('joint-name', joint.name); | ||
@@ -178,5 +131,5 @@ sliderList.appendChild(li); | ||
li.update = () => { | ||
let degVal = joint.urdf.angle; | ||
let degVal = joint.angle; | ||
if (joint.urdf.type === 'revolute' || joint.urdf.type === 'continuous') { | ||
if (joint.jointType === 'revolute' || joint.jointType === 'continuous') { | ||
degVal *= RAD2DEG; | ||
@@ -194,5 +147,5 @@ } | ||
// directly input the value | ||
slider.value = joint.urdf.angle; | ||
slider.value = joint.angle; | ||
if (viewer.ignoreLimits || joint.urdf.type === 'continuous') { | ||
if (viewer.ignoreLimits || joint.jointType === 'continuous') { | ||
slider.min = -6.28; | ||
@@ -204,11 +157,11 @@ slider.max = 6.28; | ||
} else { | ||
slider.min = joint.urdf.limit.lower; | ||
slider.max = joint.urdf.limit.upper; | ||
slider.min = joint.limit.lower; | ||
slider.max = joint.limit.upper; | ||
input.min = joint.urdf.limit.lower * RAD2DEG; | ||
input.max = joint.urdf.limit.upper * RAD2DEG; | ||
input.min = joint.limit.lower * RAD2DEG; | ||
input.max = joint.limit.upper * RAD2DEG; | ||
} | ||
}; | ||
switch (joint.urdf.type) { | ||
switch (joint.jointType) { | ||
@@ -251,3 +204,2 @@ case 'continuous': | ||
viewer.addEventListener('urdf-processed', e => updateAngles()); | ||
document.querySelector('li[urdf]').dispatchEvent(new Event('click')); | ||
@@ -259,3 +211,2 @@ | ||
updateLoop(); | ||
}); |
{ | ||
"name": "urdf-loader", | ||
"version": "0.3.5", | ||
"version": "0.4.0", | ||
"description": "URDF Loader for THREE.js and webcomponent viewer", | ||
"scripts": { | ||
"start": "cd .. && static-server", | ||
"build": "cd example && webpack" | ||
"build": "cd example && webpack", | ||
"test": "jest", | ||
"lint": "eslint *.js ./test/*.js", | ||
"coverage": "nyc report --reporter=html & opn coverage/index.html" | ||
}, | ||
"files": [ | ||
"URDFLoader.js", | ||
"urdf-viewer-element.js", | ||
"urdf-manipulator-element.js", | ||
"example/index.js", | ||
"example/dragAndDrop.js" | ||
], | ||
"license": "Apache-2.0", | ||
@@ -32,2 +42,9 @@ "repository": { | ||
"@webcomponents/webcomponentsjs": "^2.0.0", | ||
"eslint": "^5.3.0", | ||
"jest": "^23.5.0", | ||
"jest-cli": "^23.5.0", | ||
"nyc": "^12.0.2", | ||
"opn-cli": "^3.1.0", | ||
"puppeteer": "^1.7.0", | ||
"puppeteer-to-istanbul": "^1.2.2", | ||
"script-loader": "^0.7.0", | ||
@@ -34,0 +51,0 @@ "static-server": "^3.0.0", |
144
README.md
@@ -1,3 +0,13 @@ | ||
# javascript urdf-loader [![npm version](https://badge.fury.io/js/urdf-loader.svg)](https://www.npmjs.com/package/urdf-loader) | ||
# javascript urdf-loader | ||
[ | ||
![npm version](https://img.shields.io/npm/v/urdf-loader.svg?style=flat-square) | ||
](https://www.npmjs.com/package/urdf-loader) | ||
[ | ||
![travis build](https://img.shields.io/travis/gkjohnson/urdf-loaders.svg?style=flat-square) | ||
](https://travis-ci.org/gkjohnson/urdf-loaders) | ||
[ | ||
![lgtm code quality](https://img.shields.io/lgtm/grade/javascript/g/gkjohnson/urdf-loaders.svg?style=flat-square&label=code-quality) | ||
](https://lgtm.com/projects/g/gkjohnson/urdf-loaders/) | ||
Utilities for loading URDF files into THREE.js and a Web Component that loads and renders the model. | ||
@@ -9,3 +19,3 @@ | ||
## URDFLoader | ||
## Use | ||
```html | ||
@@ -29,22 +39,30 @@ <script src=".../URDFLoader.js"></script> | ||
### API | ||
#### URDFLoader(manager) | ||
## URDFLoader API | ||
### constructor(manager) | ||
Constructor | ||
##### manager | ||
#### manager : THREE.LoadingManager | ||
THREE.LoadingManager. Used for transforming load URLs. | ||
#### URDFLoader.load(package, urdfpath, robotsCallback, geometryLoader, fetchOptions) | ||
### load(urdfpath, packages, onComplete, options) | ||
Loads and builds the specified URDF robot in THREE.js | ||
##### package | ||
#### urdfpath : String | ||
_required_ | ||
The path to the URDF file relative to the specified package directory. | ||
#### packages : String | Object | ||
_required_ | ||
The path representing the `package://` directory(s) to load `package://` relative files. | ||
If the argument is a string, then it is used to replace the `package://` prefix when loading geometry. To specify multiple packages an object syntax used defining the package name to the package path: | ||
If the argument is a string, then it is used to replace the `package://` prefix when loading geometry. | ||
To specify multiple packages an object syntax is used defining the package name to the package path: | ||
```js | ||
@@ -58,30 +76,116 @@ { | ||
##### urdf | ||
#### onComplete(robot) : Function | ||
_required_ | ||
The path to the URDF file relative to the specified package directory. | ||
Callback that is called once the urdf robots have been loaded. The loaded robot is passed to the function. | ||
##### robotCallback(robot) | ||
See `URDFRobot` documentation. | ||
#### options : Object | ||
_optional_ | ||
##### options.loadMeshCallback(pathToModel, fileExtension, onComplete) : Function | ||
An optional function that can be used to override the default mesh loading functionality. The default loader is specified at `URDFLoader.defaultMeshLoader`. `onComplete` is called with the mesh once the geometry has been loaded. | ||
##### options.fetchOptions : Object | ||
An optional object with the set of options to pass to the `fetch` function call used to load the URDF file. | ||
##### workingPath : String | ||
The path to load geometry relative to. | ||
Defaults to the path relative to the loaded URDF file. | ||
### parse(urdfContent, packages, onComplete, options) : THREE.Object3D | ||
Parses URDF content and returns the robot model. | ||
#### urdfContent : String | ||
_required_ | ||
Callback that is called once the urdf robots have been loaded. The loaded robot is passed to the function. | ||
The xml content of a URDF file. | ||
The available joints are specified on `robot.urdf.joints`. | ||
#### packages : String | Object | ||
To set a joint angle, call `robot.urdf.joint.setAngle(angle)`. | ||
_required_ | ||
##### geometryLoader(pathToModel, fileExtension, doneCallback) | ||
See `load`. | ||
#### onComplete(robot) : Function | ||
_optional_ | ||
An optional function that can be used to override the default mesh loading functionality. The default loader is specified at `URDFLoader.defaultMeshLoader`. `doneCallback` is called with the mesh once the geometry has been loaded. | ||
Called immediately with the generated robot. This is the same object that is returned from the function. | ||
##### fetchOptions | ||
Note that the link geometry will not necessarily have finished being processed when this is called. | ||
_optional_ | ||
See `URDFRobot` documentation. | ||
An optional object with the set of options to pass to the `fetch` function call used to load the URDF file. | ||
#### options : Object | ||
See `load`. | ||
## URDFRobot | ||
Object that describes the URDF Robot. An extension of `THREE.Object3D`. | ||
### name : String | ||
The name of the robot described in the `<robot>` tag. | ||
### links : Object | ||
A dictionary of `linkName : URDFLink` with all links in the robot. | ||
### joints : Object | ||
A dictionary of `jointName : URDFJoint` with all joints in the robot. | ||
## URDFJoint | ||
An object representing a robot joint. An extension of `THREE.Object3D`. | ||
### name : String | ||
The name of the joint. | ||
### jointType : String | ||
The type of joint. Can only be the URDF types of joints. | ||
### limit : Object | ||
An object containing the `lower` and `upper` constraints for the joint. | ||
### axis : THREE.Vector3 | ||
The axis described for the joint. | ||
### angle : Number | ||
_readonly_ | ||
The current position or angle for joint. | ||
### ignoreLimits : Boolean | ||
Whether or not to ignore the joint limits when setting a the joint position. | ||
### setAngle(angle) | setOffset(position) | ||
#### angle | position : Number | ||
The position off of the starting position to rotate or move the joint to. | ||
### URDFLink | ||
#### name | ||
The name of the link. | ||
## urdf-viewer Element | ||
@@ -129,4 +233,2 @@ ```html | ||
#### urdf | ||
@@ -133,0 +235,0 @@ |
@@ -79,3 +79,3 @@ /* globals URDFViewer THREE */ | ||
return j.urdf && j.urdf.type && j.urdf.type !== 'fixed'; | ||
return j.isURDFJoint && j.jointType !== 'fixed'; | ||
@@ -153,3 +153,3 @@ }; | ||
// TODO: Why is the constant negated? | ||
plane.normal.copy(tg.urdf.axis).transformDirection(tg.matrixWorld).normalize(); | ||
plane.normal.copy(tg.axis).transformDirection(tg.matrixWorld).normalize(); | ||
plane.constant = -plane.normal.dot(clickPoint); | ||
@@ -230,3 +230,3 @@ | ||
plane.normal.copy(tg.urdf.axis).transformDirection(tg.parent.matrixWorld).normalize(); | ||
plane.normal.copy(tg.axis).transformDirection(tg.parent.matrixWorld).normalize(); | ||
@@ -254,3 +254,3 @@ return temp.length() * -Math.sign(temp.dot(plane.normal)); | ||
clickPoint.copy(target.point); | ||
this.dispatchEvent(new CustomEvent('manipulate-start', { bubbles: true, cancelable: true, detail: dragging.urdf.name })); | ||
this.dispatchEvent(new CustomEvent('manipulate-start', { bubbles: true, cancelable: true, detail: dragging.name })); | ||
this.controls.enabled = false; | ||
@@ -300,3 +300,3 @@ | ||
highlightLinkGeometry(wasHovered, true); | ||
this.dispatchEvent(new CustomEvent('joint-mouseout', { bubbles: true, cancelable: true, detail: wasHovered.urdf.name })); | ||
this.dispatchEvent(new CustomEvent('joint-mouseout', { bubbles: true, cancelable: true, detail: wasHovered.name })); | ||
@@ -308,3 +308,3 @@ } | ||
highlightLinkGeometry(hovered, false); | ||
this.dispatchEvent(new CustomEvent('joint-mouseover', { bubbles: true, cancelable: true, detail: hovered.urdf.name })); | ||
this.dispatchEvent(new CustomEvent('joint-mouseover', { bubbles: true, cancelable: true, detail: hovered.name })); | ||
@@ -321,7 +321,7 @@ } | ||
let delta = null; | ||
if (dragging.urdf.type === 'revolute' || dragging.urdf.type === 'continuous') { | ||
if (dragging.jointType === 'revolute' || dragging.jointType === 'continuous') { | ||
delta = getAngle(dragging, mouse, lastMouse); | ||
} else if (dragging.urdf.type === 'prismatic') { | ||
} else if (dragging.jointType === 'prismatic') { | ||
@@ -338,3 +338,3 @@ delta = getMove(dragging, mouse, lastMouse); | ||
this.setAngle(dragging.urdf.name, dragging.urdf.angle + delta); | ||
this.setAngle(dragging.name, dragging.angle + delta); | ||
@@ -354,3 +354,3 @@ } | ||
this.dispatchEvent(new CustomEvent('manipulate-end', { bubbles: true, cancelable: true, detail: dragging.urdf.name })); | ||
this.dispatchEvent(new CustomEvent('manipulate-end', { bubbles: true, cancelable: true, detail: dragging.name })); | ||
dragging = null; | ||
@@ -357,0 +357,0 @@ this.controls.enabled = true; |
@@ -50,3 +50,3 @@ /* globals THREE URDFLoader */ | ||
for (const name in this.robot.urdf.joints) angles[name] = this.robot.urdf.joints[name].urdf.angle; | ||
for (const name in this.robot.joints) angles[name] = this.robot.joints[name].angle; | ||
@@ -84,3 +84,2 @@ } | ||
dirLight.shadow.mapSize.height = 2048; | ||
dirLight.shadow.bias = -0.000025; | ||
dirLight.castShadow = true; | ||
@@ -95,2 +94,3 @@ scene.add(dirLight); | ||
renderer.shadowMap.enabled = true; | ||
renderer.shadowMap.type = THREE.PCFSoftShadowMap; | ||
renderer.gammaOutput = true; | ||
@@ -137,2 +137,4 @@ | ||
this._setUp(this.up); | ||
// redraw when something new has loaded | ||
@@ -284,6 +286,6 @@ this.loadingManager.onLoad = () => this._dirty = true; | ||
const joint = this.robot.urdf.joints[jointname]; | ||
if (joint && joint.urdf.angle !== angle) { | ||
const joint = this.robot.joints[jointname]; | ||
if (joint && joint.angle !== angle) { | ||
joint.urdf.setAngle(angle); | ||
joint.setAngle(angle); | ||
this._dirty = true; | ||
@@ -436,6 +438,8 @@ | ||
pkg = pkg.split(',').reduce(function(map, value) { | ||
pkg = pkg.split(',').reduce((map, value) => { | ||
const [pkgName, pkgPath] = value.split(/:(.+)/).filter(x => !!x); | ||
map[pkgName.trim()] = pkgPath.trim(); | ||
const split = value.split(/:/).filter(x => !!x); | ||
const pkgName = split.shift().trim(); | ||
const pkgPath = split.join(':').trim(); | ||
map[pkgName] = pkgPath; | ||
@@ -448,4 +452,4 @@ return map; | ||
this.urdfLoader.load( | ||
urdf, | ||
pkg, | ||
urdf, | ||
@@ -477,26 +481,31 @@ // Callback with array of robots | ||
// Load meshes and enable shadow casting | ||
(path, ext, done) => { | ||
// options | ||
{ | ||
loadMeshCb: (path, ext, done) => { | ||
totalMeshes++; | ||
this.urdfLoader.defaultMeshLoader(path, ext, mesh => { | ||
// Load meshes and enable shadow casting | ||
totalMeshes++; | ||
this.urdfLoader.defaultMeshLoader(path, ext, mesh => { | ||
updateMaterials(mesh); | ||
updateMaterials(mesh); | ||
done(mesh); | ||
done(mesh); | ||
meshesLoaded++; | ||
if (meshesLoaded === totalMeshes && this._requestId === requestId) { | ||
meshesLoaded++; | ||
if (meshesLoaded === totalMeshes && this._requestId === requestId) { | ||
this.dispatchEvent(new CustomEvent('geometry-loaded', { bubbles: true, cancelable: true, composed: true })); | ||
this.dispatchEvent(new CustomEvent('geometry-loaded', { bubbles: true, cancelable: true, composed: true })); | ||
} | ||
} | ||
this._dirty = true; | ||
this._dirty = true; | ||
}); | ||
}); | ||
}, | ||
{ mode: 'cors', credentials: 'same-origin' }); | ||
}, | ||
fetchOptions: { mode: 'cors', credentials: 'same-origin' }, | ||
}); | ||
} | ||
@@ -530,7 +539,7 @@ | ||
Object | ||
.values(this.robot.urdf.joints) | ||
.values(this.robot.joints) | ||
.forEach(joint => { | ||
joint.urdf.ignoreLimits = ignore; | ||
joint.urdf.setAngle(joint.urdf.angle); | ||
joint.ignoreLimits = ignore; | ||
joint.setAngle(joint.angle); | ||
@@ -537,0 +546,0 @@ }); |
@@ -90,33 +90,39 @@ /* globals THREE */ | ||
/* Public API */ | ||
// pkg: The equivelant of a (list of) ROS package(s):// directory | ||
// urdf: The path to the URDF within the package OR absolute | ||
// cb: Callback that is passed the model once loaded | ||
load(pkg, urdf, cb, loadMeshCb, fetchOptions) { | ||
// packages: The equivelant of a (list of) ROS package(s):// directory | ||
// onComplete: Callback that is passed the model once loaded | ||
load(urdf, packages, onComplete, options) { | ||
// Check if a full URI is specified before | ||
// prepending the package info | ||
const workingPath = THREE.LoaderUtils.extractUrlBase(urdf); | ||
const urdfPath = this.manager.resolveURL(urdf); | ||
let path = urdf; | ||
options = Object.assign({ workingPath }, options); | ||
// If path is relative | ||
if (!/^[^:]+:\/\//.test(path)) { | ||
fetch(urdfPath, options.fetchOptions) | ||
.then(res => res.text()) | ||
.then(data => this.parse(data, packages, onComplete, options)); | ||
// make sure we don't insert a double slash by cleaning | ||
// the package and urdf paths | ||
path = `${ pkg.replace(/(\\|\/)$/, '') }/${ urdf.replace(/^(\\|\/)/, '') }`; | ||
} | ||
} | ||
parse(content, packages, onComplete, options) { | ||
path = this.manager.resolveURL(path); | ||
options = Object.assign({ | ||
fetch(path, fetchOptions) | ||
.then(res => res.text()) | ||
.then(data => this.parse(pkg, data, cb, loadMeshCb)); | ||
loadMeshCb: this.defaultMeshLoader.bind(this), | ||
workingPath: '', | ||
} | ||
}, options); | ||
parse(pkg, content, cb, loadMeshCb) { | ||
const result = this._processUrdf(content, packages, options.workingPath, options.loadMeshCb); | ||
cb(this._processUrdf(pkg, content, loadMeshCb || this.defaultMeshLoader.bind(this))); | ||
if (typeof onComplete === 'function') { | ||
onComplete(result); | ||
} | ||
return result; | ||
} | ||
@@ -143,3 +149,3 @@ | ||
console.warn(`Could note load model at ${ path }:\nNo loader available`); | ||
console.warn(`URDFLoader: Could not load model at ${ path }.\nNo loader available`); | ||
@@ -153,6 +159,10 @@ } | ||
// Resolves the path of mesh files | ||
_resolveMeshPath(pkg, meshPath) { | ||
_resolvePackagePath(pkg, meshPath, currPath) { | ||
if (!/^package:\/\//.test(meshPath)) return meshPath; | ||
if (!/^package:\/\//.test(meshPath)) { | ||
return currPath !== undefined ? currPath + meshPath : meshPath; | ||
} | ||
// Remove "package://" keyword and split meshPath at the first slash | ||
@@ -162,18 +172,19 @@ const [targetPkg, relPath] = meshPath.replace(/^package:\/\//, '').split(/\/(.+)/); | ||
if (typeof pkg === 'string') { | ||
// "pkg" is one single package | ||
if (pkg.endsWith(targetPkg)) { | ||
if (pkg.endsWith(targetPkg)) { | ||
// "pkg" is the target package | ||
return pkg + '/' + relPath; | ||
} else { | ||
// Assume "pkg" is the target package's parent directory | ||
return pkg + '/' + targetPkg + '/' + relPath; | ||
return pkg + '/' + targetPkg + '/' + relPath; | ||
} | ||
} else if (typeof pkg === 'object') { | ||
// "pkg" is a map of packages | ||
if (targetPkg in pkg) { | ||
@@ -185,3 +196,4 @@ | ||
console.warn(`Error: ${ targetPkg } not found in provided pkgs!`); | ||
console.error(`URDFLoader : ${ targetPkg } not found in provided package list!`); | ||
return null; | ||
@@ -193,3 +205,3 @@ } | ||
// Process the URDF text format | ||
_processUrdf(pkg, data, loadMeshCb) { | ||
_processUrdf(data, packages, path, loadMeshCb) { | ||
@@ -200,3 +212,3 @@ const parser = new DOMParser(); | ||
const robottag = this.filter(urdf.children, c => c.nodeName === 'robot').pop(); | ||
return this._processRobot(pkg, robottag, loadMeshCb); | ||
return this._processRobot(robottag, packages, path, loadMeshCb); | ||
@@ -206,3 +218,3 @@ } | ||
// Process the <robot> node | ||
_processRobot(pkg, robot, loadMeshCb) { | ||
_processRobot(robot, packages, path, loadMeshCb) { | ||
@@ -213,3 +225,2 @@ const links = []; | ||
obj.name = robot.getAttribute('name'); | ||
obj.urdf = { node: robot }; | ||
@@ -230,3 +241,3 @@ // Process the <joint> and <link> nodes | ||
const name = l.getAttribute('name'); | ||
linkMap[name] = this._processLink(pkg, l, loadMeshCb); | ||
linkMap[name] = this._processLink(l, packages, path, loadMeshCb); | ||
@@ -254,4 +265,6 @@ }); | ||
obj.urdf.joints = jointMap; | ||
obj.urdf.links = linkMap; | ||
obj.joints = jointMap; | ||
obj.links = linkMap; | ||
obj.isURDFRobot = true; | ||
obj.type = 'URDFRobot'; | ||
@@ -267,14 +280,25 @@ return obj; | ||
const obj = new THREE.Object3D(); | ||
obj.isURDFJoint = true; | ||
obj.type = 'URDFJoint'; | ||
obj.name = joint.getAttribute('name'); | ||
obj.urdf = { | ||
node: joint, | ||
name: joint.getAttribute('name'), | ||
type: jointType, | ||
angle: 0, | ||
axis: null, | ||
limit: { lower: 0, upper: 0 }, | ||
ignoreLimits: false, | ||
setAngle: () => {}, | ||
}; | ||
obj.jointType = jointType; | ||
obj.axis = null; | ||
obj.angle = 0; | ||
obj.limit = { lower: 0, upper: 0 }; | ||
obj.ignoreLimits = false; | ||
obj.setOffset = () => {}; | ||
// copy the 'setOffset' function over to 'setAngle' so | ||
// it makes sense for other joint types (prismatic, planar) | ||
// TODO: Remove the 'setOffset' function | ||
// TODO: Figure out how to handle setting and getting angles of other types | ||
Object.defineProperties( | ||
obj, | ||
{ | ||
setAngle: { get() { return this.setOffset; } }, | ||
}); | ||
let parent = null; | ||
@@ -304,4 +328,4 @@ let child = null; | ||
obj.urdf.limit.lower = parseFloat(n.getAttribute('lower') || obj.urdf.limit.lower); | ||
obj.urdf.limit.upper = parseFloat(n.getAttribute('upper') || obj.urdf.limit.upper); | ||
obj.limit.lower = parseFloat(n.getAttribute('lower') || obj.limit.lower); | ||
obj.limit.upper = parseFloat(n.getAttribute('upper') || obj.limit.upper); | ||
@@ -326,4 +350,4 @@ } | ||
const axisxyz = axisnode.getAttribute('xyz').split(/\s+/g).map(num => parseFloat(num)); | ||
obj.urdf.axis = new THREE.Vector3(axisxyz[0], axisxyz[1], axisxyz[2]); | ||
obj.urdf.axis.normalize(); | ||
obj.axis = new THREE.Vector3(axisxyz[0], axisxyz[1], axisxyz[2]); | ||
obj.axis.normalize(); | ||
@@ -336,8 +360,8 @@ } | ||
case 'continuous': | ||
obj.urdf.limit.lower = -Infinity; | ||
obj.urdf.limit.upper = Infinity; | ||
obj.limit.lower = -Infinity; | ||
obj.limit.upper = Infinity; | ||
// fall through to revolute joint 'setAngle' function | ||
// fall through to revolute joint 'setOffset' function | ||
case 'revolute': | ||
obj.urdf.setAngle = function(angle = null) { | ||
obj.setOffset = function(angle = null) { | ||
@@ -365,3 +389,3 @@ if (!this.axis) return; | ||
case 'prismatic': | ||
obj.urdf.setAngle = function(angle = null) { | ||
obj.setOffset = function(angle = null) { | ||
@@ -393,8 +417,2 @@ if (!this.axis) return; | ||
// copy the 'setAngle' function over to 'set' so | ||
// it makes sense for other joint types (prismatic, planar) | ||
// TODO: Remove the 'setAngle' function | ||
// TODO: Figure out how to handle setting and getting angles of other types | ||
obj.urdf.set = obj.urdf.setAngle; | ||
return obj; | ||
@@ -405,3 +423,3 @@ | ||
// Process the <link> nodes | ||
_processLink(pkg, link, loadMeshCb) { | ||
_processLink(link, packages, path, loadMeshCb) { | ||
@@ -411,5 +429,6 @@ const visualNodes = this.filter(link.children, n => n.nodeName.toLowerCase() === 'visual'); | ||
obj.name = link.getAttribute('name'); | ||
obj.urdf = { node: link }; | ||
obj.isURDFLink = true; | ||
obj.type = 'URDFLink'; | ||
this.forEach(visualNodes, vn => this._processVisualNode(pkg, vn, obj, loadMeshCb)); | ||
this.forEach(visualNodes, vn => this._processVisualNode(vn, obj, packages, path, loadMeshCb)); | ||
@@ -421,3 +440,3 @@ return obj; | ||
// Process the visual nodes into meshes | ||
_processVisualNode(pkg, vn, linkObj, loadMeshCb) { | ||
_processVisualNode(vn, linkObj, packages, path, loadMeshCb) { | ||
@@ -439,28 +458,34 @@ let xyz = [0, 0, 0]; | ||
const filename = n.children[0].getAttribute('filename'); | ||
const path = this._resolveMeshPath(pkg, filename); | ||
const ext = path.match(/.*\.([A-Z0-9]+)$/i).pop() || ''; | ||
const scaleAttr = n.children[0].getAttribute('scale'); | ||
if (scaleAttr) scale = this._processTuple(scaleAttr); | ||
const filePath = this._resolvePackagePath(packages, filename, path); | ||
loadMeshCb(path, ext, obj => { | ||
// file path is null if a package directory is not provided. | ||
if (filePath !== null) { | ||
if (obj) { | ||
const ext = filePath.match(/.*\.([A-Z0-9]+)$/i).pop() || ''; | ||
const scaleAttr = n.children[0].getAttribute('scale'); | ||
if (scaleAttr) scale = this._processTuple(scaleAttr); | ||
if (obj instanceof THREE.Mesh) { | ||
loadMeshCb(filePath, ext, obj => { | ||
obj.material.copy(material); | ||
if (obj) { | ||
} | ||
if (obj instanceof THREE.Mesh) { | ||
linkObj.add(obj); | ||
obj.material.copy(material); | ||
obj.position.set(xyz[0], xyz[1], xyz[2]); | ||
obj.rotation.set(0, 0, 0); | ||
obj.scale.set(scale[0], scale[1], scale[2]); | ||
this._applyRotation(obj, rpy); | ||
} | ||
} | ||
linkObj.add(obj); | ||
}); | ||
obj.position.set(xyz[0], xyz[1], xyz[2]); | ||
obj.rotation.set(0, 0, 0); | ||
obj.scale.set(scale[0], scale[1], scale[2]); | ||
this._applyRotation(obj, rpy); | ||
} | ||
}); | ||
} | ||
} else if (geoType === 'box') { | ||
@@ -533,5 +558,6 @@ | ||
const filename = c.getAttribute('filename').replace(/^(package:\/\/)/, ''); | ||
const path = pkg + '/' + filename; | ||
material.map = this._textureloader.load(path); | ||
const filePath = this._resolvePackagePath(packages, filename, path); | ||
material.map = this._textureloader.load(filePath); | ||
} | ||
@@ -538,0 +564,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
323
68236
14
8
1230
2