diff --git a/src/app/studio/page.tsx b/src/app/studio/page.tsx index cc9edd5..d7af01c 100644 --- a/src/app/studio/page.tsx +++ b/src/app/studio/page.tsx @@ -30,6 +30,7 @@ import CompletionCard from '../../components/CompletionCard'; import PreviewToolbar from '../../components/PreviewToolbar'; import EditToolbar from '../../components/EditToolbar'; import ColorSystemPanel from '../../components/ColorSystemPanel'; +import CanvasColorPanel from '../../components/CanvasColorPanel'; import { getColorKeyByHex, ColorSystem, getMardToHexMapping, getAllHexValues } from '../../utils/colorSystemUtils'; // 定义编辑模式类型 @@ -140,6 +141,11 @@ export default function FocusMode() { const [history, setHistory] = useState([]); const [historyIndex, setHistoryIndex] = useState(-1); + // 去杂色模式状态 + const [showCanvasColorPanel, setShowCanvasColorPanel] = useState(false); + const [canvasPalette, setCanvasPalette] = useState>(new Set()); + const [removedColors, setRemovedColors] = useState([]); + // 计算状态 const hasSelection = selectedCells.size > 0; const canUndo = historyIndex > 0; @@ -1011,6 +1017,148 @@ export default function FocusMode() { } }, [history, historyIndex]); + // 获取画布中的所有颜色信息 + const getCanvasColors = useCallback(() => { + if (!mappedPixelData) return []; + + const colorCounts: { [hex: string]: number } = {}; + + mappedPixelData.forEach(row => { + row.forEach(pixel => { + if (pixel.color && pixel.color !== 'transparent' && !pixel.isExternal) { + colorCounts[pixel.color] = (colorCounts[pixel.color] || 0) + 1; + } + }); + }); + + return Object.entries(colorCounts).map(([hex, count]) => ({ + hex, + key: getColorKeyByHex(hex, selectedColorSystem), + count + })); + }, [mappedPixelData, selectedColorSystem]); + + // 去杂色:启动模式 + const handleRemoveNoise = useCallback(() => { + if (!mappedPixelData) return; + + // 获取当前画布颜色 + const canvasColors = getCanvasColors(); + setCanvasPalette(new Set(canvasColors.map(c => c.hex))); + setRemovedColors([]); // 重置已移除颜色列表 + setShowCanvasColorPanel(true); + }, [mappedPixelData, getCanvasColors]); + + // 去杂色:移除颜色 + const handleRemoveCanvasColor = useCallback((hexToRemove: string) => { + if (!mappedPixelData) return; + + // 更新画布色板 + const newCanvasPalette = new Set(canvasPalette); + newCanvasPalette.delete(hexToRemove); + setCanvasPalette(newCanvasPalette); + + // 添加到已移除颜色列表 + setRemovedColors(prev => [...prev, hexToRemove]); + + // 保存历史 + saveToHistory(); + + // 获取剩余的颜色 + const remainingColors = Array.from(newCanvasPalette); + + if (remainingColors.length === 0) { + console.warn('无法移除所有颜色'); + return; + } + + // 创建新的像素数据 + const newPixelData = mappedPixelData.map(row => + row.map(pixel => { + if (pixel.color === hexToRemove && !pixel.isExternal) { + // 找到最接近的替换颜色 + let closestColor = remainingColors[0]; + let minDistance = Infinity; + + const removedRgb = hexToRgb(hexToRemove); + if (removedRgb) { + remainingColors.forEach(hex => { + const rgb = hexToRgb(hex); + if (rgb) { + const distance = colorDistance(removedRgb, rgb); + if (distance < minDistance) { + minDistance = distance; + closestColor = hex; + } + } + }); + } + + return { + ...pixel, + color: closestColor, + key: closestColor + }; + } + return pixel; + }) + ); + + setMappedPixelData(newPixelData); + }, [mappedPixelData, canvasPalette, saveToHistory]); + + // 去杂色:恢复颜色 + const handleRestoreCanvasColor = useCallback((hexToRestore: string) => { + if (!mappedPixelData) return; + + // 从已移除列表中移除 + setRemovedColors(prev => prev.filter(hex => hex !== hexToRestore)); + + // 添加回画布色板 + const newCanvasPalette = new Set(canvasPalette); + newCanvasPalette.add(hexToRestore); + setCanvasPalette(newCanvasPalette); + + // 保存历史 + saveToHistory(); + + // 重新计算整个画布的颜色映射 + const allAvailableColors = Array.from(newCanvasPalette); + + const newPixelData = mappedPixelData.map(row => + row.map(pixel => { + if (!pixel.isExternal && pixel.color !== 'transparent') { + // 为每个像素找到最接近的可用颜色 + let closestColor = allAvailableColors[0]; + let minDistance = Infinity; + + const pixelRgb = hexToRgb(pixel.color); + if (pixelRgb) { + allAvailableColors.forEach(hex => { + const rgb = hexToRgb(hex); + if (rgb) { + const distance = colorDistance(pixelRgb, rgb); + if (distance < minDistance) { + minDistance = distance; + closestColor = hex; + } + } + }); + } + + return { + ...pixel, + color: closestColor, + key: closestColor + }; + } + return pixel; + }) + ); + + setMappedPixelData(newPixelData); + }, [mappedPixelData, canvasPalette, saveToHistory]); + // 编辑模式:执行操作 const handleEditOperation = useCallback((operation: 'fill' | 'clear' | 'invert') => { if (!mappedPixelData) return; @@ -1274,22 +1422,30 @@ export default function FocusMode() { {/* 编辑模式工具栏 */} {focusState.editMode === 'edit' && ( { + // TODO: 实现手动上色功能 + console.log('手动上色功能'); + }} onColorSelect={setSelectedColor} onShowColorPanel={() => setFocusState(prev => ({ ...prev, showColorPanel: true }))} /> )} + {/* 画布颜色面板 - 去杂色模式 */} + {showCanvasColorPanel && ( + setShowCanvasColorPanel(false)} + /> + )} + {/* 底部模式切换栏 */}
diff --git a/src/components/CanvasColorPanel.tsx b/src/components/CanvasColorPanel.tsx new file mode 100644 index 0000000..a4da93f --- /dev/null +++ b/src/components/CanvasColorPanel.tsx @@ -0,0 +1,129 @@ +import React from 'react'; +import { getColorKeyByHex, ColorSystem } from '../utils/colorSystemUtils'; + +interface CanvasColorInfo { + hex: string; + key: string; + count: number; +} + +interface CanvasColorPanelProps { + canvasColors: CanvasColorInfo[]; + removedColors: string[]; + selectedColorSystem: ColorSystem; + onColorRemove: (hex: string) => void; + onColorRestore: (hex: string) => void; + onClose: () => void; +} + +const CanvasColorPanel: React.FC = ({ + canvasColors, + removedColors, + selectedColorSystem, + onColorRemove, + onColorRestore, + onClose +}) => { + // 按数量从少到多排序 + const sortedColors = [...canvasColors].sort((a, b) => a.count - b.count); + + return ( +
+
+

画布颜色

+ +
+ +
+ {/* 当前画布颜色 */} + {sortedColors.map(color => ( + + ))} + + {/* 已移除颜色区域 */} + {removedColors.length > 0 && ( + <> +
+
+ 已移除 +
+
+
+ + {removedColors.map(hex => { + const key = getColorKeyByHex(hex, selectedColorSystem); + return ( + + ); + })} + + )} +
+ + {sortedColors.length === 0 && removedColors.length === 0 && ( +
+ 暂无颜色数据 +
+ )} +
+ ); +}; + +export default CanvasColorPanel; \ No newline at end of file diff --git a/src/components/EditToolbar.tsx b/src/components/EditToolbar.tsx index 1888e93..0600968 100644 --- a/src/components/EditToolbar.tsx +++ b/src/components/EditToolbar.tsx @@ -8,162 +8,46 @@ interface ColorInfo { } interface EditToolbarProps { - editTool: 'select' | 'wand'; - hasSelection: boolean; - canUndo: boolean; - canRedo: boolean; selectedColor: string; availableColors: ColorInfo[]; - selectedCells: Set; - onEditToolChange: (tool: 'select' | 'wand') => void; - onEditOperation: (operation: 'fill' | 'clear' | 'invert') => void; - onUndo: () => void; - onRedo: () => void; + onRemoveNoise: () => void; + onManualColoring: () => void; onColorSelect: (color: string) => void; onShowColorPanel: () => void; } const EditToolbar: React.FC = ({ - editTool, - hasSelection, - canUndo, - canRedo, selectedColor, availableColors, - selectedCells, - onEditToolChange, - onEditOperation, - onUndo, - onRedo, + onRemoveNoise, + onManualColoring, onColorSelect, onShowColorPanel }) => { return ( -
-
- {/* 选择工具区域 */} -
- 选择 -
- - -
-
- - {/* 编辑操作区域 */} -
- 操作 -
- - - -
-
- - {/* 历史记录区域 */} -
- 历史 -
- - -
-
+
+
+ {/* 去杂色功能 */} + -
- - {/* 颜色选择区域 */} -
- 颜色 -
- {availableColors.slice(0, 10).map(color => ( - - )} -
-
- - {/* 选择状态区域 */} -
- 状态 -
- {hasSelection ? `${selectedCells.size}格` : '未选'} -
-
+ {/* 手动上色功能 */} +
);