import { useRef, useMemo } from "react"; import { useShallow } from "zustand/react/shallow"; import { useMachineStore, usePatternUploaded, } from "../../stores/useMachineStore"; import { useMachineUploadStore } from "../../stores/useMachineUploadStore"; import { usePatternStore } from "../../stores/usePatternStore"; import { Stage, Layer } from "react-konva"; import Konva from "konva"; import { PhotoIcon } from "@heroicons/react/24/solid"; import { Grid, Origin, Hoop } from "./KonvaComponents"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, } from "@/components/ui/card"; import { ThreadLegend } from "./ThreadLegend"; import { PatternPositionIndicator } from "./PatternPositionIndicator"; import { ZoomControls } from "./ZoomControls"; import { PatternLayer } from "./PatternLayer"; import { useCanvasViewport, usePatternTransform } from "@/hooks"; export function PatternCanvas() { // Machine store const { sewingProgress, machineInfo } = useMachineStore( useShallow((state) => ({ sewingProgress: state.sewingProgress, machineInfo: state.machineInfo, })), ); // Machine upload store const { isUploading } = useMachineUploadStore( useShallow((state) => ({ isUploading: state.isUploading, })), ); // Pattern store const { pesData, patternOffset: initialPatternOffset, patternRotation: initialPatternRotation, uploadedPesData, uploadedPatternOffset: initialUploadedPatternOffset, setPatternOffset, setPatternRotation, } = usePatternStore( useShallow((state) => ({ pesData: state.pesData, patternOffset: state.patternOffset, patternRotation: state.patternRotation, uploadedPesData: state.uploadedPesData, uploadedPatternOffset: state.uploadedPatternOffset, setPatternOffset: state.setPatternOffset, setPatternRotation: state.setPatternRotation, })), ); // Derived state: pattern is uploaded if machine has pattern info const patternUploaded = usePatternUploaded(); const containerRef = useRef(null); const stageRef = useRef(null); // Canvas viewport (zoom, pan, container size) const { stagePos, stageScale, containerSize, handleWheel, handleZoomIn, handleZoomOut, handleZoomReset, handleStageDragStart, handleStageDragEnd, } = useCanvasViewport({ containerRef, pesData, uploadedPesData, machineInfo, }); // Pattern transform (position, rotation, drag/transform) const { localPatternOffset, localPatternRotation, patternGroupRef, transformerRef, attachTransformer, handleCenterPattern, handlePatternDragEnd, handleTransformEnd, } = usePatternTransform({ pesData, initialPatternOffset, initialPatternRotation, setPatternOffset, setPatternRotation, patternUploaded, isUploading, }); const hasPattern = pesData || uploadedPesData; const borderColor = hasPattern ? "border-tertiary-600 dark:border-tertiary-500" : "border-gray-400 dark:border-gray-600"; const iconColor = hasPattern ? "text-tertiary-600 dark:text-tertiary-400" : "text-gray-600 dark:text-gray-400"; // Memoize the display pattern to avoid recalculation const displayPattern = useMemo( () => uploadedPesData || pesData, [uploadedPesData, pesData], ); // Memoize pattern dimensions calculation const patternDimensions = useMemo(() => { if (!displayPattern) return null; const width = ( (displayPattern.bounds.maxX - displayPattern.bounds.minX) / 10 ).toFixed(1); const height = ( (displayPattern.bounds.maxY - displayPattern.bounds.minY) / 10 ).toFixed(1); return `${width} × ${height} mm`; }, [displayPattern]); return (
Pattern Preview {hasPattern ? ( {patternDimensions} ) : ( No pattern loaded )}
{containerSize.width > 0 && ( { stageRef.current = node; if (node) { node.container().style.cursor = "grab"; } }} > {/* Background layer: grid, origin, hoop - static, no event listening */} {displayPattern && ( <> {machineInfo && } )} {/* Original pattern layer: draggable with transformer (shown before upload starts) */} {pesData && ( )} {/* Uploaded pattern layer: locked, rotation baked in (shown during and after upload) */} {uploadedPesData && ( )} )} {/* Placeholder overlay when no pattern is loaded */} {!hasPattern && (
Load a PES file to preview the pattern
)} {/* Pattern info overlays */} {displayPattern && ( <> )}
); }