Skip to content

Commit

Permalink
Using icon to display robot (#809)
Browse files Browse the repository at this point in the history
* Render image if robot icon exists

Signed-off-by: angatupyry <[email protected]>

* Add scale to robot resource

Signed-off-by: angatupyry <[email protected]>

* Using scale if exists in resource

Signed-off-by: angatupyry <[email protected]>

* Make waypoints transparent

Signed-off-by: angatupyry <[email protected]>

* Adjust text according to its length

Signed-off-by: angatupyry <[email protected]>

* Remove hasOwn property control

Signed-off-by: angatupyry <[email protected]>

---------

Signed-off-by: angatupyry <[email protected]>
  • Loading branch information
Angatupyry authored Oct 30, 2023
1 parent 59065b1 commit db60f9c
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 58 deletions.
26 changes: 15 additions & 11 deletions packages/dashboard/src/components/map-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import {
RobotTableData,
ShapeThreeRendering,
TextThreeRendering,
RobotData,
} from 'react-components';
import { EMPTY, merge, scan, Subscription, switchMap } from 'rxjs';
import appConfig from '../app-config';
import { ResourcesContext } from './app-contexts';
import { AppEvents } from './app-events';
import { createMicroApp } from './micro-app';
import { RmfAppContext } from './rmf-app';
import { RobotData } from './robots-overlay';
import { TrajectoryData } from './trajectories-overlay';
import { WorkcellData } from './workcells-overlay';
import { RobotSummary } from './robots/robot-summary';
Expand All @@ -45,6 +45,7 @@ const TrajectoryUpdateInterval = 2000;
const colorManager = new ColorManager();

const DEFAULT_ZOOM_LEVEL = 20;
const DEFAULT_ROBOT_SCALE = 0.003;

function getRobotId(fleetName: string, robotName: string): string {
return `${fleetName}/${robotName}`;
Expand Down Expand Up @@ -273,6 +274,7 @@ export const MapApp = styled(
name: r,
// no model name
model: '',
scale: resourceManager?.robots.getRobotIconScale(fleetName, r) || DEFAULT_ROBOT_SCALE,
footprint: 0.5,
color: await colorManager.robotPrimaryColor(fleetName, r, ''),
iconPath: (await resourceManager?.robots.getIconPath(fleetName, r)) || undefined,
Expand Down Expand Up @@ -528,16 +530,18 @@ export const MapApp = styled(
linewidth={5}
/>
))}
{!disabledLayers['Robots'] && (
<RobotThree
robots={robots}
robotLocations={robotLocations}
onRobotClick={(_ev, robot) => {
setOpenRobotSummary(true);
setSelectedRobot(robot);
}}
/>
)}
{!disabledLayers['Robots'] &&
robots.map((robot) => (
<RobotThree
key={`${robot.name} ${robot.fleet}`}
robot={robot}
robotLocations={robotLocations}
onRobotClick={(_ev, robot) => {
setOpenRobotSummary(true);
setSelectedRobot(robot);
}}
/>
))}
<ambientLight />
</Canvas>
{openRobotSummary && selectedRobot && (
Expand Down
45 changes: 20 additions & 25 deletions packages/dashboard/src/components/three-fiber/robot-three.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
import { ThreeEvent } from '@react-three/fiber';
import React from 'react';
import { RobotThreeMaker } from 'react-components';
import { RobotThreeMaker, RobotData } from 'react-components';
import { Euler, Vector3 } from 'three';
import { RobotData } from '../robots-overlay';

interface RobotThreeProps {
robots: RobotData[];
robot: RobotData;
robotLocations: Record<string, [number, number, number]>;
onRobotClick?: (ev: ThreeEvent<MouseEvent>, robot: RobotData) => void;
}

export const RobotThree = ({ robots, robotLocations, onRobotClick }: RobotThreeProps) => {
const STANDAR_Z_POSITION = 4;
export const RobotThree = ({ robot, robotLocations, onRobotClick }: RobotThreeProps) => {
const STANDAR_Z_POSITION = 5;
const CIRCLE_SEGMENT = 64;

return (
<>
{robots.map((robot) => {
const robotId = `${robot.fleet}/${robot.name}`;
const robotLocation = robotLocations[robotId];
const rotationZ = robotLocation[2] + Math.PI / 2;
const robotId = `${robot.fleet}/${robot.name}`;
const robotLocation = robotLocations[robotId];
const rotationZ = robotLocation[2] - Math.PI;

const position = new Vector3(robotLocation[0], robotLocation[1], STANDAR_Z_POSITION);

const position = new Vector3(robotLocation[0], robotLocation[1], STANDAR_Z_POSITION);
return (
<React.Fragment key={robotId}>
<RobotThreeMaker
robot={robot}
position={position}
onRobotClick={onRobotClick}
rotation={new Euler(0, 0, rotationZ)}
circleSegment={CIRCLE_SEGMENT}
/>
</React.Fragment>
);
})}
</>
return (
<React.Fragment key={robotId}>
<RobotThreeMaker
robot={robot}
imageUrl={robot.iconPath}
position={position}
onRobotClick={onRobotClick}
rotation={new Euler(0, 0, rotationZ)}
circleSegment={CIRCLE_SEGMENT}
/>
</React.Fragment>
);
};
2 changes: 2 additions & 0 deletions packages/dashboard/src/managers/__mocks__/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ export default function fakeResources(): ResourceConfigurationsType {
hardware_1: [],
station_2: [],
},
scale: 0.01,
},
deliveryRobot: {
icons: { deliveryRobot: '/robots/deliveryRobot/deliveryRobot.png' },
places: {},
scale: 0.01,
},
},
logos: { headerLogo: { icons: { headerLogo: '/logos/header/headerLogo.png' } } },
Expand Down
23 changes: 23 additions & 0 deletions packages/dashboard/src/managers/resource-manager-robots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const debug = Debug('ResourceManager');
export interface RobotResource {
icons: Record<string, string>; // Record<ModelName|FleetName, IconPath>
places: Record<string, string[]>; // Record<Places, Dispensers[]>
scale: number;
}

export class RobotResourceManager {
Expand Down Expand Up @@ -53,6 +54,28 @@ export class RobotResourceManager {
}
};

getRobotIconScale = (fleetName: string, robotModel?: string | undefined): number | null => {
if (!this.fleetExists(fleetName)) {
debug(`failed to load scale for "${fleetName}/${robotModel}" (fleet not in resources)`);
return null;
}

if (!this.robots[fleetName].hasOwnProperty('scale')) {
debug(
`failed to load scale for "${fleetName}/${robotModel}" (fleet/model does not have an scale)`,
);
return null;
}

const robotScale = this.robots[fleetName].scale;

if (!!robotModel) {
return robotScale;
}

return null;
};

getDispensersPerFleet = (fleetName: string, placeName: string): string[] | null => {
if (!this.fleetExists(fleetName) || !this.placesExists(fleetName)) {
debug(
Expand Down
97 changes: 79 additions & 18 deletions packages/react-components/lib/map/three-fiber/robot-three-maker.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
import { Circle, Line, Text } from '@react-three/drei';
import { ThreeEvent } from '@react-three/fiber';
import { Circle, Line } from '@react-three/drei';
import { ThreeEvent, useLoader } from '@react-three/fiber';
import React from 'react';
import { Euler, Vector3 } from 'three';
import { TextThreeRendering } from './text-maker';
import { Euler, Vector3, Texture, TextureLoader, Color } from 'three';
import { TextThreeRendering } from '.';

export interface RobotData {
fleet: string;
name: string;
model: string;
footprint: number;
scale: number;
color: string;
inConflict?: boolean;
iconPath?: string;
}

interface CircleShapeProps {
interface RobotThreeMakerProps {
imageUrl?: string;
robot: RobotData;
position: Vector3;
onRobotClick?: (ev: ThreeEvent<MouseEvent>, robot: RobotData) => void;
rotation: Euler;
onRobotClick?: (ev: ThreeEvent<MouseEvent>) => void;
robot: RobotData;
segment: number;
circleSegment: number;
}

interface RobotThreeMakerProps {
interface RobotImageMakerProps {
imageUrl: string;
robot: RobotData;
position: Vector3;
onRobotClick?: (ev: ThreeEvent<MouseEvent>, robot: RobotData) => void;
rotation: Euler;
circleSegment: number;
}

interface CircleShapeProps {
position: Vector3;
rotation: Euler;
onRobotClick?: (ev: ThreeEvent<MouseEvent>) => void;
robot: RobotData;
segment: number;
}

const CircleShape = ({
Expand Down Expand Up @@ -61,7 +71,48 @@ const CircleShape = ({
);
};

const RobotImageMaker = ({
imageUrl,
position,
rotation,
onRobotClick,
robot,
}: RobotImageMakerProps): JSX.Element => {
const alphaTestThreshold = 0.5;
const texture: Texture | undefined = useLoader(TextureLoader, imageUrl, undefined, (err) => {
console.error(`Error loading image from ${imageUrl}:`, err);
});

if (!texture) {
console.error(`Failed to create image texture with ${robot.iconPath}.`);
return <></>;
}

return (
<>
<mesh
position={position}
rotation={new Euler(0, 0, rotation.z)}
onClick={(ev: ThreeEvent<MouseEvent>) => onRobotClick && onRobotClick(ev, robot)}
>
<planeGeometry
attach="geometry"
args={[texture.image.width * robot.scale, texture.image.height * robot.scale]}
/>
<meshBasicMaterial
attach="material"
map={texture}
color={new Color(robot.color)}
alphaTest={alphaTestThreshold}
toneMapped={false}
/>
</mesh>
</>
);
};

export const RobotThreeMaker = ({
imageUrl,
robot,
position,
onRobotClick,
Expand All @@ -70,14 +121,24 @@ export const RobotThreeMaker = ({
}: RobotThreeMakerProps): JSX.Element => {
return (
<>
<TextThreeRendering position={[position.x, position.y, position.z + 1]} text={robot.name} />
<CircleShape
position={position}
rotation={rotation}
onRobotClick={(ev: ThreeEvent<MouseEvent>) => onRobotClick && onRobotClick(ev, robot)}
robot={robot}
segment={circleSegment}
/>
<TextThreeRendering position={[position.x, position.y + 1, position.z]} text={robot.name} />
{imageUrl ? (
<RobotImageMaker
imageUrl={imageUrl}
position={position}
rotation={rotation}
onRobotClick={(ev: ThreeEvent<MouseEvent>) => onRobotClick && onRobotClick(ev, robot)}
robot={robot}
/>
) : (
<CircleShape
position={position}
rotation={rotation}
onRobotClick={(ev: ThreeEvent<MouseEvent>) => onRobotClick && onRobotClick(ev, robot)}
robot={robot}
segment={circleSegment}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const ShapeThreeRendering = ({
<group position={position}>
<mesh
position={[0, 0, positionZ]}
scale={[0.7, 0.7, 0.7]}
scale={[0.5, 0.5, 0.5]}
onPointerOver={debouncedHandlePointerOver}
onPointerOut={debouncedHandlePointerOut}
>
Expand All @@ -74,7 +74,7 @@ export const ShapeThreeRendering = ({
</Html>
)}
<boxGeometry args={[1.3, 1.3, 1.3]} />
<meshStandardMaterial color={color} opacity={1} />
<meshStandardMaterial color={color} opacity={0.6} transparent />
</mesh>
</group>
)}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-components/lib/map/three-fiber/text-maker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ export const TextThreeRendering = ({ position, text }: TextThreeRenderingProps):
};

const scaleFactor = isHovered ? 2 : 1.0;

const positionX = text && text.length > 5 ? -2 : -1;
return (
<>
<mesh position={position}>
<mesh position={[-1, 0, positionZ]}>
<mesh position={[positionX, 0, positionZ]}>
{text && (
<Html zIndexRange={[0, 0, 1]}>
{text && (
Expand Down

0 comments on commit db60f9c

Please sign in to comment.