diff --git a/examples/src/AgentMetadata.tsx b/examples/src/AgentMetadata.tsx new file mode 100644 index 00000000..bfc15bf3 --- /dev/null +++ b/examples/src/AgentMetadata.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { AgentData } from "../../type-declarations/simularium/types"; + +interface AgentMetadataProps { + agentData: AgentData; +} + +const AgentMetadata = ({ agentData }: AgentMetadataProps): JSX.Element => { + + const getContents = () => { + if (agentData.instanceId === -1) { + return
No agent selected
; + } + return ( +
+
uniqueID: {agentData.instanceId}
+
agentType: {agentData.type}
+
+ position: x = {agentData.x}, y = {agentData.y}, z = + {agentData.z} +
+
+ rotation: x = {agentData.xrot}, y = {agentData.yrot}, z = + {agentData.zrot} +
+
radius: {agentData.cr}
+
+ ); + }; + + return
Agent Metadata: {getContents()}
; +}; + +export default AgentMetadata; diff --git a/examples/src/Viewer.tsx b/examples/src/Viewer.tsx index 1cd2d5ae..887891c1 100644 --- a/examples/src/Viewer.tsx +++ b/examples/src/Viewer.tsx @@ -8,7 +8,7 @@ import type { SelectionStateInfo, SelectionEntry, } from "../../type-declarations"; -import { TrajectoryType } from "../../src/constants"; +import { NULL_AGENT, TrajectoryType } from "../../src/constants"; import SimulariumViewer, { SimulariumController, RenderStyle, @@ -33,6 +33,7 @@ import SimulariumViewer, { // ErrorLevel, // } from "../es"; import "../../style/style.css"; +import { AgentData } from "../../type-declarations/simularium/types"; import PointSimulator from "./simulators/PointSimulator"; import BindingSimulator from "./simulators/BindingSimulator2D"; import PointSimulatorLive from "./simulators/PointSimulatorLive"; @@ -51,6 +52,7 @@ import { } from "./api-settings"; import ConversionForm from "./ConversionForm"; import MetaballSimulator from "./simulators/MetaballSimulator"; +import AgentMetadata from "./AgentMetadata"; let playbackFile = "TEST_LIVEMODE_API"; let queryStringFile = ""; @@ -110,6 +112,7 @@ interface ViewerState { trajectoryTitle: string; initialPlay: boolean; firstFrameTime: number; + followObjectData: AgentData; } interface BaseType { @@ -172,6 +175,7 @@ const initialState: ViewerState = { trajectoryTitle: "", initialPlay: true, firstFrameTime: 0, + followObjectData: NULL_AGENT, }; class Viewer extends React.Component { @@ -386,16 +390,21 @@ class Viewer extends React.Component { public convertFile(obj: Record, fileType: TrajectoryType) { const fileName = uuidv4() + ".simularium"; simulariumController - .convertTrajectory(this.netConnectionSettings, obj, fileType, fileName) + .convertTrajectory( + this.netConnectionSettings, + obj, + fileType, + fileName + ) .then(() => { this.clearPendingFile(); }) .then(() => { simulariumController.changeFile( - { netConnectionSettings: this.netConnectionSettings, }, + { netConnectionSettings: this.netConnectionSettings }, fileName, - true, - ) + true + ); }) .catch((err) => { console.error(err); @@ -410,7 +419,7 @@ class Viewer extends React.Component { const simulariumFile = fileName.includes(".simularium") ? trajectoryFile : null; - this.setState({ initialPlay: true}) + this.setState({ initialPlay: true }); return simulariumController .handleFileChange(simulariumFile, fileName, geoAssets) .catch(console.log); @@ -707,6 +716,10 @@ class Viewer extends React.Component { this.setState({ isRecordingEnabled: value }); }; + public handleFollowObjectData = (agentData: AgentData) => { + this.setState({ followObjectData: agentData }); + }; + public render(): JSX.Element { if (this.state.filePending) { const fileType = this.state.filePending.type; @@ -994,6 +1007,7 @@ class Viewer extends React.Component { } /> )} +
{ ? this.onRecordedMovie : undefined } + onFollowObjectChanged={this.handleFollowObjectData.bind( + this + )} loadInitialData={true} agentColors={this.state.agentColors} showPaths={this.state.showPaths} diff --git a/src/constants.ts b/src/constants.ts index 8947de52..597d18c3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,8 @@ -import { CameraSpec, PerspectiveCameraSpec } from "./simularium/types"; +import { + AgentData, + CameraSpec, + PerspectiveCameraSpec, +} from "./simularium/types"; export const DEFAULT_CAMERA_Z_POSITION = 120; export const DEFAULT_CAMERA_SPEC_PERSPECTIVE: PerspectiveCameraSpec = { @@ -29,3 +33,17 @@ export const enum TrajectoryType { } export const DEFAULT_FRAME_RATE = 60; // frames per second + +export const NULL_AGENT: AgentData = { + "vis-type": -1, + instanceId: -1, + type: -1, + x: 0, + y: 0, + z: 0, + xrot: 0, + yrot: 0, + zrot: 0, + cr: 0, + subpoints: [], +}; diff --git a/src/simularium/VisData.ts b/src/simularium/VisData.ts index 777cc0f3..c025f96b 100644 --- a/src/simularium/VisData.ts +++ b/src/simularium/VisData.ts @@ -14,6 +14,7 @@ import { import { FrontEndError, ErrorLevel } from "./FrontEndError"; import type { ParsedBundle } from "./VisDataParse"; import { parseVisDataMessage } from "./VisDataParse"; +import { NULL_AGENT } from "../constants"; class VisData { private frameCache: AgentData[][]; @@ -47,20 +48,8 @@ class VisData { const parsedAgentData: AgentData[] = []; let j = AGENTS_OFFSET; for (let i = 0; i < expectedNumAgents; i++) { - const agentData: AgentData = { - //TODO use visType in AgentData and convert from "vis-type" here at parse time - "vis-type": -1, - instanceId: -1, - type: -1, - x: 0, - y: 0, - z: 0, - xrot: 0, - yrot: 0, - zrot: 0, - cr: 0, - subpoints: [], - }; + //TODO use visType in AgentData and convert from "vis-type" here at parse time + const agentData: AgentData = NULL_AGENT; for (let k = 0; k < AGENT_OBJECT_KEYS.length; ++k) { agentData[AGENT_OBJECT_KEYS[k]] = floatView[j++]; diff --git a/src/viewport/index.tsx b/src/viewport/index.tsx index fad1ce8a..f7fad64c 100644 --- a/src/viewport/index.tsx +++ b/src/viewport/index.tsx @@ -12,7 +12,7 @@ import { SelectionStateInfo, UIDisplayData, } from "../simularium"; -import { TrajectoryFileInfoAny } from "../simularium/types"; +import { AgentData, TrajectoryFileInfoAny } from "../simularium/types"; import { updateTrajectoryFileInfoFormat } from "../simularium/versionHandlers"; import { FrontEndError, ErrorLevel } from "../simularium/FrontEndError"; import { RenderStyle, VisGeometry, NO_AGENT } from "../visGeometry"; @@ -46,6 +46,7 @@ type ViewportProps = { lockedCamera?: boolean; onRecordedMovie?: (blob: Blob) => void; // providing this callback enables movie recording disableCache?: boolean; + onFollowObjectChanged?: (agentData: AgentData) => void; // passes agent data about the followed agent to the front end } & Partial; const defaultProps = { @@ -570,6 +571,14 @@ class Viewport extends React.Component< } this.visGeometry.setFollowObject(NO_AGENT); } + this.updateFollowObjectData(); + } + + private updateFollowObjectData(): void { + if (this.props.onFollowObjectChanged === undefined) return; + const id = this.visGeometry.getFollowObject(); + const data = this.visGeometry.getObjectData(id); + this.props.onFollowObjectChanged(data); } private handleTimeChange(e: Event): void { @@ -628,6 +637,7 @@ class Viewport extends React.Component< this.dispatchUpdatedTime(visData.currentFrameData); this.visGeometry.update(currentAgents); this.lastRenderedAgentTime = visData.currentFrameData.time; + this.updateFollowObjectData(); } } diff --git a/src/visGeometry/index.ts b/src/visGeometry/index.ts index 6ad77a5f..f9d97c35 100644 --- a/src/visGeometry/index.ts +++ b/src/visGeometry/index.ts @@ -38,7 +38,11 @@ import PDBModel from "./PDBModel"; import AgentPath from "./agentPath"; import { FrontEndError, ErrorLevel } from "../simularium/FrontEndError"; -import { DEFAULT_CAMERA_Z_POSITION, DEFAULT_CAMERA_SPEC } from "../constants"; +import { + DEFAULT_CAMERA_Z_POSITION, + DEFAULT_CAMERA_SPEC, + NULL_AGENT, +} from "../constants"; import { AgentData, AgentDisplayDataWithGeometry, @@ -646,6 +650,14 @@ class VisGeometry { this.focusMode = focus; } + public getObjectData(id: number): AgentData { + const data = this.visAgentInstances.get(id); + if (!data) { + return NULL_AGENT; + } + return data.agentData; + } + public getFollowObject(): number { return this.followObjectId; }