diff --git a/CHANGELOG.md b/CHANGELOG.md index eb630597..a1f5a82e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,29 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [v3.8.1](https://github.com/simularium/simularium-viewer/compare/v3.7.4...v3.8.1) +#### [v3.8.3](https://github.com/simularium/simularium-viewer/compare/v3.8.2...v3.8.3) + +- Fix/null agent function [`#396`](https://github.com/simularium/simularium-viewer/pull/396) +- spread null agent object when parsing frames [`#395`](https://github.com/simularium/simularium-viewer/pull/395) +- send selected agent data to front end [`#392`](https://github.com/simularium/simularium-viewer/pull/392) + +#### [v3.8.2](https://github.com/simularium/simularium-viewer/compare/v3.8.1...v3.8.2) + +> 10 June 2024 + +- Fix/remove simularium engine specific code [`#391`](https://github.com/simularium/simularium-viewer/pull/391) + +#### [v3.8.1](https://github.com/simularium/simularium-viewer/compare/v3.8.0...v3.8.1) + +> 3 June 2024 - With octopus, we can assume frame per message [`#390`](https://github.com/simularium/simularium-viewer/pull/390) - update cache setting [`#388`](https://github.com/simularium/simularium-viewer/pull/388) + +#### [v3.8.0](https://github.com/simularium/simularium-viewer/compare/v3.7.4...v3.8.0) + +> 23 May 2024 + - Fix/plotdata [`#386`](https://github.com/simularium/simularium-viewer/pull/386) - Feature/trim cache head [`#381`](https://github.com/simularium/simularium-viewer/pull/381) diff --git a/README.md b/README.md index adad8088..79ce22f4 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ This will run the example in `/examples/src/`, demonstrating the viewer's functi | transpileES | run babel on `src` directory; _do not_ transpile `import/export` statements for an ES module compatible build (used by bundlers for tree-shaking) | | test | run `jest`; searches for any files matching the pattern "src/\*_/_.test.js" | | typeCheck | run `tsc` in type-check only mode | -| start | runs an example app from `examples` for testing. Runs at `localhost:8080/public/`. Use `--octopus` to connect to octopus backend and/or `--localserver` to run backend locally. With no flags, this script will default to using remote simularium-engine as backend | +| start | runs an example app from `examples` for testing. Runs at `localhost:8080/public/`. Use `--localserver` to run backend locally. With no flags, this script will default to using the staging octopus server as backend | --- diff --git a/examples/package-lock.json b/examples/package-lock.json index b5cc848b..6b8432d9 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -828,12 +828,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1767,9 +1767,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4914,9 +4914,9 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" 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 cf7c1841..80c8a803 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 { nullAgent, 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 { @@ -138,7 +141,6 @@ interface CustomType { interface InputParams { localBackendServer: boolean; - useOctopus: boolean; } const simulariumController = new SimulariumController({}); @@ -173,6 +175,7 @@ const initialState: ViewerState = { trajectoryTitle: "", initialPlay: true, firstFrameTime: 0, + followObjectData: nullAgent(), }; class Viewer extends React.Component { @@ -197,22 +200,11 @@ class Viewer extends React.Component { this.netConnectionSettings = { serverIp: "0.0.0.0", serverPort: 8765, - useOctopus: props.useOctopus, - secureConnection: props.useOctopus, }; - } else if (props.useOctopus) { + } else { this.netConnectionSettings = { serverIp: "staging-simularium-ecs.allencell.org", serverPort: 443, - useOctopus: true, - secureConnection: true, - }; - } else { - this.netConnectionSettings = { - serverIp: "staging-node1-agentviz-backend.cellexplore.net", - serverPort: 9002, - secureConnection: true, - useOctopus: false, }; } } @@ -398,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); @@ -422,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); @@ -719,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; @@ -1006,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/examples/src/index.tsx b/examples/src/index.tsx index 098b5ff4..0fe730eb 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -3,7 +3,6 @@ import { createRoot } from "react-dom/client"; import Viewer from './Viewer'; -declare const SIMULARIUM_USE_OCTOPUS: boolean; declare const SIMULARIUM_USE_LOCAL_BACKEND: boolean; const container: HTMLElement | null = document.getElementById("root"); @@ -11,7 +10,6 @@ const container: HTMLElement | null = document.getElementById("root"); const root = createRoot(container!); root.render( ); diff --git a/examples/webpack.dev.js b/examples/webpack.dev.js index 937c2fbe..020ed66d 100644 --- a/examples/webpack.dev.js +++ b/examples/webpack.dev.js @@ -28,7 +28,6 @@ module.exports = { ], }), new webpack.DefinePlugin({ - SIMULARIUM_USE_OCTOPUS: Boolean(process.env.npm_config_octopus), SIMULARIUM_USE_LOCAL_BACKEND: Boolean(process.env.npm_config_localserver), }), ], diff --git a/package-lock.json b/package-lock.json index da1bb967..ed91bbef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@aics/simularium-viewer", - "version": "3.8.1", + "version": "3.8.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@aics/simularium-viewer", - "version": "3.8.1", + "version": "3.8.3", "license": "MIT", "dependencies": { "@babel/plugin-transform-runtime": "^7.23.6", @@ -16661,9 +16661,9 @@ } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/package.json b/package.json index be621153..2e67f737 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aics/simularium-viewer", - "version": "3.8.1", + "version": "3.8.3", "description": "An npm package to view simulations.", "main": "es/index.js", "module": "es/index.js", diff --git a/src/constants.ts b/src/constants.ts index 8947de52..7f53a886 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,19 @@ export const enum TrajectoryType { } export const DEFAULT_FRAME_RATE = 60; // frames per second + +export const nullAgent = (): AgentData => { + return { + visType: -1, + instanceId: -1, + type: -1, + x: 0, + y: 0, + z: 0, + xrot: 0, + yrot: 0, + zrot: 0, + cr: 0, + subpoints: [], + }; +}; diff --git a/src/controller/index.ts b/src/controller/index.ts index 153af1e9..f3bacb4f 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -156,11 +156,7 @@ export default class SimulariumController { this.onError ); this.remoteWebsocketClient = webSocketClient; - this.simulator = new RemoteSimulator( - webSocketClient, - !!netConnectionConfig.useOctopus, - this.onError - ); + this.simulator = new RemoteSimulator(webSocketClient, this.onError); this.simulator.setTrajectoryDataHandler( this.visData.parseAgentsFromNetData.bind(this.visData) ); diff --git a/src/index.ts b/src/index.ts index fa7a74c5..7625c064 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,5 +28,6 @@ export { } from "./simularium"; export { compareTimes, loadSimulariumFile } from "./util"; export { DEFAULT_CAMERA_SPEC } from "./constants"; +export { AgentData } from "./simularium/types"; export default Viewport; diff --git a/src/simularium/RemoteSimulator.ts b/src/simularium/RemoteSimulator.ts index 073b7d7e..ad0bef24 100644 --- a/src/simularium/RemoteSimulator.ts +++ b/src/simularium/RemoteSimulator.ts @@ -12,15 +12,8 @@ import { ISimulator } from "./ISimulator"; import { TrajectoryFileInfoV2, VisDataMessage } from "./types"; import { TrajectoryType } from "../constants"; -const enum PlayBackType { - ID_LIVE_SIMULATION = 0, - ID_PRE_RUN_SIMULATION = 1, - ID_TRAJECTORY_FILE_PLAYBACK = 2, - // insert new values here before LENGTH - LENGTH, -} -// a RemoteSimulator is a ISimulator that connects to the Simularium Engine -// back end server and plays back a trajectory specified in the NetConnectionParams +// a RemoteSimulator is a ISimulator that connects to the Octopus backend server +// and plays back a trajectory specified in the NetConnectionParams export class RemoteSimulator implements ISimulator { public webSocketClient: WebsocketClient; protected logger: ILogger; @@ -29,16 +22,13 @@ export class RemoteSimulator implements ISimulator { public healthCheckHandler: () => void; protected lastRequestedFile: string; public handleError: (error: FrontEndError) => void | (() => void); - protected useOctopus: boolean; public constructor( webSocketClient: WebsocketClient, - useOctopus: boolean, errorHandler?: (error: FrontEndError) => void ) { this.webSocketClient = webSocketClient; this.lastRequestedFile = ""; - this.useOctopus = useOctopus; this.handleError = errorHandler || (() => { @@ -260,69 +250,24 @@ export class RemoteSimulator implements ISimulator { /** * WebSocket Simulation Control - * - * Simulation Run Modes: - * Live : Results are sent as they are calculated - * Pre-Run : All results are evaluated, then sent piecemeal - * Trajectory File: No simulation run, stream a result file piecemeal - * */ public startRemoteSimPreRun( - timeStep: number, - numTimeSteps: number - ): Promise { - const jsonData = { - msgType: NetMessageEnum.ID_VIS_DATA_REQUEST, - mode: PlayBackType.ID_PRE_RUN_SIMULATION, - timeStep: timeStep, - numTimeSteps: numTimeSteps, - }; - - return this.connectToRemoteServer() - .then(() => { - this.webSocketClient.sendWebSocketRequest( - jsonData, - "Start Simulation Pre-Run" - ); - }) - .catch((e) => { - throw new FrontEndError(e.message, ErrorLevel.ERROR); - }); + _timeStep: number, + _numTimeSteps: number + ): void { + // not implemented } - public startRemoteSimLive(): Promise { - const jsonData = { - msgType: NetMessageEnum.ID_VIS_DATA_REQUEST, - mode: PlayBackType.ID_LIVE_SIMULATION, - }; - - return this.connectToRemoteServer() - .then(() => { - this.webSocketClient.sendWebSocketRequest( - jsonData, - "Start Simulation Live" - ); - }) - .catch((e) => { - throw new FrontEndError(e.message, ErrorLevel.ERROR); - }); + public startRemoteSimLive(): void { + // not implemented } public startRemoteTrajectoryPlayback(fileName: string): Promise { this.lastRequestedFile = fileName; - let jsonData; - if (this.useOctopus) { - jsonData = { - msgType: NetMessageEnum.ID_INIT_TRAJECTORY_FILE, - fileName: fileName, - }; - } else { - jsonData = { - msgType: NetMessageEnum.ID_VIS_DATA_REQUEST, - mode: PlayBackType.ID_TRAJECTORY_FILE_PLAYBACK, - "file-name": fileName, - }; - } + const jsonData = { + msgType: NetMessageEnum.ID_INIT_TRAJECTORY_FILE, + fileName: fileName, + }; // begins a stream which will include a TrajectoryFileInfo and a series of VisDataMessages // Note that it is possible for the first vis data to arrive before the TrajectoryFileInfo... @@ -375,7 +320,6 @@ export class RemoteSimulator implements ISimulator { this.webSocketClient.sendWebSocketRequest( { msgType: NetMessageEnum.ID_VIS_DATA_REQUEST, - mode: PlayBackType.ID_TRAJECTORY_FILE_PLAYBACK, frameNumber: startFrameNumber, fileName: this.lastRequestedFile, }, diff --git a/src/simularium/VisData.ts b/src/simularium/VisData.ts index 777cc0f3..d6dd1385 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 { nullAgent } from "../constants"; class VisData { private frameCache: AgentData[][]; @@ -47,20 +48,7 @@ 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: [], - }; + const agentData: AgentData = nullAgent(); for (let k = 0; k < AGENT_OBJECT_KEYS.length; ++k) { agentData[AGENT_OBJECT_KEYS[k]] = floatView[j++]; diff --git a/src/simularium/WebsocketClient.ts b/src/simularium/WebsocketClient.ts index 73c9fde0..7e465bd2 100644 --- a/src/simularium/WebsocketClient.ts +++ b/src/simularium/WebsocketClient.ts @@ -80,15 +80,12 @@ export const CONNECTION_FAIL_MSG = export interface NetConnectionParams { serverIp?: string; serverPort?: number; - secureConnection?: boolean; - useOctopus?: boolean; } export class WebsocketClient { private webSocket: WebSocket | null; private serverIp: string; private serverPort: number; - private secureConnection: boolean; public connectionTimeWaited: number; public connectionRetries: number; protected jsonMessageHandlers: Map void>; @@ -114,10 +111,6 @@ export class WebsocketClient { >(); this.serverIp = opts && opts.serverIp ? opts.serverIp : "localhost"; this.serverPort = opts && opts.serverPort ? opts.serverPort : 9002; - this.secureConnection = - opts && opts.secureConnection !== undefined - ? opts.secureConnection - : true; this.connectionTimeWaited = 0; this.connectionRetries = 0; this.handleError = @@ -260,9 +253,7 @@ export class WebsocketClient { } public getIp(): string { - return `${this.secureConnection ? "wss" : "ws"}://${this.serverIp}:${ - this.serverPort - }/`; + return `wss://${this.serverIp}:${this.serverPort}/`; } public async waitForWebSocket(timeout: number): Promise { diff --git a/src/simularium/types.ts b/src/simularium/types.ts index 9c92c6ba..21be13fe 100644 --- a/src/simularium/types.ts +++ b/src/simularium/types.ts @@ -154,8 +154,7 @@ export interface FileReturn { // IMPORTANT: Order of this array needs to perfectly match the incoming data. export const AGENT_OBJECT_KEYS = [ - // TODO: convert "vis-type" to visType at parse time - "vis-type", + "visType", "instanceId", "type", "x", @@ -179,7 +178,7 @@ export interface AgentData { yrot: number; zrot: number; instanceId: number; - ["vis-type"]: number; + visType: number; type: number; cr: number; subpoints: number[]; diff --git a/src/test/DummyRemoteSimulator.ts b/src/test/DummyRemoteSimulator.ts index 0d556fa8..0559cdd2 100644 --- a/src/test/DummyRemoteSimulator.ts +++ b/src/test/DummyRemoteSimulator.ts @@ -21,7 +21,7 @@ export class DummyRemoteSimulator extends RemoteSimulator { public constructor(opts: NetConnectionParams) { const webSocketClient = new WebsocketClient(opts); - super(webSocketClient, true); + super(webSocketClient); this.webSocketClient = webSocketClient; this.isStreamingData = false; diff --git a/src/test/RemoteSimulator.test.ts b/src/test/RemoteSimulator.test.ts index beb3a989..878d0045 100644 --- a/src/test/RemoteSimulator.test.ts +++ b/src/test/RemoteSimulator.test.ts @@ -19,7 +19,7 @@ describe("RemoteSimulator", () => { describe("createWebSocket", () => { test("creates a WebSocket object", () => { const websocketClient = new WebsocketClient(CONNECTION_SETTINGS); - const simulator = new RemoteSimulator(websocketClient, true); + const simulator = new RemoteSimulator(websocketClient); expect(simulator.socketIsValid()).toBe(false); websocketClient.createWebSocket(simulator.getIp()); @@ -32,7 +32,7 @@ describe("RemoteSimulator", () => { test("returns true if connection succeeds within allotted time with no retries", async () => { const websocketClient = new WebsocketClient(CONNECTION_SETTINGS); - const simulator = new RemoteSimulator(websocketClient, true); + const simulator = new RemoteSimulator(websocketClient); jest.spyOn(websocketClient, "waitForWebSocket").mockResolvedValue( true ); @@ -49,7 +49,7 @@ describe("RemoteSimulator", () => { }); test("returns false if connection does not succeed within allotted time and number of retries", async () => { const websocketClient = new WebsocketClient(CONNECTION_SETTINGS); - const simulator = new RemoteSimulator(websocketClient, true); + const simulator = new RemoteSimulator(websocketClient); jest.spyOn(websocketClient, "waitForWebSocket").mockResolvedValue( false ); @@ -67,7 +67,7 @@ describe("RemoteSimulator", () => { }); test("returns true if connection succeeds on the retry", async () => { const websocketClient = new WebsocketClient(CONNECTION_SETTINGS); - const simulator = new RemoteSimulator(websocketClient, true); + const simulator = new RemoteSimulator(websocketClient); const waitForWebSocket = jest.spyOn( websocketClient, "waitForWebSocket" @@ -116,7 +116,7 @@ describe("RemoteSimulator", () => { describe("startRemoteTrajectoryPlayback", () => { test("does not throw error if connectToRemoteServer succeeds", async () => { const websocketClient = new WebsocketClient(CONNECTION_SETTINGS); - const simulator = new RemoteSimulator(websocketClient, true); + const simulator = new RemoteSimulator(websocketClient); jest.spyOn(simulator, "connectToRemoteServer").mockResolvedValue( CONNECTION_SUCCESS_MSG ); @@ -130,7 +130,7 @@ describe("RemoteSimulator", () => { }); test("throws error emitted by connectToRemoteServer as a FrontEndError if connection fails", async () => { const websocketClient = new WebsocketClient(CONNECTION_SETTINGS); - const simulator = new RemoteSimulator(websocketClient, true); + const simulator = new RemoteSimulator(websocketClient); jest.spyOn(simulator, "connectToRemoteServer").mockRejectedValue( new Error("Mock error message") ); diff --git a/src/test/VisData.test.ts b/src/test/VisData.test.ts index 474e9641..aea69945 100644 --- a/src/test/VisData.test.ts +++ b/src/test/VisData.test.ts @@ -47,7 +47,7 @@ const parsedData = [ subpoints: [], type: 7, instanceId: 0, - "vis-type": 1000, + visType: 1000, x: 1, xrot: 0, y: 1, @@ -63,7 +63,7 @@ const parsedData = [ subpoints: [], type: 7, instanceId: 0, - "vis-type": 1000, + visType: 1000, x: 2, xrot: 0, y: 2, @@ -79,7 +79,7 @@ const parsedData = [ subpoints: [], type: 7, instanceId: 0, - "vis-type": 1000, + visType: 1000, x: 3, xrot: 0, y: 3, @@ -95,7 +95,7 @@ const parsedData = [ subpoints: [], type: 7, instanceId: 0, - "vis-type": 1000, + visType: 1000, x: 4, xrot: 0, y: 4, @@ -111,7 +111,7 @@ const parsedData = [ subpoints: [], type: 7, instanceId: 0, - "vis-type": 1000, + visType: 1000, x: 5, xrot: 0, y: 5, @@ -126,7 +126,7 @@ describe("VisData module", () => { describe("VisData parse", () => { test("it returns an array of objects of agent data and time stamp data", () => { const testData = [ - 10, //"vis-type", + 10, //"visType", 15, //"instanceId", 20, //"type", 30, //"x", @@ -166,7 +166,7 @@ describe("VisData module", () => { subpoints: [60, 61, 62], //"subpoint-1", "subpoint-2", "subpoint-3"], type: 20, //"type", instanceId: 15, //"instanceId", - "vis-type": 10, //"vis-type", + visType: 10, //"visType", x: 30, //"x", xrot: 40, //"xrot", y: 31, //"y", @@ -179,7 +179,7 @@ describe("VisData module", () => { }); test("it throws an error if number of supoints does not match the nSubpoints value", () => { const tooShort = [ - 10, //"vis-type", + 10, //"visType", 15, //"instanceId", 20, //"type", 30, //"x", @@ -209,7 +209,7 @@ describe("VisData module", () => { fileName: "", }; const tooLong = [ - 10, //"vis-type", + 10, //"visType", 15, //"instanceId", 20, //"type", 30, //"x", @@ -272,7 +272,7 @@ describe("VisData module", () => { subpoints: [], type: 7, instanceId: 0, - "vis-type": 1000, + visType: 1000, x: 1, xrot: 0, y: 1, diff --git a/src/viewport/index.tsx b/src/viewport/index.tsx index d274bef6..5a6207f3 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 { @@ -54,6 +54,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 sessionUIData?: UIDisplayData; } & Partial; @@ -580,6 +581,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 { @@ -657,6 +666,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/VisAgent.ts b/src/visGeometry/VisAgent.ts index 3012ed05..a6f021e8 100644 --- a/src/visGeometry/VisAgent.ts +++ b/src/visGeometry/VisAgent.ts @@ -31,7 +31,7 @@ export default class VisAgent { yrot: 0, zrot: 0, instanceId: NO_AGENT, - "vis-type": VisTypes.ID_VIS_TYPE_DEFAULT, + visType: VisTypes.ID_VIS_TYPE_DEFAULT, type: 0, cr: 1.0, subpoints: [], @@ -62,7 +62,7 @@ export default class VisAgent { yrot: 0, zrot: 0, instanceId: NO_AGENT, - "vis-type": VisTypes.ID_VIS_TYPE_DEFAULT, + visType: VisTypes.ID_VIS_TYPE_DEFAULT, type: 0, cr: 1.0, subpoints: [], @@ -165,7 +165,7 @@ export default class VisAgent { this.agentData.z ); if ( - this.agentData["vis-type"] === VisTypes.ID_VIS_TYPE_FIBER && + this.agentData.visType === VisTypes.ID_VIS_TYPE_FIBER && this.fiberCurve ) { return this.fiberCurve.getPoint(0.5).add(pos); diff --git a/src/visGeometry/index.ts b/src/visGeometry/index.ts index 00c5c84b..fc957e52 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, + nullAgent, +} from "../constants"; import { AgentData, AgentDisplayDataWithGeometry, @@ -647,6 +651,14 @@ class VisGeometry { this.focusMode = focus; } + public getObjectData(id: number): AgentData { + const data = this.visAgentInstances.get(id); + if (!data) { + return nullAgent(); + } + return data.agentData; + } + public getFollowObject(): number { return this.followObjectId; } @@ -1549,7 +1561,7 @@ class VisGeometry { } agents.forEach((agentData) => { - const visType = agentData["vis-type"]; + const visType = agentData.visType; const instanceId = agentData.instanceId; const typeId = agentData.type; lastx = agentData.x;