Spaces:
Configuration error
Configuration error
File size: 6,061 Bytes
b700c24 eebc40f 6bda4a6 b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 6bda4a6 b700c24 6bda4a6 b700c24 6bda4a6 b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f b700c24 eebc40f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | import { useRef, useEffect, useState } from 'react';
import * as d3 from 'd3';
import { useFontMapStore } from '../../../store/fontMapStore';
import { useGlyphRenderer } from './useGlyphRenderer';
import { useZoom } from './useZoom';
import { useVisualState } from './useVisualState';
/**
* Hook principal pour la visualisation D3
* Orchestre le rendu, le zoom et les interactions
*/
export const useD3Visualization = (fonts, glyphPaths, filter, searchTerm, darkMode, loading) => {
const svgRef = useRef(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const scaledPositionsRef = useRef([]); // Pour stocker les positions mises à l'échelle
// Store global
const {
selectedFont,
setSelectedFont,
setHoveredFont
} = useFontMapStore();
// Hooks spécialisés
const { createGlyphs, updateGlyphPositions, updateGlyphDimensions } = useGlyphRenderer();
const { setupZoom, setupGlobalZoomFunctions, centerOnFont, createZoomIndicator } = useZoom(svgRef, darkMode);
const { visualStateRef, updateVisualStates, updateGlyphSizes, updateGlyphOpacity, updateGlyphColors } = useVisualState();
// Initialisation et redimensionnement
useEffect(() => {
const handleResize = () => {
if (svgRef.current && svgRef.current.parentElement) {
const { clientWidth, clientHeight } = svgRef.current.parentElement;
setDimensions({ width: clientWidth, height: clientHeight });
}
};
window.addEventListener('resize', handleResize);
handleResize();
return () => window.removeEventListener('resize', handleResize);
}, []);
// Rendu principal D3
useEffect(() => {
if (loading || !fonts || fonts.length === 0 || !svgRef.current || dimensions.width === 0) return;
const svg = d3.select(svgRef.current);
svg.selectAll('*').remove(); // Nettoyage complet
const width = dimensions.width;
const height = dimensions.height;
svg
.attr('width', width)
.attr('height', height)
.style('background-color', darkMode ? '#1a1a1a' : '#ffffff');
// Groupes principaux
const containerGroup = svg.append('g').attr('class', 'container-group');
const viewportGroup = containerGroup.append('g').attr('class', 'viewport-group');
const uiGroup = svg.append('g').attr('class', 'ui-group');
// Injecter le sprite SVG directement dans le SVG
// Créer les symboles à partir des glyphPaths
if (glyphPaths && Object.keys(glyphPaths).length > 0) {
const defs = svg.append('defs');
Object.entries(glyphPaths).forEach(([id, pathData]) => {
defs.append('symbol')
.attr('id', id)
.attr('viewBox', '0 0 80 80')
.append('path')
.attr('d', pathData)
.attr('fill', 'currentColor');
});
console.log(`✅ Injected ${Object.keys(glyphPaths).length} symbols into SVG`);
} else {
console.warn('⚠️ No glyphPaths available for sprite injection');
}
// Configuration du zoom
const zoom = setupZoom(svg, viewportGroup, uiGroup, width, height);
setupGlobalZoomFunctions(svg);
// Calculer les échelles pour mapper les coordonnées UMAP vers l'espace SVG
const padding = 50; // Padding autour de la visualisation
const xExtent = d3.extent(fonts, d => d.x);
const yExtent = d3.extent(fonts, d => d.y);
const xScale = d3.scaleLinear()
.domain(xExtent)
.range([padding, width - padding]);
const yScale = d3.scaleLinear()
.domain(yExtent)
.range([padding, height - padding]);
// Créer les positions mises à l'échelle
const scaledPositions = fonts.map(font => ({
...font,
x: xScale(font.x),
y: yScale(font.y)
}));
// Stocker pour les effets réactifs
scaledPositionsRef.current = scaledPositions;
// Échelle de couleurs
const colorScale = d3.scaleOrdinal(
darkMode
? ['#ffffff', '#cccccc', '#999999', '#666666', '#333333']
: ['#000000', '#333333', '#666666', '#999999', '#cccccc']
);
const families = [...new Set(fonts.map(d => d.family))];
families.forEach(family => colorScale(family));
// Création des glyphes avec les positions mises à l'échelle
createGlyphs(
viewportGroup,
scaledPositions, // Utiliser les positions mises à l'échelle !
glyphPaths, // Paths extraits pour rendu direct
darkMode,
1.0, // characterSize par défaut
filter,
searchTerm,
colorScale,
false, // debugMode
setSelectedFont,
selectedFont,
visualStateRef
);
// Indicateurs UI
createZoomIndicator(uiGroup, width, height, true);
}, [
loading,
fonts,
glyphPaths, // Needed for sprite injection
dimensions.width,
dimensions.height,
darkMode,
setupZoom,
createGlyphs,
createZoomIndicator,
setupGlobalZoomFunctions,
visualStateRef,
setSelectedFont,
selectedFont // Pour l'état initial
]);
// Mises à jour réactives (sans redessiner tout)
useEffect(() => {
if (!svgRef.current || loading || scaledPositionsRef.current.length === 0) return;
const svg = d3.select(svgRef.current);
const viewportGroup = svg.select('.viewport-group');
if (viewportGroup.empty()) return;
// Mise à jour des états visuels
updateVisualStates(svg, viewportGroup, selectedFont, null, darkMode);
updateGlyphOpacity(viewportGroup, scaledPositionsRef.current, filter, searchTerm, selectedFont);
// Si une police est sélectionnée, on centre dessus
if (selectedFont && !visualStateRef.current.isTransitioning) {
// Utiliser les positions mises à l'échelle pour le centrage
centerOnFont(selectedFont, scaledPositionsRef.current, visualStateRef, (val) => {
visualStateRef.current.isTransitioning = val;
});
}
}, [
selectedFont,
filter,
searchTerm,
darkMode,
fonts,
loading,
updateVisualStates,
updateGlyphOpacity,
centerOnFont,
visualStateRef
]);
return svgRef;
};
|