Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for resolving transform defined by "links" scene graph #407

Merged
merged 1 commit into from Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion modules/core/src/components/log-viewer/core-3d-viewer.js
Expand Up @@ -269,6 +269,7 @@ export default class Core3DViewer extends PureComponent {
const stream = lookAheads[streamName] || streams[streamName];
const coordinateProps = resolveCoordinateTransform(
frame,
streamName,
streamsMetadata[streamName],
getTransformMatrix
);
Expand Down Expand Up @@ -317,6 +318,7 @@ export default class Core3DViewer extends PureComponent {
additionalProps,
resolveCoordinateTransform(
frame,
props.streamName,
streamsMetadata[props.streamName],
getTransformMatrix
),
Expand All @@ -328,7 +330,7 @@ export default class Core3DViewer extends PureComponent {
// Apply log-specific coordinate props
Object.assign(
additionalProps,
resolveCoordinateTransform(frame, props, getTransformMatrix)
resolveCoordinateTransform(frame, null, props, getTransformMatrix)
);
} else {
return layer;
Expand Down
Expand Up @@ -74,7 +74,12 @@ export default class ObjectLabelsOverlay extends PureComponent {
}

const {frame, streamsMetadata, getTransformMatrix} = this.props;
result = resolveCoordinateTransform(frame, streamsMetadata[streamName], getTransformMatrix);
result = resolveCoordinateTransform(
frame,
streamName,
streamsMetadata[streamName],
getTransformMatrix
);
// cache calculated coordinate props by stream name
coordinateProps[streamName] = result;

Expand Down
62 changes: 58 additions & 4 deletions modules/core/src/utils/transform.js
Expand Up @@ -18,6 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

/* eslint-disable camelcase */
import {COORDINATE_SYSTEM} from '@deck.gl/core';
import {_Pose as Pose, Matrix4} from 'math.gl';
import {addMetersToLngLat} from 'viewport-mercator-project';
Expand All @@ -27,8 +28,54 @@ import {COORDINATE} from '../constants';
// keep in sync with core-3d-viewer.js
const DEFAULT_ORIGIN = [0, 0, 0];

export function resolveCoordinateTransform(frame, streamMetadata = {}, getTransformMatrix) {
const {origin, transforms = {}, vehicleRelativeTransform} = frame;
// Export only for testing
export function resolveLinksTransform(links, streams, streamName) {
const transforms = [];
let parentPose = links[streamName] && links[streamName].target_pose;

// TODO(twojtasz): we could cache the resulting transform based on the entry
// into the link structure.

let missingPose = false;

// Collect all poses from child to root
while (parentPose) {
if (!streams[parentPose]) {
missingPose = true;
break;
}
transforms.push(streams[parentPose]);
parentPose = links[parentPose] && links[parentPose].target_pose;
}

// Resolve pose transforms. If missingPose is true, which can happen if a
// persistent link is defined before normal state has been sent, ignore it
// TODO(twojtasz): Flag stream affected by missingPose so it can be reported
// by application
if (!missingPose && transforms.length) {
// process from root to child
return transforms.reduceRight((acc, val) => {
return acc.multiplyRight(new Pose(val).getTransformationMatrix());
}, new Matrix4());
}

return null;
}

/* Return the modelMatrix used for rendering a stream.
*
* frame - constains all the XVIZ state
* streamName - The name of the stream we are rendering
* streamMetadata - Anym metadata associated with the stream
* getTransformMatrix - A callback function for when stream metadata specifieds a DYNAMIC coordinate system
*/
export function resolveCoordinateTransform(
frame,
streamName,
streamMetadata = {},
getTransformMatrix
) {
const {origin, links, streams, transforms = {}, vehicleRelativeTransform} = frame;
const {coordinate, transform, pose} = streamMetadata;

let coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS;
Expand All @@ -41,6 +88,7 @@ export function resolveCoordinateTransform(frame, streamMetadata = {}, getTransf
break;

case COORDINATE.DYNAMIC:
// TODO(twojtasz): this should work with links and needs streamName passed
// cache calculated transform matrix for each frame
transforms[transform] = transforms[transform] || getTransformMatrix(transform, frame);
modelMatrix = transforms[transform];
Expand All @@ -49,18 +97,24 @@ export function resolveCoordinateTransform(frame, streamMetadata = {}, getTransf
break;

case COORDINATE.VEHICLE_RELATIVE:
// NOTE: In XVIZ this setting means a relationship to `/vehicle_pose` stream.
// However, with the addition of *links* this really becomes only a convenience
// as you could choose arbitrary poses.
modelMatrix = vehicleRelativeTransform;
break;

case COORDINATE.IDENTITY:
default:
case COORDINATE.IDENTITY:
modelMatrix = resolveLinksTransform(links, streams, streamName);
break;
}

if (pose) {
// TODO - remove when builder updates
// TODO(twojtasz): remove when builder updates
streamTransform = new Pose(pose).getTransformationMatrix();
}
if (streamTransform && streamTransform.length > 0) {
// TODO(twojtasz): this needs tested with links
modelMatrix = modelMatrix
? new Matrix4(modelMatrix).multiplyRight(streamTransform)
: streamTransform;
Expand Down
14 changes: 2 additions & 12 deletions test/apps/viewer/webpack.config.js
Expand Up @@ -47,18 +47,8 @@ const CONFIG = {
// Compile ES2015 using bable
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: BABEL_CONFIG
}
]
},
{
// Unfortunately, webpack doesn't import library sourcemaps on its own...
test: /\.js$/,
use: ['source-map-loader'],
enforce: 'pre'
loader: 'babel-loader',
options: BABEL_CONFIG
}
]
},
Expand Down
1 change: 1 addition & 0 deletions test/modules/core/utils/index.js
Expand Up @@ -24,3 +24,4 @@ import './buffer-range.spec';
import './image-buffer.spec';
import './metrics-helper.spec';
import './style.spec';
import './transform.spec';
139 changes: 139 additions & 0 deletions test/modules/core/utils/transform.spec.js
@@ -0,0 +1,139 @@
// Copyright (c) 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

/* eslint-disable camelcase */
import test from 'tape';

import {resolveLinksTransform} from '@streetscape.gl/core/utils/transform';

test('resolveLinksTransform', t => {
// transation links
// A -> B -> C
// A -> D
//
// rotation links
// r90 -> m10 -> DD
// rn90 -> m20 -> CC
const links = {
C: {
target_pose: 'B'
},
B: {
target_pose: 'A'
},
D: {
target_pose: 'A'
},
CC: {
target_pose: 'm20'
},
DD: {
target_pose: 'm10'
},
m10: {
target_pose: 'r90'
},
m20: {
target_pose: 'rn90'
}
};

const streams = {
A: {
yaw: 0,
pitch: 0,
roll: 0,
x: 10,
y: 10,
z: 0
},
B: {
yaw: 0,
pitch: 0,
roll: 0,
x: 20,
y: 20,
z: 0
},
r90: {
yaw: Math.PI / 2,
pitch: 0,
roll: 0,
x: 0,
y: 0,
z: 0
},
rn90: {
yaw: -Math.PI / 2,
pitch: 0,
roll: 0,
x: 0,
y: 0,
z: 0
},
m10: {
yaw: 0,
pitch: 0,
roll: 0,
x: 10,
y: 10,
z: 0
},
m20: {
yaw: 0,
pitch: 0,
roll: 0,
x: 20,
y: 20,
z: 0
}
};

const testCases = [
{stream: 'C', expected: [30, 30, 0]},
{stream: 'D', expected: [10, 10, 0]},
{stream: 'E', expected: null},
{stream: 'CC', expected: [20, -20, 0]},
{stream: 'DD', expected: [-10, 10, 0]}
];

for (const testcase of testCases) {
const transformTo = resolveLinksTransform(links, streams, testcase.stream);
if (transformTo) {
const resultOrigin = transformTo.transformVector([0, 0, 0]);

t.comment(typeof resultOrigin);
t.comment(typeof testcase.expected);
t.deepEqual(
testcase.expected,
resultOrigin,
`transformed origin of ${testcase.stream} matches expected result ${testcase.expected}`
);
} else {
t.equal(
transformTo,
testcase.expected,
`missing links entry matches expected '${testcase.expected}'`
);
}
}

t.end();
});