import { useRef } from "react"; import { useShallow } from "zustand/react/shallow"; import { useMachineStore, usePatternUploaded, } from "../../stores/useMachineStore"; 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 } from "../../hooks/useCanvasViewport"; import { usePatternTransform } from "../../hooks/usePatternTransform"; export function PatternCanvas() { // Machine store const { sewingProgress, machineInfo, isUploading } = useMachineStore( useShallow((state) => ({ sewingProgress: state.sewingProgress, machineInfo: state.machineInfo, 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, } = 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"; return (
Pattern Preview {hasPattern ? ( {(() => { const displayPattern = uploadedPesData || pesData; return displayPattern ? ( <> {( (displayPattern.bounds.maxX - displayPattern.bounds.minX) / 10 ).toFixed(1)}{" "} ×{" "} {( (displayPattern.bounds.maxY - displayPattern.bounds.minY) / 10 ).toFixed(1)}{" "} mm ) : null; })()} ) : ( No pattern loaded )}
{containerSize.width > 0 && ( { if (stageRef.current) { stageRef.current.container().style.cursor = "grabbing"; } }} onDragEnd={() => { if (stageRef.current) { stageRef.current.container().style.cursor = "grab"; } }} ref={(node) => { stageRef.current = node; if (node) { node.container().style.cursor = "grab"; } }} > {/* Background layer: grid, origin, hoop */} {hasPattern && ( <> {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 */} {hasPattern && (() => { const displayPattern = uploadedPesData || pesData; return ( displayPattern && ( <> ) ); })()}
); }