import React, { useState } from 'react'; import { AtomicHotspot } from '../types'; import { Eye, Flame, Info, ShieldCheck, Zap } from 'lucide-react'; interface MoleculeVisualizerProps { smiles: string; compoundName: string; hotspots: AtomicHotspot[]; toxicophores: string[]; } export const MoleculeVisualizer: React.FC = ({ smiles, compoundName, hotspots, toxicophores }) => { const [hoveredAtom, setHoveredAtom] = useState(null); // Generate a clean 2D visual layout representation for the SMILES string const atoms = hotspots; const totalAtoms = atoms.length; // Simple ring/chain coordinate calculation for visual rendering const getAtomCoordinates = (index: number, total: number) => { const isBenzene = smiles.includes('C1=CC=CC=C1') || smiles.includes('c1ccccc1'); if (isBenzene && index < 6) { // 6-member ring const angle = (index * 2 * Math.PI) / 6 - Math.PI / 2; const radius = 55; return { cx: 140 + radius * Math.cos(angle), cy: 110 + radius * Math.sin(angle) }; } else { // Horizontal wave chain with branching const cols = 6; const row = Math.floor(index / cols); const col = index % cols; return { cx: 60 + col * 45 + (row % 2 === 1 ? 22 : 0), cy: 60 + row * 45 }; } }; // Map attention weight to color gradient const getHotspotColor = (weight: number) => { if (weight >= 0.85) return { fill: '#ef4444', stroke: '#f87171', bg: 'bg-rose-500/20 text-rose-300 border-rose-500/40' }; // Severe toxicophore if (weight >= 0.65) return { fill: '#f97316', stroke: '#fb923c', bg: 'bg-amber-500/20 text-amber-300 border-amber-500/40' }; // Moderate toxicophore if (weight >= 0.40) return { fill: '#eab308', stroke: '#fde047', bg: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/40' }; // Mild risk return { fill: '#3b82f6', stroke: '#60a5fa', bg: 'bg-sky-500/20 text-sky-300 border-sky-500/40' }; // Safe/neutral }; return (
{/* Header */}

XAI Atomic Toxicity Hotspot Map

16-Head Cross-Attention Toxicophore Subgraph Contributions

{totalAtoms} Reactive Centers
{/* SVG Chemical Graph Viewer */}
{/* Connective Bonds */} {atoms.map((atom, i) => { if (i === 0) return null; const prevCoords = getAtomCoordinates(i - 1, totalAtoms); const currCoords = getAtomCoordinates(i, totalAtoms); return ( ); })} {/* Atom Nodes with Attention Glow */} {atoms.map((atom, i) => { const { cx, cy } = getAtomCoordinates(i, totalAtoms); const colors = getHotspotColor(atom.attentionWeight); const isHovered = hoveredAtom?.atomIndex === atom.atomIndex; return ( setHoveredAtom(atom)} onMouseLeave={() => setHoveredAtom(null)} className="cursor-pointer transition-transform duration-200" > {/* Outer Attention Halo */} {/* Inner Core Node */} {/* Symbol Label */} {atom.symbol} ); })} {/* Hovered Atom Info Box */} {hoveredAtom ? (
Atom #{hoveredAtom.atomIndex + 1} ({hoveredAtom.symbol}): {hoveredAtom.subgraphName}
α = {hoveredAtom.attentionWeight.toFixed(2)}
) : (
Hover over atomic nodes to inspect XAI attention weights & toxicophore subgraphs
)}
{/* Identified Toxicophore Badges */}
Detected Structural Toxicophores:
{toxicophores.map((t, idx) => ( {t} ))}
{/* Attention Heatmap Legend */}
Attention Intensity Scale:
Low (α < 0.4) Mid (0.4 - 0.7) Toxicophore (α > 0.8)
); };