Skip to content

Commit

Permalink
APP-1787 - Fix and simplify PCD icon rendering (#2344)
Browse files Browse the repository at this point in the history
  • Loading branch information
micheal-parks authored May 9, 2023
1 parent 1b2cb82 commit 6515eb8
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 187 deletions.
2 changes: 1 addition & 1 deletion web/frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@viamrobotics/remote-control",
"version": "1.1.4",
"version": "1.1.5",
"license": "Apache-2.0",
"type": "module",
"files": [
Expand Down
196 changes: 67 additions & 129 deletions web/frontend/src/components/slam-2d-render.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,40 @@ import * as THREE from 'three';
import { MapControls } from 'three/examples/jsm/controls/MapControls';
import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader';
import type { commonApi } from '@viamrobotics/sdk';
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader';
import { baseMarkerUrl } from '../lib/base-marker-url';
import { destMarkerUrl } from '../lib/destination-marker-url';
type SvgOffset = {
x: number,
y: number,
z: number
}
import DestMarker from '../lib/destination-marker.txt?raw';
import BaseMarker from '../lib/base-marker.txt?raw';
let pointsMaterial: THREE.PointsMaterial | undefined;
const backgroundGridColor = 0xCA_CA_CA;
const gridSubparts = ['AxesPos', 'AxesNeg', 'Grid'];
const gridHelperRenderOrder = 997;
const axesHelperRenderOrder = 998;
const svgMarkerRenderOrder = 999;
const svgMarkerRenderOrder = 4;
const pointsRenderOrder = 3;
const axesHelperRenderOrder = 2;
const gridHelperRenderOrder = 1;
const cameraScale = 25;
const aspectInverse = 4;
const initialPointSize = 4;
const gridHelperScalar = 4;
const axesHelperSize = 8;
// Note: updating the scale of the destination or base marker requires an offset update
const baseMarkerScalar = 0.002;
const destinationMarkerScalar = 0.1;
const textureLoader = new THREE.TextureLoader();
const baseMarkerOffset: SvgOffset = {
x: -0.05,
y: -0.3,
z: 0,
const makeMarker = (png: string, name: string) => {
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshBasicMaterial({ map: textureLoader.load(png), transparent: true });
const marker = new THREE.Mesh(geometry, material);
marker.name = name;
marker.renderOrder = svgMarkerRenderOrder;
return marker;
};
const destinationMarkerOffset: SvgOffset = {
x: 1.2,
y: 2.5,
z: 0,
};
const baseMarkerOffset = new THREE.Vector3(-0.05, -0.3, 0);
const destinationMarkerOffset = new THREE.Vector3(0, 0.5, 0);
/*
* this color map is greyscale. The color map is being used map probability values of a PCD
Expand Down Expand Up @@ -87,7 +85,7 @@ const loader = new PCDLoader();
const container = $ref<HTMLElement>();
const { scene, renderer, canvas, start, stop, setCamera } = threeInstance({
const { scene, renderer, canvas, start, stop, setCamera, update } = threeInstance({
parameters: {
antialias: true,
},
Expand All @@ -99,13 +97,20 @@ renderer.setClearColor(color, 1);
canvas.style.cssText = 'width:100%;height:100%;';
const camera = new THREE.OrthographicCamera(-1, 1, 0.5, -0.5, -1, 1000);
camera.userData.size = 2;
const camera = new THREE.OrthographicCamera();
camera.near = -1000;
camera.far = 1000;
camera.userData.size = 1;
setCamera(camera);
scene.add(camera);
const baseMarker = makeMarker(BaseMarker, 'BaseMarker');
const destMarker = makeMarker(DestMarker, 'DestinationMarker');
destMarker.visible = false;
const controls = new MapControls(camera, canvas);
controls.enableRotate = false;
controls.screenSpacePanning = true;
const raycaster = new MouseRaycaster({ camera, renderer, recursive: false });
Expand All @@ -116,76 +121,6 @@ raycaster.on('click', (event: THREE.Event) => {
}
});
/*
* svgLoader example for webgl:
* https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_svg.html
*/
const svgLoader = new SVGLoader();
const makeMarker = async (url : string, name: string, scalar: number) => {
const data = await svgLoader.loadAsync(url);
const { paths } = data!;
const group = new THREE.Group();
group.scale.multiplyScalar(scalar);
for (const path of paths) {
const fillColor = path!.userData!.style.fill;
if (fillColor !== undefined && fillColor !== 'none') {
const material = new THREE.MeshBasicMaterial({
color: new THREE.Color().setStyle(fillColor)
.convertSRGBToLinear(),
opacity: path!.userData!.style.fillOpacity,
transparent: true,
side: THREE.FrontSide,
depthWrite: false,
wireframe: false,
});
const shapes = SVGLoader.createShapes(path!);
for (const shape of shapes) {
const geometry = new THREE.ShapeGeometry(shape);
const mesh = new THREE.Mesh(geometry.rotateZ(-Math.PI), material);
group.add(mesh);
}
}
const strokeColor = path!.userData!.style.stroke;
if (strokeColor !== undefined && strokeColor !== 'none') {
const material = new THREE.MeshBasicMaterial({
color: new THREE.Color().setStyle(strokeColor)
.convertSRGBToLinear(),
opacity: path!.userData!.style.strokeOpacity,
transparent: true,
side: THREE.DoubleSide,
depthWrite: false,
wireframe: false,
});
for (const subPath of path!.subPaths) {
const geometry = SVGLoader.pointsToStroke(subPath.getPoints(), path!.userData!.style);
if (geometry) {
const mesh = new THREE.Mesh(geometry.rotateZ(-Math.PI), material);
group.add(mesh);
}
}
}
}
group.name = name;
scene.add(group);
group.renderOrder = svgMarkerRenderOrder;
return group;
};
const disposeScene = () => {
scene.traverse((object) => {
if (object.name === 'BaseMarker' || object.name === 'DestinationMarker') {
Expand All @@ -204,13 +139,11 @@ const disposeScene = () => {
scene.clear();
};
const updatePose = async (newPose: commonApi.Pose) => {
const updatePose = (newPose: commonApi.Pose) => {
const x = newPose.getX();
const y = newPose.getY();
const z = newPose.getZ();
const baseMarker = scene.getObjectByName('BaseMarker') ??
await makeMarker(baseMarkerUrl, 'BaseMarker', baseMarkerScalar);
baseMarker.position.set(x + baseMarkerOffset.x, y + baseMarkerOffset.y, z + baseMarkerOffset.z);
baseMarker.position.set(x, y, z).add(baseMarkerOffset);
};
/*
Expand Down Expand Up @@ -243,12 +176,7 @@ const createAxisHelper = (name: string, rotation: number): THREE.AxesHelper => {
};
// create the background gray grid
const createGridHelper = (
points: THREE.Points<THREE.BufferGeometry, THREE.Material | THREE.Material[]>
): THREE.GridHelper => {
points.geometry.computeBoundingBox();
const boundingBox = points.geometry.boundingBox!;
const createGridHelper = (boundingBox: THREE.Box3): THREE.GridHelper => {
const deltaX = Math.abs(boundingBox.max.x - boundingBox.min.x);
const deltaZ = Math.abs(boundingBox.max.z - boundingBox.min.z);
let maxDelta = Math.round(Math.max(deltaX, deltaZ) * gridHelperScalar);
Expand All @@ -270,45 +198,40 @@ const createGridHelper = (
return gridHelper;
};
const updateOrRemoveDestinationMarker = async () => {
const updateOrRemoveDestinationMarker = () => {
if (props.destVector && props.destExists) {
const marker = scene.getObjectByName('DestinationMarker') ??
await makeMarker(destMarkerUrl, 'DestinationMarker', destinationMarkerScalar);
marker.position.set(
props.destVector.x + destinationMarkerOffset.x,
props.destVector.y + destinationMarkerOffset.y,
props.destVector.z + destinationMarkerOffset.z
);
destMarker.visible = true;
destMarker.position.copy(props.destVector).add(destinationMarkerOffset);
}
if (!props.destExists) {
const marker = scene.getObjectByName('DestinationMarker');
if (marker !== undefined) {
scene.remove(marker);
}
destMarker.visible = false;
}
};
const updatePointCloud = (pointcloud: Uint8Array) => {
disposeScene();
const viewHeight = 1;
const viewWidth = viewHeight * 2;
const points = loader.parse(pointcloud.buffer);
pointsMaterial = points.material as THREE.PointsMaterial;
pointsMaterial.sizeAttenuation = false;
pointsMaterial.size = initialPointSize;
points.geometry.computeBoundingSphere();
points.renderOrder = pointsRenderOrder;
const { radius = 1, center = { x: 0, y: 0 } } = points.geometry.boundingSphere ?? {};
camera.position.set(center.x, center.y, 100);
controls.target.set(center.x, center.y, 0);
camera.lookAt(center.x, center.y, 0);
const viewHeight = 1;
const viewWidth = viewHeight * 2;
const aspect = canvas.clientHeight / canvas.clientWidth;
camera.zoom = aspect > 1
? viewHeight / (radius * 2)
: camera.zoom = viewWidth / (radius * 2);
camera.updateProjectionMatrix();
camera.zoom = aspect > 1
? viewHeight / (radius * aspectInverse)
: viewWidth / (radius * aspectInverse);
controls.target.set(center.x, center.y, 0);
controls.maxZoom = radius * 2;
const intersectionPlane = new THREE.Mesh(
Expand All @@ -334,8 +257,10 @@ const updatePointCloud = (pointcloud: Uint8Array) => {
}
}
points.geometry.computeBoundingBox();
// construct grid spaced at 1 meter
const gridHelper = createGridHelper(points);
const gridHelper = createGridHelper(points.geometry.boundingBox!);
// construct axes
const axesPos = createAxisHelper('AxesPos', Math.PI / 2);
Expand All @@ -350,16 +275,28 @@ const updatePointCloud = (pointcloud: Uint8Array) => {
points,
intersectionPlane,
axesPos,
axesNeg
axesNeg,
baseMarker,
destMarker
);
if (props.pose !== undefined) {
updatePose(props.pose!);
}
updateOrRemoveDestinationMarker();
};
let removeUpdate: (() => void) | undefined;
onMounted(() => {
removeUpdate = update(() => {
const { zoom } = camera;
if (pointsMaterial) {
pointsMaterial.size = zoom * cameraScale;
}
});
container?.append(canvas);
start();
Expand All @@ -377,6 +314,7 @@ onMounted(() => {
onUnmounted(() => {
stop();
disposeScene();
removeUpdate?.();
});
watch(() => [props.destVector!.x, props.destVector!.y, props.destExists], updateOrRemoveDestinationMarker);
Expand Down
37 changes: 0 additions & 37 deletions web/frontend/src/lib/base-marker-url.ts

This file was deleted.

Binary file added web/frontend/src/lib/base-marker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions web/frontend/src/lib/base-marker.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
data:image/png;base64,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
20 changes: 0 additions & 20 deletions web/frontend/src/lib/destination-marker-url.ts

This file was deleted.

Binary file added web/frontend/src/lib/destination-marker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6515eb8

Please sign in to comment.