diff --git a/src/App.tsx b/src/App.tsx index b3d8cf77..8d68e80f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Layout, Typography } from "antd"; import { getJobStatus, updateJobStatusTimestamp } from "./utils/firebase"; -import { getFirebaseRecipe, jsonToString } from "./utils/recipeLoader"; +import { getFirebaseRecipe, recipeToString } from "./utils/recipeLoader"; import { getSubmitPackingUrl, JOB_STATUS } from "./constants/aws"; import { useJobId, @@ -43,7 +43,7 @@ function App() { recipeString: string ): Promise => { const originalRecipe = await getFirebaseRecipe(recipeId); - return !(jsonToString(originalRecipe) == recipeString); + return !(recipeToString(originalRecipe) == recipeString); }; const submitRecipe = async ( diff --git a/src/components/InputSwitch/index.tsx b/src/components/InputSwitch/index.tsx index 952574bb..bc2e1332 100644 --- a/src/components/InputSwitch/index.tsx +++ b/src/components/InputSwitch/index.tsx @@ -49,7 +49,7 @@ const InputSwitch = (props: InputSwitchProps): JSX.Element => { } if (typeof value == "number") { value = value * conversion; - value = Number(value.toFixed(4)); + value = Number(value.toFixed(2)); } return value; }, [getCurrentValue, id, min, conversion, dataType]); diff --git a/src/state/store.ts b/src/state/store.ts index 48382aed..252dba26 100644 --- a/src/state/store.ts +++ b/src/state/store.ts @@ -2,7 +2,7 @@ import { create } from "zustand"; import { subscribeWithSelector } from "zustand/middleware"; import { isEmpty, isEqual, get as lodashGet } from "lodash-es"; import { PackingResult, RecipeData, RecipeManifest } from "../types"; -import { jsonToString } from "../utils/recipeLoader"; +import { recipeToString } from "../utils/recipeLoader"; import { getRecipeDataFromFirebase, getRecipeManifestFromFirebase, @@ -236,7 +236,7 @@ export const useRecipeStore = create()( edits ); if (!recipeObject) return; - const recipeString = jsonToString(recipeObject); + const recipeString = recipeToString(recipeObject); set({ isPacking: true }); try { await callback(s.selectedRecipeId, configId, recipeString); diff --git a/src/test/recipeLoader.test.ts b/src/test/recipeLoader.test.ts index e1e01c07..d8d95404 100644 --- a/src/test/recipeLoader.test.ts +++ b/src/test/recipeLoader.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import fs from 'fs'; -import { isFirebaseRef, getFirebaseRecipe, jsonToString } from '../utils/recipeLoader'; +import { isFirebaseRef, getFirebaseRecipe, recipeToString } from '../utils/recipeLoader'; test('isFirebaseRef detects Firebase references correctly', () => { expect(isFirebaseRef('firebase:recipes/some_id')).toBe(true); @@ -14,7 +14,7 @@ test('isFirebaseRef detects Firebase references correctly', () => { test('getFirebaseRecipe works as expected for ER_peroxisome_v_struct_gradient_370574', async () => { const recipeId = 'ER_peroxisome_v_struct_gradient_370574'; const recipeJson = await getFirebaseRecipe(recipeId); - const recipeString = jsonToString(recipeJson); + const recipeString = recipeToString(recipeJson); expect(recipeString).toBeDefined(); expect(typeof recipeString).toBe('string'); @@ -30,7 +30,7 @@ test('getFirebaseRecipe works as expected for ER_peroxisome_v_struct_gradient_37 test('getFirebaseRecipe works as expected for one_sphere', async () => { const recipeId = 'one_sphere_v_1.0.0'; const recipeJson = await getFirebaseRecipe(recipeId); - const recipeString = jsonToString(recipeJson); + const recipeString = recipeToString(recipeJson); expect(recipeString).toBeDefined(); expect(typeof recipeString).toBe('string'); diff --git a/src/test/test-files/ER_peroxisome.json b/src/test/test-files/ER_peroxisome.json index 96dad222..b41cb835 100644 --- a/src/test/test-files/ER_peroxisome.json +++ b/src/test/test-files/ER_peroxisome.json @@ -132,54 +132,6 @@ } }, "gradients": { - "apical_gradient": { - "description": "gradient based on distance from a plane", - "invert": null, - "mode": "vector", - "mode_settings": { - "center": [0, 0, 106.875], - "direction": [0, 0, 1] - }, - "name": "apical_gradient", - "pick_mode": "linear", - "reversed": false, - "weight_mode": "exponential", - "weight_mode_settings": { - "decay_length": 0.1 - } - }, - "membrane_gradient": { - "description": "gradient based on distance from the surface of the membrane mesh", - "invert": null, - "mode": "surface", - "mode_settings": { - "object": "membrane", - "scale_to_next_surface": false - }, - "name": "membrane_gradient", - "pick_mode": "linear", - "reversed": false, - "weight_mode": "exponential", - "weight_mode_settings": { - "decay_length": 0.01 - } - }, - "nucleus_gradient": { - "description": "gradient based on distance from the surface of the nucleus mesh", - "invert": null, - "mode": "surface", - "mode_settings": { - "object": "nucleus", - "scale_to_next_surface": false - }, - "name": "nucleus_gradient", - "pick_mode": "linear", - "reversed": false, - "weight_mode": "exponential", - "weight_mode_settings": { - "decay_length": 0.1 - } - }, "struct_gradient": { "name": "struct_gradient", "invert": null, diff --git a/src/utils/recipeLoader.ts b/src/utils/recipeLoader.ts index c133f037..a2eea95c 100644 --- a/src/utils/recipeLoader.ts +++ b/src/utils/recipeLoader.ts @@ -328,9 +328,34 @@ const getFirebaseRecipe = async (name: string): Promise => { return unpackedRecipe; } -const jsonToString = (json: ViewableRecipe): string => { - return JSON.stringify(json, null, 2); +const recipeToString = (rec: ViewableRecipe): string => { + // Deep copy recipe to avoid mutating original object + const recipe: ViewableRecipe = structuredClone(rec); + + // Collect a list of gradients that are referenced by objects the recipe + const referencedGradients: Set = new Set(); + if (recipe.objects) { + for (const obj of Object.values(recipe.objects)) { + if (obj.packing_mode === "gradient" && obj.gradient) { + referencedGradients.add(obj.gradient); + } else if (obj.packing_mode === "random" && obj.gradient) { + // If packing mode is random, gradient field is irrelevant + // and should be cleared + delete obj.gradient; + } + } + } + + // If the recipe has gradients that aren't referenced in any objects, delete them + if (recipe.gradients) { + for (const gradientName of Object.keys(recipe.gradients)) { + if (!referencedGradients.has(gradientName)) { + delete recipe.gradients[gradientName]; + } + } + } + return JSON.stringify(recipe, null, 2); } -export { getFirebaseRecipe, isFirebaseRef, jsonToString }; \ No newline at end of file +export { getFirebaseRecipe, isFirebaseRef, recipeToString }; \ No newline at end of file