diff --git a/frontend/src/components/cellDivision/CellDivisionScene.tsx b/frontend/src/components/cellDivision/CellDivisionScene.tsx index 237f1ca..7b95c5d 100644 --- a/frontend/src/components/cellDivision/CellDivisionScene.tsx +++ b/frontend/src/components/cellDivision/CellDivisionScene.tsx @@ -23,6 +23,38 @@ function smoothing(delta: number, halfLifeSeconds = 0.18) { return 1 - Math.pow(0.5, delta / halfLifeSeconds); } +// Geometria das fibras do fuso: cada cilindro vai de A até B de verdade +// (não só "perto" do centrossomo com um ângulo aproximado) — calculamos +// a posição (ponto médio), o comprimento e a rotação (quaternion) direto +// dos dois pontos, então a fibra sempre nasce exatamente na esfera do +// centrossomo e se afunila em leque até perto do centro da célula, como +// o fuso mitótico de verdade. +const CENTROSOME_X = 0.65; +const FIBER_FAN = [-0.26, -0.12, 0.12, 0.26]; + +function cylinderBetween(from: THREE.Vector3, to: THREE.Vector3) { + const direction = new THREE.Vector3().subVectors(to, from); + const length = direction.length(); + const position = new THREE.Vector3().addVectors(from, to).multiplyScalar(0.5); + const quaternion = new THREE.Quaternion().setFromUnitVectors( + new THREE.Vector3(0, 1, 0), // eixo padrão do CylinderGeometry + direction.clone().normalize(), + ); + return { position, quaternion, length }; +} + +// Um leque de fibras por polo, todas nascendo EXATAMENTE no centrossomo +// (0, ±CENTROSOME_X ao longo de X) e convergindo pra perto do centro da +// célula — computado uma única vez (é geometria fixa, não depende de +// estado/props) em vez de recalcular a cada render. +const FIBER_DEFS = ([-1, 1] as const).flatMap((side) => { + const from = new THREE.Vector3(side * CENTROSOME_X, 0, 0); + return FIBER_FAN.map((offset) => { + const to = new THREE.Vector3(side * 0.12, offset * 0.55, offset * 0.55); + return { side, ...cylinderBetween(from, to) }; + }); +}); + interface SelectionProps { selected: CellComponentId | null; onSelect: (id: CellComponentId) => void; @@ -220,31 +252,27 @@ function AnimatedCellSlot({ {/* fibras do fuso — só a partir da prófase (quando `spindle: true`) */} - {[-1, 1].map((side) => ( - - {[-0.25, -0.1, 0.1, 0.25].map((offset, i) => ( - 0 ? Math.PI / 2 + offset * 0.3 : -Math.PI / 2 + offset * 0.3]} - onClick={(e) => { - e.stopPropagation(); - onSelect("fibras"); - }} - > - {/* raio um pouco maior só pra facilitar o clique — a fibra - visual continua fina, isso é puramente a área alvo */} - - - - ))} - + {FIBER_DEFS.map((fiber, i) => ( + { + e.stopPropagation(); + onSelect("fibras"); + }} + > + {/* raio um pouco maior só pra facilitar o clique — a fibra + visual continua fina, isso é puramente a área alvo */} + + + ))}