Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 83 additions & 39 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
AgentName,
CurrentConcentration,
InputConcentration,
LayoutType,
Module,
ProductName,
ScatterTrace,
Expand All @@ -22,7 +23,7 @@ import LeftPanel from "./components/main-layout/LeftPanel";
import RightPanel from "./components/main-layout/RightPanel";
import ReactionDisplay from "./components/main-layout/ReactionDisplay";
import ContentPanelTimer from "./components/main-layout/ContentPanelTimer";
import content, { moduleNames } from "./content";
import content, { FIRST_PAGE, moduleNames } from "./content";
import {
PROMPT_TO_ADJUST_B,
DEFAULT_VIEWPORT_SIZE,
Expand All @@ -48,10 +49,8 @@ import LiveSimulationData from "./simulation/LiveSimulationData";
import { PLOT_COLORS } from "./components/plots/constants";
import useModule from "./hooks/useModule";

const ADJUSTABLE_AGENT = AgentName.B;

function App() {
const [page, setPage] = useState(1);
const [page, setPage] = useState(FIRST_PAGE);
const [time, setTime] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [trajectoryStatus, setTrajectoryStatus] = useState(
Expand Down Expand Up @@ -86,12 +85,18 @@ function App() {
const [timeFactor, setTimeFactor] = useState(
LiveSimulationData.INITIAL_TIME_FACTOR
);
const [viewportSize, setViewportSize] = useState(DEFAULT_VIEWPORT_SIZE);
const [viewportWidth, setViewportWidth] = useState(
DEFAULT_VIEWPORT_SIZE.width
);
const [viewportHeight, setViewportHeight] = useState(
DEFAULT_VIEWPORT_SIZE.height
);

Comment on lines +88 to +94
Copy link
Contributor Author

@meganrm meganrm Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • this can be a stand alone fix

/**
* Analysis state
* used to create plots and feedback
*/
const [trajectoryPlotData, setTrajectoryPlotData] =
const [preComputedTrajectoryPlotData, setPreComputedTrajectoryPlotData] =
useState<ScatterTrace[]>();
const [liveConcentration, setLiveConcentration] =
useState<CurrentConcentration>({
Expand Down Expand Up @@ -161,21 +166,22 @@ function App() {
if (!trajectory) {
return null;
}
const longestAxis = Math.max(viewportSize.width, viewportSize.height);
const longestAxis = Math.max(viewportWidth, viewportHeight);
return new BindingSimulator(trajectory, longestAxis / 3);
}, [currentModule, viewportSize, simulationData]);
}, [viewportWidth, viewportHeight, simulationData, currentModule]);

const preComputedPlotDataManager = useMemo(() => {
if (!trajectoryPlotData) {
if (!preComputedTrajectoryPlotData) {
return null;
}
return new PreComputedPlotData(trajectoryPlotData);
}, [trajectoryPlotData]);
return new PreComputedPlotData(preComputedTrajectoryPlotData);
}, [preComputedTrajectoryPlotData]);

useEffect(() => {
if (!clientSimulator) {
return;
}

simulariumController.changeFile(
{
clientSimulator: clientSimulator,
Expand Down Expand Up @@ -244,6 +250,7 @@ function App() {
uniqMeasuredConcentrations.length >= 3
);
}, [hasAValueAboveKd, hasAValueBelowKd, uniqMeasuredConcentrations]);

const handleNewInputConcentration = useCallback(
(name: string, value: number) => {
if (value === 0) {
Expand All @@ -261,6 +268,7 @@ function App() {
name as keyof typeof LiveSimulationData.AVAILABLE_AGENTS;
const agentId = LiveSimulationData.AVAILABLE_AGENTS[agentName].id;
clientSimulator.changeConcentration(agentId, value);

simulariumController.gotoTime(time + 1);
resetCurrentRunAnalysisState();
},
Expand All @@ -281,14 +289,20 @@ function App() {
[AgentName.B]:
LiveSimulationData.INITIAL_CONCENTRATIONS[AgentName.B],
});
// advances time
handleNewInputConcentration(
ADJUSTABLE_AGENT,
LiveSimulationData.ADJUSTABLE_AGENT_MAP[currentModule],
LiveSimulationData.INITIAL_CONCENTRATIONS[AgentName.B]
);
setIsPlaying(false);
clearAllAnalysisState();
setTimeFactor(LiveSimulationData.INITIAL_TIME_FACTOR);
}, [clearAllAnalysisState, handleNewInputConcentration, productName]);
}, [
clearAllAnalysisState,
handleNewInputConcentration,
productName,
currentModule,
]);
// Special events in page navigation
// usePageNumber takes a page number, a conditional and a callback

Expand All @@ -299,7 +313,10 @@ function App() {
// clicked the home button
usePageNumber(
page,
(page) => page === 1 && currentProductConcentrationArray.length > 1,
(page) =>
page === 1 &&
currentModule === 1 &&
currentProductConcentrationArray.length > 1,
Comment on lines +316 to +319
Copy link
Contributor Author

@meganrm meganrm Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • add this to pr

() => {
totalReset();
}
Expand All @@ -313,17 +330,35 @@ function App() {
isPlaying &&
recordedInputConcentration.length > 0 &&
recordedInputConcentration[0] !==
inputConcentration[ADJUSTABLE_AGENT],
inputConcentration[
LiveSimulationData.ADJUSTABLE_AGENT_MAP[currentModule]
],
() => {
setPage(page + 1);
}
);

// handle trajectory changes based on content changes
useEffect(() => {
const currentPage = content[currentModule][page];
const nextPage = content[currentModule][page + 1];
if (!nextPage?.trajectoryUrl && !currentPage.trajectoryUrl) {
if (trajectoryStatus === TrajectoryStatus.LOADED) {
simulariumController.clearFile();
setTrajectoryStatus(TrajectoryStatus.INITIAL);
}
if (
currentPage.layout === LayoutType.LiveSimulation &&
simulariumController.getFile() !== LIVE_SIMULATION_NAME
) {
setTrajectoryName(LIVE_SIMULATION_NAME);
}
}

if (!nextPage) {
return;
}
// if the next page has a trajectory, load it
const url = nextPage.trajectoryUrl;
if (trajectoryStatus === TrajectoryStatus.INITIAL && url) {
const changeTrajectory = async () => {
Expand All @@ -334,7 +369,7 @@ function App() {
await fetch3DTrajectory(
url,
simulariumController,
setTrajectoryPlotData
setPreComputedTrajectoryPlotData
);
setTrajectoryStatus(TrajectoryStatus.LOADED);
};
Expand All @@ -349,15 +384,14 @@ function App() {
totalReset,
]);

usePageNumber(
page,
(page) => page === finalPageNumber,
() => {
if (simulariumController.getFile()) {
simulariumController.clearFile();
}
useEffect(() => {
const { section } = content[currentModule][page];
if (section === Section.Experiment) {
setTimeFactor(LiveSimulationData.DEFAULT_TIME_FACTOR);
} else if (section === Section.Introduction) {
setTimeFactor(LiveSimulationData.INITIAL_TIME_FACTOR);
}
);
}, [currentModule, page]);

const addProductionTrace = (previousConcentration: number) => {
const traces = productOverTimeTraces;
Expand All @@ -376,7 +410,6 @@ function App() {
const handleStartExperiment = () => {
simulariumController.pause();
totalReset();
setTimeFactor(LiveSimulationData.DEFAULT_TIME_FACTOR);
setPage(page + 1);
};

Expand All @@ -386,12 +419,7 @@ function App() {
// 2d trajectory
// switch to orthographic camera
simulariumController.setCameraType(true);
const { section } = content[currentModule][page];
if (section === Section.Experiment) {
setTimeFactor(LiveSimulationData.DEFAULT_TIME_FACTOR);
} else {
setTimeFactor(LiveSimulationData.INITIAL_TIME_FACTOR);
}
setPreComputedTrajectoryPlotData(undefined);
setFinalTime(-1);
} else {
// 3d trajectory
Expand All @@ -407,14 +435,14 @@ function App() {
const handleTimeChange = (timeData: TimeData) => {
const { time } = timeData;
setTime(time);
console.log("new time", time);
// can't use isLastFrame here because the time is not updated
// in state yet
if (finalTime > 0 && time >= finalTime - timeFactor && isPlaying) {
setIsPlaying(false);
}
let concentrations: CurrentConcentration = {};
let previousData = currentProductConcentrationArray;

if (preComputedPlotDataManager) {
if (timeData.time === 0) {
// for the 3D trajectory,
Expand Down Expand Up @@ -482,16 +510,19 @@ function App() {
setEquilibriumFeedbackTimeout("Not yet!");
return false;
}
const adjustableAgentName =
LiveSimulationData.ADJUSTABLE_AGENT_MAP[currentModule];
// this will always be defined for the current run, but since there are
// different agents in each module, typescript fears it will be undefined
const currentInputConcentration = inputConcentration[ADJUSTABLE_AGENT];
const currentInputConcentration =
inputConcentration[adjustableAgentName];
if (currentInputConcentration === undefined) {
return false;
}
const concentrations =
clientSimulator.getCurrentConcentrations(productName);
const productConcentration = concentrations[productName];
const reactantConcentration = concentrations[ADJUSTABLE_AGENT];
const reactantConcentration = concentrations[adjustableAgentName];

const currentTime = indexToTime(
currentProductConcentrationArray.length,
Expand Down Expand Up @@ -538,32 +569,45 @@ function App() {

const { totalMainContentPages } = useModule(currentModule);
const lastPageOfExperiment = page === totalMainContentPages;

const adjustableAgentName =
LiveSimulationData.ADJUSTABLE_AGENT_MAP[currentModule];
return (
<>
<div className="app">
<SimulariumContext.Provider
value={{
trajectoryName,
productName,
adjustableAgentName,
fixedAgentStartingConcentration:
inputConcentration[AgentName.A] || 0,
currentProductionConcentration:
liveConcentration[productName] || 0,
maxConcentration:
simulationData.getMaxConcentration(currentModule),
handleStartExperiment,
section: content[currentModule][page].section,
moduleLength: content[currentModule].length,
module: currentModule,
getAgentColor: simulationData.getAgentColor,
isPlaying,
setIsPlaying,
simulariumController,
handleTimeChange,
page,
setPage,
setCurrentModule,
timeFactor,
timeUnit: simulationData.timeUnit,
handleTrajectoryChange,
viewportSize,
setViewportSize,
viewportSize: {
width: viewportWidth,
height: viewportHeight,
},
setViewportSize: ({ width, height }) => {
setViewportWidth(width);
setViewportHeight(height);
},
recordedConcentrations: recordedInputConcentration,
}}
>
Expand Down Expand Up @@ -611,7 +655,7 @@ function App() {
unbindingEventsOverTime={
unBindingEventsOverTime
}
adjustableAgent={ADJUSTABLE_AGENT}
adjustableAgent={adjustableAgentName}
/>
}
centerPanel={
Expand All @@ -636,7 +680,7 @@ function App() {
handleRecordEquilibrium
}
currentAdjustableAgentConcentration={
inputConcentration[ADJUSTABLE_AGENT] || 0
inputConcentration[adjustableAgentName] || 0
}
equilibriumData={{
inputConcentrations:
Expand Down
1 change: 1 addition & 0 deletions src/components/AdminUi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const AdminUI: React.FC<AdminUIProps> = ({
min={0}
max={totalPages}
step={1}
overRideValue={page}
initialValue={page}
onChange={(_, value): void => {
setPage(value);
Expand Down
3 changes: 2 additions & 1 deletion src/components/LabView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import styles from "./labview.module.css";
import classNames from "classnames";
import ScaleBar from "./ScaleBar";
import VisibilityControl from "./shared/VisibilityControl";
import { Module } from "../types";

const LabView: React.FC = () => {
const { currentProductionConcentration, maxConcentration, page } =
Expand All @@ -24,7 +25,7 @@ const LabView: React.FC = () => {
{ [styles.top]: page === 1 },
])}
>
<VisibilityControl excludedPages={[1]}>
<VisibilityControl excludedPages={{ [Module.A_B_AB]: [1] }}>
<ScaleBar />
</VisibilityControl>
<div className={styles.cuvette}>
Expand Down
11 changes: 9 additions & 2 deletions src/components/PlayButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SimulariumContext } from "../simulation/context";
import ProgressionControl from "./shared/ProgressionControl";
import VisibilityControl from "./shared/VisibilityControl";
import { OverlayButton } from "./shared/ButtonLibrary";
import { Module } from "../types";

const PlayButton: React.FC = () => {
const { isPlaying, setIsPlaying } = useContext(SimulariumContext);
Expand All @@ -16,8 +17,14 @@ const PlayButton: React.FC = () => {
const iconStyle = { fontSize: 26 };

return (
<VisibilityControl excludedPages={[1]}>
<ProgressionControl onPage={[2, 6]}>
<VisibilityControl excludedPages={{ [Module.A_B_AB]: [1] }}>
<ProgressionControl
onPage={{
[Module.A_B_AB]: [2, 6],
[Module.A_C_AC]: [],
[Module.A_B_C_AB_AC]: [],
}}
>
<OverlayButton
onClick={handleClick}
style={{ top: 14, left: 16 }}
Expand Down
9 changes: 8 additions & 1 deletion src/components/RecordEquilibriumButton.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Module } from "../types";
import { PillButton } from "./shared/ButtonLibrary";
import ProgressionControl from "./shared/ProgressionControl";

Expand All @@ -10,7 +11,13 @@ const RecordEquilibriumButton = ({
handleRecordEquilibrium,
}: RecordEquilibriumButtonProps) => {
return (
<ProgressionControl onPage={[FIRST_RECORD_PAGE, SECOND_RECORD_PAGE]}>
<ProgressionControl
onPage={{
[Module.A_B_AB]: [FIRST_RECORD_PAGE, SECOND_RECORD_PAGE],
[Module.A_C_AC]: [1],
[Module.A_B_C_AB_AC]: [],
}}
>
<PillButton onClick={handleRecordEquilibrium}>Record</PillButton>
</ProgressionControl>
);
Expand Down
Loading