Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

urdf-loader

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

urdf-loader - npm Package Compare versions

Comparing version 0.2.4 to 0.2.5

6

package.json
{
"name": "urdf-loader",
"version": "0.2.4",
"version": "0.2.5",
"description": "URDF Loader for THREE.js and webcomponent viewer",

@@ -28,3 +28,3 @@ "scripts": {

"peerDependencies": {
"three": "^0.89.0"
"three": "^0.94.0"
},

@@ -35,3 +35,3 @@ "devDependencies": {

"static-server": "^3.0.0",
"three": "^0.91.0",
"three": "^0.94.0",
"threejs-model-loader": "0.0.1",

@@ -38,0 +38,0 @@ "wc-loader": "^1.1.12",

@@ -0,1 +1,2 @@

/* globals THREE URDFLoader */
// urdf-viewer element

@@ -12,126 +13,199 @@ // Loads and displays a 3D view of a URDF-formatted robot

static get observedAttributes() {
return ['package', 'urdf', 'up', 'display-shadow', 'ambient-color', 'ignore-limits']
static get observedAttributes () {
return ['package', 'urdf', 'up', 'display-shadow', 'ambient-color', 'ignore-limits'];
}
get package() { return this.getAttribute('package') || '' }
set package(val) { this.setAttribute('package', val) }
get package () {
get urdf() { return this.getAttribute('urdf') || '' }
set urdf(val) { this.setAttribute('urdf', val) }
return this.getAttribute('package') || '';
get ignoreLimits() { return this.hasAttribute('ignore-limits') || false }
set ignoreLimits(val) {
val ? this.setAttribute('ignore-limits', val) : this.removeAttribute('ignore-limits')
}
set package (val) {
get up() { return this.getAttribute('up') || '+Z' }
set up(val) { this.setAttribute('up', val) }
this.setAttribute('package', val);
get displayShadow() { return this.hasAttribute('display-shadow') || false }
set displayShadow(val) {
val = !!val
val ? this.setAttribute('display-shadow', '') : this.removeAttribute('display-shadow')
}
get ambientColor() { return this.getAttribute('ambient-color') || '#455A64' }
set ambientColor(val) {
val ? this.setAttribute('ambient-color', val) : this.removeAttribute('ambient-color')
get urdf () {
return this.getAttribute('urdf') || '';
}
set urdf (val) {
get autoRedraw() { return this.hasAttribute('auto-redraw') || false }
set autoRedraw(val) {
val ? this.setAttribute('auto-redraw', true) : this.removeAttribute('auto-redraw')
this.setAttribute('urdf', val);
}
get angles() {
const angles = {}
get ignoreLimits () {
return this.hasAttribute('ignore-limits') || false;
}
set ignoreLimits (val) {
val ? this.setAttribute('ignore-limits', val) : this.removeAttribute('ignore-limits');
}
get up () {
return this.getAttribute('up') || '+Z';
}
set up (val) {
this.setAttribute('up', val);
}
get displayShadow () {
return this.hasAttribute('display-shadow') || false;
}
set displayShadow (val) {
val = !!val;
val ? this.setAttribute('display-shadow', '') : this.removeAttribute('display-shadow');
}
get ambientColor () {
return this.getAttribute('ambient-color') || '#455A64';
}
set ambientColor (val) {
val ? this.setAttribute('ambient-color', val) : this.removeAttribute('ambient-color');
}
get autoRedraw () {
return this.hasAttribute('auto-redraw') || false;
}
set autoRedraw (val) {
val ? this.setAttribute('auto-redraw', true) : this.removeAttribute('auto-redraw');
}
get angles () {
const angles = {};
if (this.robot) {
for (let name in this.robot.urdf.joints) angles[name] = this.robot.urdf.joints[name].urdf.angle
for (let name in this.robot.urdf.joints) angles[name] = this.robot.urdf.joints[name].urdf.angle;
}
return angles
return angles;
}
set angles(val) { this._setAngles(val) }
set angles (val) {
get loadingManager() {
this._setAngles(val);
}
get loadingManager () {
return this._loadingManager = this._loadingManager || new THREE.LoadingManager();
}
get urdfLoader() {
return this._urdfLoader = this._urdfLoader || new URDFLoader(this.loadingManager)
get urdfLoader () {
return this._urdfLoader = this._urdfLoader || new URDFLoader(this.loadingManager);
}
/* Lifecycle Functions */
constructor() {
super()
constructor () {
this._requestId = 0
this._dirty = false
this.robot = null
super();
this._requestId = 0;
this._dirty = false;
this.robot = null;
// Scene setup
const scene = new THREE.Scene()
const scene = new THREE.Scene();
const ambientLight = new THREE.AmbientLight(this.ambientColor)
scene.add(ambientLight)
const ambientLight = new THREE.HemisphereLight(this.ambientColor, '#000');
ambientLight.groundColor.lerp(ambientLight.color, 0.5);
ambientLight.intensity = 0.5;
ambientLight.position.set(0, 1, 0);
scene.add(ambientLight);
// Light setup
const dirLight = new THREE.DirectionalLight(0xffffff)
dirLight.position.set(4, 10, 1)
dirLight.shadow.mapSize.width = 2048
dirLight.shadow.mapSize.height = 2048
dirLight.castShadow = true
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(4, 10, 1);
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.bias = -0.0001;
dirLight.castShadow = true;
scene.add(dirLight);
scene.add(dirLight)
// Renderer setup
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
renderer.setClearColor(0xffffff)
renderer.setClearAlpha(0)
renderer.shadowMap.enabled = true
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setClearColor(0xffffff);
renderer.setClearAlpha(0);
renderer.shadowMap.enabled = true;
renderer.gammaOutput = true;
// Camera setup
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000)
camera.position.z = -10
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
camera.position.z = -10;
// World setup
const world = new THREE.Object3D()
scene.add(world)
const world = new THREE.Object3D();
scene.add(world);
const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 40, 40 ),
new THREE.ShadowMaterial( { side: THREE.DoubleSide, transparent: true, opacity: 0.25 } )
)
plane.rotation.x = -Math.PI/2
plane.position.y = -0.5
plane.receiveShadow = true
plane.scale.set(10, 10, 10)
scene.add(plane)
new THREE.PlaneBufferGeometry(40, 40),
new THREE.ShadowMaterial({ side: THREE.DoubleSide, transparent: true, opacity: 0.5 })
);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
plane.receiveShadow = true;
plane.scale.set(10, 10, 10);
scene.add(plane);
// Controls setup
const controls = new THREE.OrbitControls(camera, renderer.domElement)
controls.rotateSpeed = 2.0
controls.zoomSpeed = 5
controls.panSpeed = 2
controls.enableZoom = true
controls.enablePan = true
controls.enableDamping = false
controls.maxDistance = 50
controls.minDistance = 0.25
controls.addEventListener('change', () => this._dirty = true)
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.rotateSpeed = 2.0;
controls.zoomSpeed = 5;
controls.panSpeed = 2;
controls.enableZoom = true;
controls.enablePan = false;
controls.enableDamping = false;
controls.maxDistance = 50;
controls.minDistance = 0.25;
controls.addEventListener('change', () => this._dirty = true);
this.world = world
this.renderer = renderer
this.camera = camera
this.controls = controls
this.plane = plane
this.ambientLight = ambientLight
this.scene = scene;
this.world = world;
this.renderer = renderer;
this.camera = camera;
this.controls = controls;
this.plane = plane;
this.directionalLight = dirLight;
this.ambientLight = ambientLight;
const _renderLoop = () => {
if(this.parentNode) {
this.updateSize()
if (this.parentNode) {
this.updateSize();
if (this._dirty || this.autoRedraw) {
this._updateEnvironment()
this._updateEnvironment();
}

@@ -141,30 +215,37 @@

// case the controls are retargeted
this.controls.update()
this.controls.update();
if (this._dirty || this.autoRedraw) {
this.renderer.render(scene, camera)
this._dirty = false
this.renderer.render(scene, camera);
this._dirty = false;
}
}
this._renderLoopId = requestAnimationFrame(_renderLoop)
}
_renderLoop()
this._renderLoopId = requestAnimationFrame(_renderLoop);
};
_renderLoop();
}
connectedCallback() {
connectedCallback () {
// Add our initialize styles for the element if they haven't
// been added yet
if (!this.constructor._styletag) {
const styletag = document.createElement('style')
const styletag = document.createElement('style');
styletag.innerHTML =
`
${this.tagName} { display: block; }
${this.tagName} canvas {
${ this.tagName } { display: block; }
${ this.tagName } canvas {
width: 100%;
height: 100%;
}
`
document.head.appendChild(styletag)
this.constructor._styletag = styletag
`;
document.head.appendChild(styletag);
this.constructor._styletag = styletag;
}

@@ -174,60 +255,84 @@

if (this.childElementCount === 0) {
this.appendChild(this.renderer.domElement)
this.appendChild(this.renderer.domElement);
}
this.updateSize()
requestAnimationFrame(() => this.updateSize())
this.updateSize();
requestAnimationFrame(() => this.updateSize());
}
disconnectedCallback() {
cancelAnimationFrame(this._renderLoopId)
disconnectedCallback () {
cancelAnimationFrame(this._renderLoopId);
}
attributeChangedCallback(attr, oldval, newval) {
this._dirty = true
attributeChangedCallback (attr, oldval, newval) {
switch(attr) {
this._dirty = true;
switch (attr) {
case 'package':
case 'urdf': {
this._loadUrdf(this.package, this.urdf)
break
this._loadUrdf(this.package, this.urdf);
break;
}
case 'up': {
this._setUp(this.up)
break
this._setUp(this.up);
break;
}
case 'ambient-color': {
this.ambientLight.color.set(this.ambientColor)
break
this.ambientLight.color.set(this.ambientColor);
this.ambientLight.groundColor.set('#000').lerp(this.ambientLight.color, 0.5);
break;
}
case 'ignore-limits': {
this._setIgnoreLimits(this.ignoreLimits, true)
break
this._setIgnoreLimits(this.ignoreLimits, true);
break;
}
}
}
/* Public API */
updateSize() {
const r = this.renderer
const w = this.clientWidth
const h = this.clientHeight
const currsize = r.getSize()
updateSize () {
if (currsize.width != w || currsize.height != h) {
this._dirty = true
const r = this.renderer;
const w = this.clientWidth;
const h = this.clientHeight;
const currsize = r.getSize();
if (currsize.width !== w || currsize.height !== h) {
this._dirty = true;
}
r.setPixelRatio(window.devicePixelRatio)
r.setSize(w, h, false)
r.setPixelRatio(window.devicePixelRatio);
r.setSize(w, h, false);
this.camera.aspect = w / h
this.camera.aspect = w / h;
this.camera.updateProjectionMatrix();
}
redraw() {
this._dirty = true
redraw () {
this._dirty = true;
}

@@ -237,14 +342,20 @@

// angle in degrees
setAngle(jointname, angle) {
if (!this.robot) return
setAngle (jointname, angle) {
const joint = this.robot.urdf.joints[jointname]
if (!this.robot) return;
const joint = this.robot.urdf.joints[jointname];
if (joint && joint.urdf.angle !== angle) {
joint.urdf.setAngle(angle)
this._dirty = true
joint.urdf.setAngle(angle);
this._dirty = true;
}
}
setAngles(angles) {
for(name in angles) this.setAngle(name, angles[name])
setAngles (angles) {
for (const name in angles) this.setAngle(name, angles[name]);
}

@@ -256,40 +367,54 @@

// camera on the center of the scene
_updateEnvironment() {
this.plane.visible = this.displayShadow
if(this.robot && this.displayShadow) {
this.world.updateMatrixWorld()
_updateEnvironment () {
const bbox = new THREE.Box3().setFromObject(this.robot)
this.controls.target.y = bbox.getCenter(new THREE.Vector3()).y
this.plane.position.y = bbox.min.y - 1e-3
this.plane.visible = this.displayShadow;
if (this.robot && this.displayShadow) {
this.world.updateMatrixWorld();
const bbox = new THREE.Box3().setFromObject(this.robot);
this.controls.target.y = bbox.getCenter(new THREE.Vector3()).y;
this.plane.position.y = bbox.min.y - 1e-3;
const minmax = bbox.getBoundingSphere(new THREE.Sphere()).radius;
const cam = this.directionalLight.shadow.camera;
cam.left = cam.bottom = -minmax;
cam.right = cam.top = minmax;
cam.updateProjectionMatrix();
}
}
// Watch the package and urdf field and load the
_loadUrdf(pkg, urdf) {
_loadUrdf (pkg, urdf) {
const _dispose = item => {
if (!item) return
if (item.parent) item.parent.remove(item)
if (item.dispose) item.dispose()
item.children.forEach(c => _dispose(c))
}
if (this._prevload === `${pkg}|${urdf}`) return
if (!item) return;
if (item.parent) item.parent.remove(item);
if (item.dispose) item.dispose();
item.children.forEach(c => _dispose(c));
_dispose(this.robot)
this.robot = null
};
this.dispatchEvent(new CustomEvent('urdf-change', { bubbles: true, cancelable: true, composed: true }))
if (this._prevload === `${ pkg }|${ urdf }`) return;
_dispose(this.robot);
this.robot = null;
this.dispatchEvent(new CustomEvent('urdf-change', { bubbles: true, cancelable: true, composed: true }));
if (pkg && urdf) {
this._prevload = `${pkg}|${urdf}`
this._prevload = `${ pkg }|${ urdf }`;
// Keep track of this request and make
// sure it doesn't get overwritten by
// a subsequent one
this._requestId ++
const requestId = this._requestId
this._requestId++;
const requestId = this._requestId;
let totalMeshes = 0
let meshesLoaded = 0
let totalMeshes = 0;
let meshesLoaded = 0;
this.urdfLoader.load(

@@ -301,15 +426,19 @@ pkg,

robot => {
// If another request has come in to load a new
// robot, then ignore this one
if (this._requestId !== requestId) {
_dispose(robot)
return
_dispose(robot);
return;
}
this.robot = robot
this.world.add(robot)
this.robot = robot;
this.world.add(robot);
this._setIgnoreLimits(this.ignoreLimits)
this._setIgnoreLimits(this.ignoreLimits);
this.dispatchEvent(new CustomEvent('urdf-processed', { bubbles: true, cancelable: true, composed: true }))
this.dispatchEvent(new CustomEvent('urdf-processed', { bubbles: true, cancelable: true, composed: true }));
},

@@ -319,23 +448,60 @@

(path, ext, done) => {
totalMeshes++
totalMeshes++;
this.urdfLoader.defaultMeshLoader(path, ext, mesh => {
const _enableShadows = o => {
if (o instanceof THREE.Mesh) {
o.castShadow = true
mesh.traverse(c => {
if (c.type === 'Mesh') {
c.castShadow = true;
c.receiveShadow = true;
if (c.material) {
const mats =
(Array.isArray(c.material) ? c.material : [c.material])
.map(m => {
if (m instanceof THREE.MeshBasicMaterial) {
return new THREE.MeshPhongMaterial();
}
if (m.map) {
m.map.encoding = THREE.GammaEncoding;
}
return m;
});
c.material = mats.length === 1 ? mats[0] : mats;
}
}
o.children.forEach(c => _enableShadows(c))
}
_enableShadows(mesh)
done(mesh)
meshesLoaded++
});
done(mesh);
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
})
},
{ mode: 'cors', credentials: 'same-origin' })
this._dirty = true;
});
},
{ mode: 'cors', credentials: 'same-origin' });
}
}

@@ -345,13 +511,15 @@

// rotation of the scene to match
_setUp(up) {
if (!up) up = '+Z'
up = up.toUpperCase()
const sign = up.replace(/[^-+]/g, '')[0] || '+'
const axis = up.replace(/[^XYZ]/gi, '')[0] || 'Z'
_setUp (up) {
const PI = Math.PI
const HALFPI = PI / 2
if (axis === 'X') this.world.rotation.set(0, 0, sign === '+' ? HALFPI : -HALFPI)
if (axis === 'Z') this.world.rotation.set(sign === '+' ? -HALFPI : HALFPI, 0, 0)
if (axis === 'Y') this.world.rotation.set(sign === '+' ? 0 : PI, 0, 0)
if (!up) up = '+Z';
up = up.toUpperCase();
const sign = up.replace(/[^-+]/g, '')[0] || '+';
const axis = up.replace(/[^XYZ]/gi, '')[0] || 'Z';
const PI = Math.PI;
const HALFPI = PI / 2;
if (axis === 'X') this.world.rotation.set(0, 0, sign === '+' ? HALFPI : -HALFPI);
if (axis === 'Z') this.world.rotation.set(sign === '+' ? -HALFPI : HALFPI, 0, 0);
if (axis === 'Y') this.world.rotation.set(sign === '+' ? 0 : PI, 0, 0);
}

@@ -361,17 +529,25 @@

// joint limits or not
_setIgnoreLimits(ignore, dispatch = false) {
_setIgnoreLimits (ignore, dispatch = false) {
if (this.robot) {
Object
.values(this.robot.urdf.joints)
.forEach(joint => {
joint.urdf.ignoreLimits = ignore
joint.urdf.setAngle(joint.urdf.angle)
})
joint.urdf.ignoreLimits = ignore;
joint.urdf.setAngle(joint.urdf.angle);
});
}
if (dispatch) {
this.dispatchEvent(new CustomEvent('ignore-limits-change', { bubbles: true, cancelable: true, composed: true }))
this.dispatchEvent(new CustomEvent('ignore-limits-change', { bubbles: true, cancelable: true, composed: true }));
}
}
}
};

@@ -0,1 +1,2 @@

/* globals THREE */
/*

@@ -29,20 +30,26 @@ Reference coordinate frames for THREE.js and ROS.

// Cached mesh loaders
get STLLoader() {
this._stlloader = this._stlloader || new THREE.STLLoader(this.manager)
return this._stlloader
get STLLoader () {
this._stlloader = this._stlloader || new THREE.STLLoader(this.manager);
return this._stlloader;
}
get DAELoader() {
this._daeloader = this._daeloader || new THREE.ColladaLoader(this.manager)
return this._daeloader
get DAELoader () {
this._daeloader = this._daeloader || new THREE.ColladaLoader(this.manager);
return this._daeloader;
}
get TextureLoader() {
this._textureloader = this._textureloader || new THREE.TextureLoader(this.manager)
return this._textureloader
get TextureLoader () {
this._textureloader = this._textureloader || new THREE.TextureLoader(this.manager);
return this._textureloader;
}
constructor(manager) {
constructor (manager) {
this.manager = manager || THREE.DefaultLoadingManager
this.manager = manager || THREE.DefaultLoadingManager;

@@ -54,17 +61,29 @@ }

// HTMLCollection does not the by default
forEach(coll, func) { return [].forEach.call(coll, func) }
filter(coll, func) { return [].filter.call(coll, func) }
forEach (coll, func) {
return [].forEach.call(coll, func);
}
filter (coll, func) {
return [].filter.call(coll, func);
}
// take a vector "x y z" and process it into
// an array [x, y, z]
_processTuple(val) {
if (!val) return [0, 0, 0]
return val.trim().split(/\s+/g).map(num => parseFloat(num))
_processTuple (val) {
if (!val) return [0, 0, 0];
return val.trim().split(/\s+/g).map(num => parseFloat(num));
}
// applies a rotation a threejs object in URDF order
_applyRotation(obj, rpy) {
obj.rotateOnAxis(new THREE.Vector3(0,0,1), rpy[2])
obj.rotateOnAxis(new THREE.Vector3(0,1,0), rpy[1])
obj.rotateOnAxis(new THREE.Vector3(1,0,0), rpy[0])
_applyRotation (obj, rpy) {
obj.rotateOnAxis(new THREE.Vector3(0, 0, 1), rpy[2]);
obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), rpy[1]);
obj.rotateOnAxis(new THREE.Vector3(1, 0, 0), rpy[0]);
}

@@ -76,6 +95,6 @@

// cb: Callback that is passed the model once loaded
load(pkg, urdf, cb, loadMeshCb, fetchOptions) {
load (pkg, urdf, cb, loadMeshCb, fetchOptions) {
// normalize the path slashes
let path = `${pkg}/${urdf}`.replace(/\\/g, '/').replace(/\/+/g, '/')
let path = `${ pkg }/${ urdf }`.replace(/\\/g, '/').replace(/\/+/g, '/');
path = this.manager.resolveURL(path);

@@ -85,22 +104,35 @@

.then(res => res.text())
.then(data => this.parse(pkg, data, cb, loadMeshCb))
.then(data => this.parse(pkg, data, cb, loadMeshCb));
}
parse(pkg, content, cb, loadMeshCb) {
cb(this._processUrdf(pkg, content, loadMeshCb || this.defaultMeshLoader.bind(this)))
parse (pkg, content, cb, loadMeshCb) {
cb(this._processUrdf(pkg, content, loadMeshCb || this.defaultMeshLoader.bind(this)));
}
// Default mesh loading function
defaultMeshLoader(path, ext, done) {
defaultMeshLoader (path, ext, done) {
if (/\.stl$/i.test(path))
if (/\.stl$/i.test(path)) {
this.STLLoader.load(path, geom => {
const mesh = new THREE.Mesh()
mesh.geometry = geom
done(mesh)
})
else if (/\.dae$/i.test(path))
this.DAELoader.load(path, dae => done(dae.scene))
else
console.warn(`Could note load model at ${path}:\nNo loader available`)
const mesh = new THREE.Mesh();
mesh.geometry = geom;
done(mesh);
});
} else if (/\.dae$/i.test(path)) {
this.DAELoader.load(path, dae => done(dae.scene));
} else {
console.warn(`Could note load model at ${ path }:\nNo loader available`);
}
}

@@ -110,112 +142,150 @@

// Process the URDF text format
_processUrdf(pkg, data, loadMeshCb) {
const parser = new DOMParser()
const urdf = parser.parseFromString(data, 'text/xml')
_processUrdf (pkg, data, loadMeshCb) {
const robottag = this.filter(urdf.children, c => c.nodeName === 'robot').pop()
return this._processRobot(pkg, robottag, loadMeshCb)
const parser = new DOMParser();
const urdf = parser.parseFromString(data, 'text/xml');
const robottag = this.filter(urdf.children, c => c.nodeName === 'robot').pop();
return this._processRobot(pkg, robottag, loadMeshCb);
}
// Process the <robot> node
_processRobot(pkg, robot, loadMeshCb) {
const links = []
const joints = []
const obj = new THREE.Object3D()
obj.name = robot.getAttribute('name')
obj.urdf = { node: robot }
_processRobot (pkg, robot, loadMeshCb) {
const links = [];
const joints = [];
const obj = new THREE.Object3D();
obj.name = robot.getAttribute('name');
obj.urdf = { node: robot };
// Process the <joint> and <link> nodes
this.forEach(robot.children, n => {
const type = n.nodeName.toLowerCase()
if (type === 'link') links.push(n)
else if (type === 'joint') joints.push(n)
})
const type = n.nodeName.toLowerCase();
if (type === 'link') links.push(n);
else if (type === 'joint') joints.push(n);
});
// Create the <link> map
const linkMap = {}
const linkMap = {};
this.forEach(links, l => {
const name = l.getAttribute('name')
linkMap[name] = this._processLink(pkg, l, loadMeshCb)
})
const name = l.getAttribute('name');
linkMap[name] = this._processLink(pkg, l, loadMeshCb);
});
// Create the <joint> map
const jointMap = {}
const jointMap = {};
this.forEach(joints, j => {
const name = j.getAttribute('name')
jointMap[name] = this._processJoint(j, linkMap)
})
for (let key in linkMap) linkMap[key].parent ? null : obj.add(linkMap[key])
const name = j.getAttribute('name');
jointMap[name] = this._processJoint(j, linkMap);
obj.urdf.joints = jointMap
obj.urdf.links = linkMap
});
return obj
for (let key in linkMap) {
if (linkMap[key].parent == null) {
obj.add(linkMap[key]);
}
}
obj.urdf.joints = jointMap;
obj.urdf.links = linkMap;
return obj;
}
// Process joint nodes and parent them
_processJoint(joint, linkMap) {
const jointType = joint.getAttribute('type')
const obj = new THREE.Object3D()
obj.name = joint.getAttribute('name')
_processJoint (joint, linkMap) {
const jointType = joint.getAttribute('type');
const obj = new THREE.Object3D();
obj.name = joint.getAttribute('name');
obj.urdf = {
node: joint, type: jointType, angle: 0, axis: null,
node: joint,
type: jointType,
angle: 0,
axis: null,
limits: { lower: 0, upper: 0 },
ignoreLimits: false,
setAngle: () => {}
}
setAngle: () => {},
};
let parent = null
let child = null
let xyz = [0, 0, 0]
let rpy = [0, 0, 0]
let parent = null;
let child = null;
let xyz = [0, 0, 0];
let rpy = [0, 0, 0];
// Extract the attributes
this.forEach(joint.children, n => {
const type = n.nodeName.toLowerCase()
const type = n.nodeName.toLowerCase();
if (type === 'origin') {
xyz = this._processTuple(n.getAttribute('xyz'))
rpy = this._processTuple(n.getAttribute('rpy'))
} else if(type === 'child') {
child = linkMap[n.getAttribute('link')]
} else if(type === 'parent') {
parent = linkMap[n.getAttribute('link')]
} else if(type === 'limit') {
obj.urdf.limits.lower = parseFloat(n.getAttribute('lower') || obj.urdf.limits.lower)
obj.urdf.limits.upper = parseFloat(n.getAttribute('upper') || obj.urdf.limits.upper)
xyz = this._processTuple(n.getAttribute('xyz'));
rpy = this._processTuple(n.getAttribute('rpy'));
} else if (type === 'child') {
child = linkMap[n.getAttribute('link')];
} else if (type === 'parent') {
parent = linkMap[n.getAttribute('link')];
} else if (type === 'limit') {
obj.urdf.limits.lower = parseFloat(n.getAttribute('lower') || obj.urdf.limits.lower);
obj.urdf.limits.upper = parseFloat(n.getAttribute('upper') || obj.urdf.limits.upper);
}
})
});
// Join the links
parent.add(obj)
obj.add(child)
this._applyRotation(obj, rpy)
obj.position.set(xyz[0], xyz[1], xyz[2])
parent.add(obj);
obj.add(child);
this._applyRotation(obj, rpy);
obj.position.set(xyz[0], xyz[1], xyz[2]);
// Set up the rotate function
const origRot = new THREE.Quaternion().copy(obj.quaternion)
const origPos = new THREE.Vector3().copy(obj.position)
const axisnode = this.filter(joint.children, n => n.nodeName.toLowerCase() === 'axis')[0]
const origRot = new THREE.Quaternion().copy(obj.quaternion);
const origPos = new THREE.Vector3().copy(obj.position);
const axisnode = this.filter(joint.children, n => n.nodeName.toLowerCase() === 'axis')[0];
if (axisnode) {
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()
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();
}
switch (jointType) {
case 'fixed': break;
case 'continuous':
obj.urdf.limits.lower = -Infinity
obj.urdf.limits.upper = Infinity
obj.urdf.limits.lower = -Infinity;
obj.urdf.limits.upper = Infinity;
// fall through to revolute joint 'setAngle' function
case 'revolute':
obj.urdf.setAngle = function(angle = null) {
if (!this.axis) return
if (angle == null) return
obj.urdf.setAngle = function (angle = null) {
if (!this.axis) return;
if (angle == null) return;
if (!this.ignoreLimits) {
angle = Math.min(this.limits.upper, angle)
angle = Math.max(this.limits.lower, angle)
angle = Math.min(this.limits.upper, angle);
angle = Math.max(this.limits.lower, angle);
}

@@ -225,30 +295,36 @@

// expected angle for URDF, so negate it here
const delta = new THREE.Quaternion().setFromAxisAngle(this.axis, angle)
obj.quaternion.multiplyQuaternions(origRot, delta)
const delta = new THREE.Quaternion().setFromAxisAngle(this.axis, angle);
obj.quaternion.multiplyQuaternions(origRot, delta);
this.angle = angle
}
break
this.angle = angle;
};
break;
case 'prismatic':
obj.urdf.setAngle = function(angle = null) {
if (!this.axis) return
if (angle == null) return
obj.urdf.setAngle = function (angle = null) {
if (!this.axis) return;
if (angle == null) return;
if (!this.ignoreLimits) {
angle = Math.min(this.limits.upper, angle)
angle = Math.max(this.limits.lower, angle)
angle = Math.min(this.limits.upper, angle);
angle = Math.max(this.limits.lower, angle);
}
obj.position.copy(origPos);
obj.position.addScaledVector(this.axis, angle)
obj.position.addScaledVector(this.axis, angle);
this.angle = angle
}
break
this.angle = angle;
};
break;
case 'floating':
case 'planar':
// TODO: Support these joint types
console.warn(`'${ jointType }' joint not yet supported`)
console.warn(`'${ jointType }' joint not yet supported`);
}

@@ -260,121 +336,158 @@

// TODO: Figure out how to handle setting and getting angles of other types
obj.urdf.set = obj.urdf.setAngle
obj.urdf.set = obj.urdf.setAngle;
return obj
return obj;
}
// Process the <link> nodes
_processLink(pkg, link, loadMeshCb) {
const visualNodes = this.filter(link.children, n => n.nodeName.toLowerCase() === 'visual')
const obj = new THREE.Object3D()
obj.name = link.getAttribute('name')
obj.urdf = { node: link }
_processLink (pkg, link, loadMeshCb) {
this.forEach(visualNodes, vn => this._processVisualNode(pkg, vn, obj, loadMeshCb))
const visualNodes = this.filter(link.children, n => n.nodeName.toLowerCase() === 'visual');
const obj = new THREE.Object3D();
obj.name = link.getAttribute('name');
obj.urdf = { node: link };
return obj
this.forEach(visualNodes, vn => this._processVisualNode(pkg, vn, obj, loadMeshCb));
return obj;
}
// Process the visual nodes into meshes
_processVisualNode(pkg, vn, linkObj, loadMeshCb) {
let xyz = [0, 0, 0]
let rpy = [0, 0, 0]
let scale = [1, 1, 1]
let mesh = null
_processVisualNode (pkg, vn, linkObj, loadMeshCb) {
const material = new THREE.MeshLambertMaterial()
let xyz = [0, 0, 0];
let rpy = [0, 0, 0];
let scale = [1, 1, 1];
const material = new THREE.MeshPhongMaterial();
this.forEach(vn.children, n => {
const type = n.nodeName.toLowerCase()
const type = n.nodeName.toLowerCase();
if (type === 'geometry') {
const geoType = n.children[0].nodeName.toLowerCase()
const geoType = n.children[0].nodeName.toLowerCase();
if (geoType === 'mesh') {
const filename = n.children[0].getAttribute('filename').replace(/^((package:\/\/)|(model:\/\/))/, '')
const path = pkg + '/' + filename
const ext = path.match(/.*\.([A-Z0-9]+)$/i).pop() || ''
let scale_exist = n.children[0].getAttribute('scale')
if (scale_exist) scale = this._processTuple(scale_exist)
const filename = n.children[0].getAttribute('filename').replace(/^((package:\/\/)|(model:\/\/))/, '');
const path = pkg + '/' + filename;
const ext = path.match(/.*\.([A-Z0-9]+)$/i).pop() || '';
let scaleAttr = n.children[0].getAttribute('scale');
if (scaleAttr) scale = this._processTuple(scaleAttr);
loadMeshCb(path, ext, obj => {
if (obj) {
if (obj instanceof THREE.Mesh) {
obj.material = material
obj.material.copy(material);
}
linkObj.add(obj)
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)
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') {
requestAnimationFrame(() => {
const mesh = new THREE.Mesh()
mesh.geometry = new THREE.BoxGeometry(1, 1, 1)
mesh.material = material
const size = this._processTuple(n.children[0].getAttribute('size'))
const mesh = new THREE.Mesh();
mesh.geometry = new THREE.BoxGeometry(1, 1, 1);
mesh.material = material;
linkObj.add(mesh)
this._applyRotation(mesh, rpy)
mesh.position.set(xyz[0], xyz[1], xyz[2])
mesh.scale.set(size[0], size[1], size[2])
})
const size = this._processTuple(n.children[0].getAttribute('size'));
linkObj.add(mesh);
this._applyRotation(mesh, rpy);
mesh.position.set(xyz[0], xyz[1], xyz[2]);
mesh.scale.set(size[0], size[1], size[2]);
});
} else if (geoType === 'sphere') {
requestAnimationFrame(() => {
const mesh = new THREE.Mesh()
mesh.geometry = new THREE.SphereGeometry(1, 20, 20)
mesh.material = material
const radius = parseFloat(n.children[0].getAttribute('radius')) || 0
mesh.position.set(xyz[0], xyz[1], xyz[2])
mesh.scale.set(radius, radius, radius)
})
const mesh = new THREE.Mesh();
mesh.geometry = new THREE.SphereGeometry(1, 20, 20);
mesh.material = material;
const radius = parseFloat(n.children[0].getAttribute('radius')) || 0;
mesh.position.set(xyz[0], xyz[1], xyz[2]);
mesh.scale.set(radius, radius, radius);
});
} else if (geoType === 'cylinder') {
requestAnimationFrame(() => {
const radius = parseFloat(n.children[0].getAttribute('radius')) || 0
const length = parseFloat(n.children[0].getAttribute('length')) || 0
const mesh = new THREE.Mesh()
mesh.geometry = new THREE.CylinderBufferGeometry(1, 1, 1, 25)
mesh.material = material
mesh.scale.set(radius, length, radius)
const radius = parseFloat(n.children[0].getAttribute('radius')) || 0;
const length = parseFloat(n.children[0].getAttribute('length')) || 0;
const obj = new THREE.Object3D()
obj.add(mesh)
mesh.rotation.set(Math.PI, 0, 0)
const mesh = new THREE.Mesh();
mesh.geometry = new THREE.CylinderBufferGeometry(1, 1, 1, 25);
mesh.material = material;
mesh.scale.set(radius, length, radius);
linkObj.add(obj)
this._applyRotation(obj, rpy)
obj.position.set(xyz[0], xyz[1], xyz[2])
})
const obj = new THREE.Object3D();
obj.add(mesh);
mesh.rotation.set(Math.PI, 0, 0);
linkObj.add(obj);
this._applyRotation(obj, rpy);
obj.position.set(xyz[0], xyz[1], xyz[2]);
});
}
} else if(type === 'origin') {
xyz = this._processTuple(n.getAttribute('xyz'))
rpy = this._processTuple(n.getAttribute('rpy'))
} else if(type === 'material') {
} else if (type === 'origin') {
xyz = this._processTuple(n.getAttribute('xyz'));
rpy = this._processTuple(n.getAttribute('rpy'));
} else if (type === 'material') {
this.forEach(n.children, c => {
if (c.nodeName.toLowerCase() === 'color') {
let rgba = c.getAttribute('rgba')
.split(/\s/g)
.map(v => parseFloat(v))
.map(v => parseFloat(v));
material.color.r = rgba[0]
material.color.g = rgba[1]
material.color.b = rgba[2]
material.opacity = rgba[3]
material.color.r = rgba[0];
material.color.g = rgba[1];
material.color.b = rgba[2];
material.opacity = rgba[3];
if (material.opacity < 1) material.transparent = true
if (material.opacity < 1) material.transparent = true;
} else if (c.nodeName.toLowerCase() === 'texture') {
const filename = c.getAttribute('filename').replace(/^(package:\/\/)/, '')
const path = pkg + '/' + filename
material.map = this._textureloader.load(path)
const filename = c.getAttribute('filename').replace(/^(package:\/\/)/, '');
const path = pkg + '/' + filename;
material.map = this._textureloader.load(path);
}
})
});
}
})
});
}
}
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc