Map node type to fix color

This commit is contained in:
yangdx
2025-04-08 12:39:22 +08:00
parent 1af97bbead
commit f87dfd2067

View File

@@ -11,112 +11,148 @@ import { useSettingsStore } from '@/stores/settings'
import seedrandom from 'seedrandom' import seedrandom from 'seedrandom'
// Predefined node colors - Primary colors // 同义词列表 - 用于快速查找节点类型
const NODE_COLORS = [ const TYPE_SYNONYMS: Record<string, string> = {
'#fdd868', // Yellow - UNKNOWN // unknown 类型及其同义词
'#e3493b', // Google Red - geo 'unknown': 'unknown',
'#1212a1', // Deep Cyan - weapon '未知': 'unknown',
'#0f705d', // Green - organization 'other': 'unknown',
'#b300b3', // Purple - technology
'#f46a9b', // Magenta
'#bd7ebe', // Light Violet
'#fdcce5', // Pale Pink - category
'#0f558a', // Blue - location
'#b2e061', // Yellow Green
'#00bfa0', // Turquoise - event
'#439bd6', // Cyan - person
'#094338', // Deep Green
'#5a2c6d', // Deep Violet
'#fd7f6f', // Light Brown - location
'#adc2eb', // Sky Blue - animal
];
// Extended colors - Used when node types exceed primary colors // category 类型及其同义词
const EXTENDED_COLORS = [ 'category': 'category',
'#742b25', // Dark Brown '类别': 'category',
'#9efacc', // Light Green 'type': 'category',
'#ffbf80', // Light Brown '分类': 'category',
'#003366', // Dark Blue
'#996600', // Yellow Brown
'#4421af', // Deep Purple
'#cf6d17', // Carrot
'#ff1a1a', // Pure Red
];
// All available colors combined // organization 类型及其同义词
const ALL_COLORS = [...NODE_COLORS, ...EXTENDED_COLORS]; 'organization': 'organization',
'组织': 'organization',
'org': 'organization',
'company': 'organization',
'公司': 'organization',
'机构': 'organization',
// Helper function to get color based on node type // event 类型及其同义词
const getNodeColorByType = (nodeType: string | undefined): string => { 'event': 'event',
const defaultColor = '#5D6D7E'; // Default color for nodes without a type or undefined type '事件': 'event',
'activity': 'event',
'活动': 'event',
// Return default color if node type is undefined // person 类型及其同义词
if (!nodeType) { 'person': 'person',
return defaultColor; '人物': 'person',
} 'people': 'person',
'human': 'person',
'人': 'person',
// Get type color map from store // animal 类型及其同义词
const typeColorMap = useGraphStore.getState().typeColorMap; 'animal': 'animal',
'动物': 'animal',
'creature': 'animal',
'生物': 'animal',
// If this type already has an assigned color, return it // geo 类型及其同义词
if (typeColorMap.has(nodeType)) { 'geo': 'geo',
return typeColorMap.get(nodeType) || defaultColor; '地理': 'geo',
} 'geography': 'geo',
'地域': 'geo',
// Get all currently used colors // location 类型及其同义词
const usedColors = new Set<string>(); 'location': 'location',
typeColorMap.forEach(color => { '地点': 'location',
usedColors.add(color); 'place': 'location',
}); 'address': 'location',
'位置': 'location',
'地址': 'location',
// Assign color for new node type // technology 类型及其同义词
// Use a simple hash function to map node type to color index 'technology': 'technology',
const getColorIndex = (str: string): number => { '技术': 'technology',
let hash = 0; 'tech': 'technology',
for (let i = 0; i < str.length; i++) { '科技': 'technology',
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer // equipment 类型及其同义词
} 'equipment': 'equipment',
// Ensure result is positive and within NODE_COLORS range only '设备': 'equipment',
return Math.abs(hash) % NODE_COLORS.length; 'device': 'equipment',
'装备': 'equipment',
// weapon 类型及其同义词
'weapon': 'weapon',
'武器': 'weapon',
'arms': 'weapon',
'军火': 'weapon'
}; };
// Get initial color index from hash // 节点类型到颜色的映射
const colorIndex = getColorIndex(nodeType); const NODE_TYPE_COLORS: Record<string, string> = {
let newColor = NODE_COLORS[colorIndex]; 'unknown': '#f4d371', // Yellow
'category': '#e3493b', // GoogleRed
'organization': '#0f705d', // Green
'event': '#00bfa0', // Turquoise
'person': '#4169E1', // RoyalBlue
'animal': '#84a3e1', // SkyBlue
'geo': '#ff99cc', // Pale Pink
'location': '#cf6d17', // Carrot
'technology': '#b300b3', // Purple
'equipment': '#2F4F4F', // DarkSlateGray
'weapon': '#0f558a', // NavyBlue
};
// If the color is already used, find the next available color // Extended colors pool - Used for unknown node types
if (usedColors.has(newColor) && usedColors.size < ALL_COLORS.length) { const EXTENDED_COLORS = [
// First try to find an unused color in NODE_COLORS '#4421af', // DeepPurple
let foundUnused = false; '#cd071e', // ChinaRed
for (let i = 0; i < NODE_COLORS.length; i++) { '#5a2c6d', // DeepViolet
const candidateColor = NODE_COLORS[i]; '#0000ff', // Blue
if (!usedColors.has(candidateColor)) { '#00cc00', // Green
newColor = candidateColor; '#9b3a31', // DarkBrown
foundUnused = true; '#003366', // DarkBlue
break; '#00CED1', // DarkTurquoise
} '#DEB887', // BurlyWood
'#bd7ebe', // LightViolet
'#b2e061', // YellowGreen
'#6ef7b3', // LightGreen
];
// Select color based on node type
const getNodeColorByType = (nodeType: string | undefined): string => {
const defaultColor = '#5D6D7E';
const normalizedType = nodeType ? nodeType.toLowerCase() : 'unknown';
const typeColorMap = useGraphStore.getState().typeColorMap;
// Return previous color if already mapped
if (typeColorMap.has(normalizedType)) {
return typeColorMap.get(normalizedType) || defaultColor;
} }
// If all NODE_COLORS are used, then try EXTENDED_COLORS const standardType = TYPE_SYNONYMS[normalizedType];
if (!foundUnused) { if (standardType) {
newColor = defaultColor; const color = NODE_TYPE_COLORS[standardType];
for (let i = 0; i < EXTENDED_COLORS.length; i++) { // Update color mapping
const candidateColor = EXTENDED_COLORS[i]; const newMap = new Map(typeColorMap);
if (!usedColors.has(candidateColor)) { newMap.set(normalizedType, color);
newColor = candidateColor; useGraphStore.setState({ typeColorMap: newMap });
break; return color;
}
}
}
} }
// If all colors are used, we'll still use the hashed color // For unpredefind nodeTypes, use extended colors
// In a more advanced implementation, we could create color variants here // Find used extended colors
const usedExtendedColors = new Set(
Array.from(typeColorMap.entries())
.filter(([, color]) => !Object.values(NODE_TYPE_COLORS).includes(color))
.map(([, color]) => color)
);
// Find and use the first unused extended color
const unusedColor = EXTENDED_COLORS.find(color => !usedExtendedColors.has(color));
const newColor = unusedColor || defaultColor;
// Update color mapping // Update color mapping
const newMap = new Map(typeColorMap); const newMap = new Map(typeColorMap);
newMap.set(nodeType, newColor); newMap.set(normalizedType, newColor);
useGraphStore.setState({ typeColorMap: newMap }); useGraphStore.setState({ typeColorMap: newMap });
return newColor; return newColor;