Skip to content

Commit e6eecf0

Browse files
committed
Update resolveLinksTransform() for parsed XVIZ changes
XVIZ is separating pose streams from others to better support the scene graph. Maintain compatibility, but support the new 'poses' field from parsed XVIZ messages. Also ensure we handle missing poses and cycles.
1 parent f947a6f commit e6eecf0

File tree

4 files changed

+3545
-3037
lines changed

4 files changed

+3545
-3037
lines changed

modules/core/src/utils/transform.js

+41-18
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,51 @@ import {COORDINATE} from '../constants';
2929
const DEFAULT_ORIGIN = [0, 0, 0];
3030

3131
// Export only for testing
32-
export function resolveLinksTransform(links, streams, streamName) {
32+
export function resolveLinksTransform(links, poses, streamName) {
3333
const transforms = [];
34-
let parentPose = links[streamName] && links[streamName].target_pose;
34+
35+
// If streamName has a pose entry, ensure we capture it
36+
if (poses[streamName]) {
37+
transforms.push(poses[streamName]);
38+
}
3539

3640
// TODO(twojtasz): we could cache the resulting transform based on the entry
3741
// into the link structure.
42+
let missingPose = '';
43+
let cycleDetected = false;
44+
if (links) {
45+
let parentPoseName = links[streamName] && links[streamName].target_pose;
46+
const seen = new Set();
47+
48+
// Collect all poses from child to root
49+
while (parentPoseName) {
50+
cycleDetected = seen.has(parentPoseName);
51+
if (cycleDetected) {
52+
break;
53+
}
54+
seen.add(parentPoseName);
55+
56+
if (!poses[parentPoseName]) {
57+
missingPose = parentPoseName;
58+
break;
59+
}
60+
61+
transforms.push(poses[parentPoseName]);
62+
parentPoseName = links[parentPoseName] && links[parentPoseName].target_pose;
63+
}
64+
}
3865

39-
let missingPose = false;
66+
if (missingPose) {
67+
// TODO(twojtasz): report issue
68+
return null;
69+
}
4070

41-
// Collect all poses from child to root
42-
while (parentPose) {
43-
if (!streams[parentPose]) {
44-
missingPose = true;
45-
break;
46-
}
47-
transforms.push(streams[parentPose]);
48-
parentPose = links[parentPose] && links[parentPose].target_pose;
71+
if (cycleDetected) {
72+
// TODO(twojtasz): report issue
73+
return null;
4974
}
5075

51-
// Resolve pose transforms. If missingPose is true, which can happen if a
52-
// persistent link is defined before normal state has been sent, ignore it
53-
// TODO(twojtasz): Flag stream affected by missingPose so it can be reported
54-
// by application
55-
if (!missingPose && transforms.length) {
76+
if (transforms.length) {
5677
// process from root to child
5778
return transforms.reduceRight((acc, val) => {
5879
return acc.multiplyRight(new Pose(val).getTransformationMatrix());
@@ -69,13 +90,14 @@ export function resolveLinksTransform(links, streams, streamName) {
6990
* streamMetadata - Anym metadata associated with the stream
7091
* getTransformMatrix - A callback function for when stream metadata specifieds a DYNAMIC coordinate system
7192
*/
93+
/* eslint-disable complexity */
7294
export function resolveCoordinateTransform(
7395
frame,
7496
streamName,
7597
streamMetadata = {},
7698
getTransformMatrix
7799
) {
78-
const {origin, links = {}, streams, transforms = {}, vehicleRelativeTransform} = frame;
100+
const {origin, links, poses, streams, transforms = {}, vehicleRelativeTransform} = frame;
79101
const {coordinate, transform, pose} = streamMetadata;
80102

81103
let coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS;
@@ -105,7 +127,7 @@ export function resolveCoordinateTransform(
105127

106128
default:
107129
case COORDINATE.IDENTITY:
108-
modelMatrix = resolveLinksTransform(links, streams, streamName);
130+
modelMatrix = resolveLinksTransform(links, poses || streams, streamName);
109131
break;
110132
}
111133

@@ -126,6 +148,7 @@ export function resolveCoordinateTransform(
126148
modelMatrix
127149
};
128150
}
151+
/* eslint-enable complexity */
129152

130153
export function positionToLngLat([x, y, z], {coordinateSystem, coordinateOrigin, modelMatrix}) {
131154
if (modelMatrix) {

package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@
2626
"@babel/preset-flow": "^7.0.0",
2727
"@babel/preset-react": "^7.0.0",
2828
"@babel/register": "^7.0.0",
29+
"@deck.gl/test-utils": "^8.1.5",
30+
"@luma.gl/test-utils": "^8.1.2",
31+
"@probe.gl/test-utils": "^3.2.1",
2932
"@storybook/addon-actions": "^4.0.7",
3033
"@storybook/addon-knobs": "^4.0.7",
3134
"@storybook/addon-links": "^4.0.7",
3235
"@storybook/addon-options": "^4.0.7",
3336
"@storybook/react": "^4.0.7",
34-
"@deck.gl/test-utils": "^8.1.5",
35-
"@luma.gl/test-utils": "^8.1.2",
36-
"@probe.gl/test-utils": "^3.2.1",
3737
"babel-loader": "^8.0.0",
3838
"coveralls": "^3.0.0",
3939
"enzyme": "^3.7.0",
@@ -58,5 +58,6 @@
5858
"copyright-check",
5959
"test-fast"
6060
],
61-
"private": true
61+
"private": true,
62+
"dependencies": {}
6263
}

test/modules/core/utils/transform.spec.js

+59-10
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ import test from 'tape';
2424
import {resolveLinksTransform} from '@streetscape.gl/core/utils/transform';
2525

2626
test('resolveLinksTransform', t => {
27-
// transation links
27+
// translation links
2828
// A -> B -> C
29-
// A -> D
29+
// A -> D -> E
3030
//
3131
// rotation links
3232
// r90 -> m10 -> DD
3333
// rn90 -> m20 -> CC
34+
//
35+
// cycle links
36+
// cycleA -> cycleB -> cycleA
3437
const links = {
3538
C: {
3639
target_pose: 'B'
@@ -41,6 +44,9 @@ test('resolveLinksTransform', t => {
4144
D: {
4245
target_pose: 'A'
4346
},
47+
E: {
48+
target_pose: 'D'
49+
},
4450
CC: {
4551
target_pose: 'm20'
4652
},
@@ -52,10 +58,16 @@ test('resolveLinksTransform', t => {
5258
},
5359
m20: {
5460
target_pose: 'rn90'
61+
},
62+
cycleA: {
63+
target_pose: 'cycleB'
64+
},
65+
cycleB: {
66+
target_pose: 'cycleA'
5567
}
5668
};
5769

58-
const streams = {
70+
const poses = {
5971
A: {
6072
yaw: 0,
6173
pitch: 0,
@@ -72,6 +84,22 @@ test('resolveLinksTransform', t => {
7284
y: 20,
7385
z: 0
7486
},
87+
D: {
88+
yaw: 0,
89+
pitch: 0,
90+
roll: 0,
91+
x: 0,
92+
y: 0,
93+
z: 0
94+
},
95+
E: {
96+
yaw: 0,
97+
pitch: 0,
98+
roll: 0,
99+
x: 10,
100+
y: 10,
101+
z: 0
102+
},
75103
r90: {
76104
yaw: Math.PI / 2,
77105
pitch: 0,
@@ -103,19 +131,40 @@ test('resolveLinksTransform', t => {
103131
x: 20,
104132
y: 20,
105133
z: 0
134+
},
135+
cycleA: {
136+
yaw: 0,
137+
pitch: 0,
138+
roll: 0,
139+
x: 5,
140+
y: 5,
141+
z: 0
142+
},
143+
cycleB: {
144+
yaw: 0,
145+
pitch: 0,
146+
roll: 0,
147+
x: 5,
148+
y: 5,
149+
z: 0
106150
}
107151
};
108152

109153
const testCases = [
110-
{stream: 'C', expected: [30, 30, 0]},
111-
{stream: 'D', expected: [10, 10, 0]},
112-
{stream: 'E', expected: null},
113-
{stream: 'CC', expected: [20, -20, 0]},
114-
{stream: 'DD', expected: [-10, 10, 0]}
154+
{stream: 'A', expected: [10, 10, 0]}, // no link, just pose
155+
{stream: 'C', expected: [30, 30, 0]}, // 2 links, 2 poses
156+
{stream: 'D', expected: [10, 10, 0]}, // 1 link, 1 pose
157+
{stream: 'E', expected: [20, 20, 0]}, // 2 links, 3 poses
158+
{stream: 'Z', expected: null}, // missing pose
159+
{stream: 'CC', expected: [20, -20, 0]}, // rotation & translation path
160+
{stream: 'DD', expected: [-10, 10, 0]}, // rotation & translation path
161+
{stream: 'cycleB', expected: null} // cycle in path
115162
];
116163

164+
// For each testcase, resolve the transform for the named stream
165+
// and verify the expected
117166
for (const testcase of testCases) {
118-
const transformTo = resolveLinksTransform(links, streams, testcase.stream);
167+
const transformTo = resolveLinksTransform(links, poses, testcase.stream);
119168
if (transformTo) {
120169
const resultOrigin = transformTo.transformVector([0, 0, 0]);
121170

@@ -130,7 +179,7 @@ test('resolveLinksTransform', t => {
130179
t.equal(
131180
transformTo,
132181
testcase.expected,
133-
`missing links entry matches expected '${testcase.expected}'`
182+
`null return matches expected '${testcase.expected}'`
134183
);
135184
}
136185
}

0 commit comments

Comments
 (0)