// Color schemes for different visualization types export const VISUALIZATION_COLORS = { // Entity type colors entityTypes: { person: "#3b82f6", organization: "#10b981", location: "#f59e0b", concept: "#8b5cf6", event: "#ef4444", default: "#6b7280", }, // Processing method colors processingMethods: { production: "#059669", baseline: "#dc2626", hybrid: "#7c3aed", sequential: "#ea580c", default: "#6b7280", }, // Chart color palettes sequential: [ "#f7fafc", "#edf2f7", "#e2e8f0", "#cbd5e0", "#a0aec0", "#718096", "#4a5568", "#2d3748", "#1a202c", ], categorical: [ "#3b82f6", "#10b981", "#f59e0b", "#8b5cf6", "#ef4444", "#06b6d4", "#84cc16", "#f97316", "#ec4899", "#6366f1", ], diverging: [ "#dc2626", "#ef4444", "#f87171", "#fca5a5", "#fed7d7", "#f3f4f6", "#d1fae5", "#86efac", "#34d399", "#10b981", ], }; // Text processing utilities export function removeStopwords( text: string, customStopwords: string[] = [] ): string { const defaultStopwords = [ "the", "be", "to", "of", "and", "a", "in", "that", "have", "i", "it", "for", "not", "on", "with", "he", "as", "you", "do", "at", "this", "but", "his", "by", "from", "they", "we", "say", "her", "she", "or", "an", "will", "my", "one", "all", "would", "there", "their", "what", "so", "up", "out", "if", "about", "who", "get", "which", "go", "me", "when", "make", "can", "like", "time", "no", "just", "him", "know", "take", "people", "into", "year", "your", "good", "some", "could", "them", "see", "other", "than", "then", "now", "look", "only", "come", "its", "over", "think", "also", "back", "after", "use", "two", "how", "our", "work", "first", "well", "way", "even", "new", "want", "because", "any", "these", "give", "day", "most", "us", "is", "was", "are", "been", "has", "had", "were", "said", "each", ]; const allStopwords = new Set([...defaultStopwords, ...customStopwords]); return text .toLowerCase() .replace(/[^\w\s]/g, " ") .split(/\s+/) .filter((word) => word.length > 2 && !allStopwords.has(word)) .join(" "); } export function extractKeywords( text: string, maxKeywords: number = 20 ): string[] { const processedText = removeStopwords(text); const words = processedText.split(/\s+/); // Calculate word frequency const frequency: Record = {}; words.forEach((word) => { frequency[word] = (frequency[word] || 0) + 1; }); // Return top keywords by frequency return Object.entries(frequency) .sort(([, a], [, b]) => b - a) .slice(0, maxKeywords) .map(([word]) => word); } export function calculateTfIdf( documents: string[], maxTerms: number = 100 ): Array<{ word: string; score: number }> { // Calculate TF-IDF scores for term extraction const documentWords = documents.map((doc) => removeStopwords(doc).split(/\s+/) ); const vocabulary = new Set(documentWords.flat()); const tfidfScores: Record = {}; vocabulary.forEach((term) => { let tfidfSum = 0; documentWords.forEach((docWords) => { const tf = docWords.filter((word) => word === term).length / docWords.length; const df = documentWords.filter((doc) => doc.includes(term)).length; const idf = Math.log(documents.length / df); tfidfSum += tf * idf; }); tfidfScores[term] = tfidfSum; }); return Object.entries(tfidfScores) .sort(([, a], [, b]) => b - a) .slice(0, maxTerms) .map(([word, score]) => ({ word, score })); } // Color utility functions export function getColorForType( type: string, colorScheme: "entity" | "processing" = "entity" ): string { const schemes = { entity: VISUALIZATION_COLORS.entityTypes, processing: VISUALIZATION_COLORS.processingMethods, }; return ( schemes[colorScheme][type as keyof (typeof schemes)[typeof colorScheme]] || schemes[colorScheme].default ); } export function getColorScale( count: number, type: "sequential" | "categorical" | "diverging" = "categorical" ): string[] { const palette = VISUALIZATION_COLORS[type]; if (count <= palette.length) { return palette.slice(0, count); } // Generate interpolated colors for larger counts const step = Math.floor(palette.length / count); const colors: string[] = []; for (let i = 0; i < count; i++) { const index = Math.min(i * step, palette.length - 1); colors.push(palette[index] || "#6b7280"); } return colors; } export function hexToRgba(hex: string, alpha: number = 1): string { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } // Text analysis utilities export function analyzeTextReadability(text: string): { readingTime: number; complexity: number; wordCount: number; sentenceCount: number; avgWordsPerSentence: number; avgSyllablesPerWord: number; } { const words = text.split(/\s+/).filter((word) => word.length > 0); const sentences = text.split(/[.!?]+/).filter((s) => s.trim().length > 0); const wordCount = words.length; const sentenceCount = sentences.length; const avgWordsPerSentence = wordCount / sentenceCount; // Simple syllable counting (approximation) const avgSyllablesPerWord = words.reduce((sum, word) => { const syllables = word .toLowerCase() .replace(/[^a-z]/g, "") .replace(/[aeiou]{2,}/g, "a") .match(/[aeiou]/g); return sum + (syllables ? syllables.length : 1); }, 0) / wordCount; // Flesch-Kincaid Grade Level (simplified) const complexity = Math.max( 0, Math.min( 100, 206.835 - 1.015 * avgWordsPerSentence - 84.6 * avgSyllablesPerWord ) ); // Reading time (200 words per minute) const readingTime = Math.ceil(wordCount / 200); return { readingTime, complexity, wordCount, sentenceCount, avgWordsPerSentence, avgSyllablesPerWord, }; } // Data transformation utilities export function normalizeValue( value: number, min: number, max: number ): number { return (value - min) / (max - min); } export function scaleValue( value: number, oldMin: number, oldMax: number, newMin: number, newMax: number ): number { return newMin + ((value - oldMin) * (newMax - newMin)) / (oldMax - oldMin); } export function formatNumber(num: number, decimals: number = 0): string { if (num >= 1000000) { return (num / 1000000).toFixed(decimals) + "M"; } else if (num >= 1000) { return (num / 1000).toFixed(decimals) + "K"; } else { return num.toFixed(decimals); } } export function formatPercentage(value: number, total: number): string { return `${((value / total) * 100).toFixed(1)}%`; } // Debounce utility for performance export function debounce void>( func: T, delay: number ): (...args: Parameters) => void { let timeoutId: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; } // DOM utilities for visualization export function getElementDimensions(element: HTMLElement | null): { width: number; height: number; } { if (!element) return { width: 0, height: 0 }; const rect = element.getBoundingClientRect(); return { width: rect.width, height: rect.height, }; } export function createSvgElement(width: number, height: number): SVGSVGElement { const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("width", width.toString()); svg.setAttribute("height", height.toString()); svg.setAttribute("viewBox", `0 0 ${width} ${height}`); return svg; } // Accessibility utilities export function generateAccessibleLabel( chartType: string, dataPoints: number, summary?: string ): string { const baseLabel = `${chartType} with ${dataPoints} data points`; return summary ? `${baseLabel}. ${summary}` : baseLabel; } export function getContrastColor(backgroundColor: string): string { // Simple contrast color calculation const hex = backgroundColor.replace("#", ""); const r = parseInt(hex.substr(0, 2), 16); const g = parseInt(hex.substr(2, 2), 16); const b = parseInt(hex.substr(4, 2), 16); const brightness = (r * 299 + g * 587 + b * 114) / 1000; return brightness > 128 ? "#000000" : "#ffffff"; }