diff --git a/embryo-frontend/src/pages/EmbryoGeneration.tsx b/embryo-frontend/src/pages/EmbryoGeneration.tsx index 87c8af8..4a9e23d 100644 --- a/embryo-frontend/src/pages/EmbryoGeneration.tsx +++ b/embryo-frontend/src/pages/EmbryoGeneration.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef, useCallback } from "react"; +import React, { useState, useEffect, useRef, useCallback, useMemo } from "react"; import axios from "axios"; import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; @@ -34,10 +34,23 @@ interface AnimationSectionConfig { }; layout: LayoutConfig; } +const CELL_COLORS = [ + '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', + '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9', + '#F8C471', '#82E0AA', '#F1948A', '#85CDCE', '#D7BDE2', + '#A9DFBF', '#F9E79F', '#D5A6BD', '#AED6F1', '#E8DAEF', + '#FF9A8B', '#FF6CAB', '#C56CF0', '#6C5CE7', '#81ECEC', + '#74B9FF', '#A8E6CF', '#DCEDC1', '#FFD3B6', '#FFAAA5', + '#FFC3A0', '#FF677D', '#B2EBF2', '#80CBC4', '#C5E1A5', + '#FFF176', '#FFD54F', '#FFB347', '#B39DDB', '#9FA8DA', + '#90CAF9', '#64B5F6', '#4DD0E1', '#4DB6AC', '#81C784', + '#A5D6A7', '#E6EE9C', '#DCE775', '#FF8A65', '#F06292' +]; + const ANIMATION_SECTIONS: AnimationSectionConfig[] = [ { id: "human-cs", - title: "Human Carnegie stages", + title: "Human gastrulation", description: "CS7 and CS8 point clouds converge into CS7.5 to highlight human embryonic development.", stages: { sourceLeft: "CS7", @@ -59,7 +72,7 @@ const ANIMATION_SECTIONS: AnimationSectionConfig[] = [ }, { id: "mouse-early", - title: "Mouse early gastrulation", + title: "Mouse organogenesis", description: "Mouse 7.5 and 7.75 datasets merge toward stage 8.5, mirroring the human pipeline.", stages: { // sourceLeft: "GSM9046243_Embryo_E7.5_stereo_rep1", @@ -98,7 +111,7 @@ const EmbryoGeneration: React.FC = () => {

Embryo Generation

- Observe cell migration and merging across human CS7/CS8/CS7.5 and mouse 7.5/7.75/8.5 datasets with the same control workflow. + Observe cell migration and merging across human CS7/CS8/CS7.5 and mouse 7.5/7.75/8.0 datasets with the same control workflow.

@@ -136,7 +149,7 @@ interface EmbryoAnimationSectionProps { } const EmbryoAnimationSection: React.FC = ({ config }) => { - const { stages, title, description, layout } = config; + const { stages, title, layout } = config; const { sourceLeft, sourceRight, target } = stages; const [sourceLeftData, setSourceLeftData] = useState([]); const [sourceRightData, setSourceRightData] = useState([]); @@ -206,15 +219,14 @@ const EmbryoAnimationSection: React.FC = ({ config setAnimationProgress(0); }; - const datasetLabel = `${sourceLeft} + ${sourceRight} -> ${target}`; const progressPercent = Math.round(animationProgress * 100); return (

{title}

-

{description}

-

Dataset: {datasetLabel}

+ {/*

{description}

+

Dataset: {datasetLabel}

*/}
= ({ sourceL const animationIdRef = useRef(null); const animationStateRef = useRef(null); - const { leftOffsetX, rightOffsetX, targetOffsetX = 0, cameraParams, transFunc = null } = layout; + const { leftOffsetX, rightOffsetX, targetOffsetX = 0, cameraParams, transFunc } = layout; if (transFunc) { for (let i = 0; i < sourceLeftData.length; i++) { @@ -339,17 +351,20 @@ const UnifiedEmbryoAnimation: React.FC = ({ sourceL } } + const legendEntries = useMemo(() => { + const allData = [...sourceLeftData, ...sourceRightData, ...targetData]; + const cellTypes = Array.from(new Set(allData.map(p => p.value as string))).sort(); + return cellTypes.map((type, idx) => ({ + type, + color: CELL_COLORS[idx % CELL_COLORS.length] + })); + }, [sourceLeftData, sourceRightData, targetData]); + const getColorMap = (allData: Point[]) => { const cellTypes = Array.from(new Set(allData.map(p => p.value as string))).sort(); - const colors = [ - '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', - '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9', - '#F8C471', '#82E0AA', '#F1948A', '#85CDCE', '#D7BDE2', - '#A9DFBF', '#F9E79F', '#D5A6BD', '#AED6F1', '#E8DAEF' - ]; const colorMap = new Map(); cellTypes.forEach((type, idx) => { - colorMap.set(type, new THREE.Color(colors[idx % colors.length])); + colorMap.set(type, new THREE.Color(CELL_COLORS[idx % CELL_COLORS.length])); }); return colorMap; }; @@ -409,7 +424,7 @@ const UnifiedEmbryoAnimation: React.FC = ({ sourceL sceneRef.current = scene; const camera = new THREE.PerspectiveCamera(cameraParams.fov, width / height, cameraParams.near, cameraParams.far); camera.position.set(cameraParams.position.x, cameraParams.position.y, cameraParams.position.z); - camera.lookAt(0, 0, 0); + camera.lookAt(cameraParams.lookAt); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(width, height); rendererRef.current = renderer; @@ -559,10 +574,6 @@ const UnifiedEmbryoAnimation: React.FC = ({ sourceL const toBaseY = to.y; const toBaseZ = to.z; - if (i < 5) { - console.log(`Point ${i}: from (${fromX.toFixed(2)}, ${fromY.toFixed(2)}, ${fromZ.toFixed(2)}) to (${toBaseX.toFixed(2)}, ${toBaseY.toFixed(2)}, ${toBaseZ.toFixed(2)})`); - } - fromPositions[fromBaseIndex] = fromX; fromPositions[fromBaseIndex + 1] = fromY; fromPositions[fromBaseIndex + 2] = fromZ; @@ -700,7 +711,52 @@ const UnifiedEmbryoAnimation: React.FC = ({ sourceL applyAnimationProgress(animationProgress); }, [animationProgress, applyAnimationProgress]); - return
; + return ( +
+
+ {legendEntries.length > 0 && ( +
+
Legend
+
+ {legendEntries.map((entry) => ( + + + {entry.type} + + ))} +
+
+ )} +
+ ); }; // 创建纹理贴图(与PointsMaterial相同)