diff --git a/src/App.tsx b/src/App.tsx
index f732876..5d6689d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,6 +8,13 @@ import { LeftSidebar } from "./components/LeftSidebar";
import { PatternCanvas } from "./components/PatternCanvas";
import { PatternCanvasPlaceholder } from "./components/PatternCanvasPlaceholder";
import { BluetoothDevicePicker } from "./components/BluetoothDevicePicker";
+import { transformStitchesRotation } from "./utils/rotationUtils";
+import { encodeStitchesToPen } from "./formats/pen/encoder";
+import { decodePenData } from "./formats/pen/decoder";
+import {
+ calculatePatternCenter,
+ calculateBoundsFromDecodedStitches,
+} from "./components/PatternCanvas/patternCanvasHelpers";
import "./App.css";
function App() {
@@ -25,10 +32,20 @@ function App() {
);
// Pattern store - for auto-loading cached pattern
- const { pesData, setPattern, setPatternOffset } = usePatternStore(
+ const {
+ pesData,
+ uploadedPesData,
+ setPattern,
+ setUploadedPattern,
+ setPatternRotation,
+ setPatternOffset,
+ } = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
+ uploadedPesData: state.uploadedPesData,
setPattern: state.setPattern,
+ setUploadedPattern: state.setUploadedPattern,
+ setPatternRotation: state.setPatternRotation,
setPatternOffset: state.setPatternOffset,
})),
);
@@ -47,23 +64,130 @@ function App() {
// Auto-load cached pattern when available
useEffect(() => {
- if (resumedPattern && !pesData) {
+ // Only auto-load if we have a resumed pattern and haven't already loaded it
+ if (resumedPattern && !uploadedPesData && !pesData) {
+ if (!resumedPattern.pesData) {
+ console.error(
+ "[App] ERROR: resumedPattern has no pesData!",
+ resumedPattern,
+ );
+ return;
+ }
+
console.log(
"[App] Loading resumed pattern:",
resumeFileName,
"Offset:",
resumedPattern.patternOffset,
+ "Rotation:",
+ resumedPattern.patternRotation,
+ "Has stitches:",
+ resumedPattern.pesData.stitches?.length || 0,
+ "Has cached uploaded data:",
+ !!resumedPattern.uploadedPesData,
);
- setPattern(resumedPattern.pesData, resumeFileName || "");
- // Restore the cached pattern offset
- if (resumedPattern.patternOffset) {
- setPatternOffset(
- resumedPattern.patternOffset.x,
- resumedPattern.patternOffset.y,
+
+ const originalPesData = resumedPattern.pesData;
+ const cachedUploadedPesData = resumedPattern.uploadedPesData;
+ const rotation = resumedPattern.patternRotation || 0;
+ const originalOffset = resumedPattern.patternOffset || { x: 0, y: 0 };
+
+ // Set the original pattern data for editing
+ setPattern(originalPesData, resumeFileName || "");
+
+ // Restore the original offset (setPattern resets it to 0,0)
+ setPatternOffset(originalOffset.x, originalOffset.y);
+
+ // Set rotation if present
+ if (rotation !== 0) {
+ setPatternRotation(rotation);
+ }
+
+ // Use cached uploadedPesData if available, otherwise recalculate
+ if (cachedUploadedPesData) {
+ // Use the exact uploaded data from cache
+ // Calculate the adjusted offset (same logic as upload)
+ if (rotation !== 0) {
+ const originalCenter = calculatePatternCenter(originalPesData.bounds);
+ const rotatedCenter = calculatePatternCenter(
+ cachedUploadedPesData.bounds,
+ );
+ const centerShiftX = rotatedCenter.x - originalCenter.x;
+ const centerShiftY = rotatedCenter.y - originalCenter.y;
+
+ const adjustedOffset = {
+ x: originalOffset.x + centerShiftX,
+ y: originalOffset.y + centerShiftY,
+ };
+
+ setUploadedPattern(
+ cachedUploadedPesData,
+ adjustedOffset,
+ resumeFileName || undefined,
+ );
+ } else {
+ setUploadedPattern(
+ cachedUploadedPesData,
+ originalOffset,
+ resumeFileName || undefined,
+ );
+ }
+ } else if (rotation !== 0) {
+ // Fallback: recalculate if no cached uploaded data (shouldn't happen for new uploads)
+ console.warn("[App] No cached uploaded data, recalculating rotation");
+ const rotatedStitches = transformStitchesRotation(
+ originalPesData.stitches,
+ rotation,
+ originalPesData.bounds,
+ );
+
+ const penResult = encodeStitchesToPen(rotatedStitches);
+ const penData = new Uint8Array(penResult.penBytes);
+ const decoded = decodePenData(penData);
+ const rotatedBounds = calculateBoundsFromDecodedStitches(decoded);
+
+ const originalCenter = calculatePatternCenter(originalPesData.bounds);
+ const rotatedCenter = calculatePatternCenter(rotatedBounds);
+ const centerShiftX = rotatedCenter.x - originalCenter.x;
+ const centerShiftY = rotatedCenter.y - originalCenter.y;
+
+ const adjustedOffset = {
+ x: originalOffset.x + centerShiftX,
+ y: originalOffset.y + centerShiftY,
+ };
+
+ const rotatedPesData = {
+ ...originalPesData,
+ stitches: rotatedStitches,
+ penData,
+ penStitches: decoded,
+ bounds: rotatedBounds,
+ };
+
+ setUploadedPattern(
+ rotatedPesData,
+ adjustedOffset,
+ resumeFileName || undefined,
+ );
+ } else {
+ // No rotation - uploaded pattern is same as original
+ setUploadedPattern(
+ originalPesData,
+ originalOffset,
+ resumeFileName || undefined,
);
}
}
- }, [resumedPattern, resumeFileName, pesData, setPattern, setPatternOffset]);
+ }, [
+ resumedPattern,
+ resumeFileName,
+ uploadedPesData,
+ pesData,
+ setPattern,
+ setUploadedPattern,
+ setPatternRotation,
+ setPatternOffset,
+ ]);
return (
@@ -76,7 +200,11 @@ function App() {
{/* Right Column - Pattern Preview */}
- {pesData ?
:
}
+ {pesData || uploadedPesData ? (
+
+ ) : (
+
+ )}
diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx
index 2f7e54e..0adad6a 100644
--- a/src/components/FileUpload.tsx
+++ b/src/components/FileUpload.tsx
@@ -206,11 +206,14 @@ export function FileUpload() {
setUploadedPattern(pesDataForUpload, adjustedOffset);
// Upload the pattern with offset
+ // IMPORTANT: Pass original unrotated pesData for caching, rotated pesData for upload
uploadPattern(
penDataToUpload,
pesDataForUpload,
displayFileName,
adjustedOffset,
+ patternRotation,
+ pesData, // Original unrotated pattern for caching
);
return; // Early return to skip the upload below
@@ -226,6 +229,8 @@ export function FileUpload() {
pesDataForUpload,
displayFileName,
patternOffset,
+ 0, // No rotation
+ // No need to pass originalPesData since it's the same as pesDataForUpload
);
}
}, [
diff --git a/src/components/LeftSidebar.tsx b/src/components/LeftSidebar.tsx
index adddb0e..01abd0f 100644
--- a/src/components/LeftSidebar.tsx
+++ b/src/components/LeftSidebar.tsx
@@ -13,14 +13,16 @@ export function LeftSidebar() {
})),
);
- const { pesData } = usePatternStore(
+ const { pesData, uploadedPesData } = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
+ uploadedPesData: state.uploadedPesData,
})),
);
// Derived state: pattern is uploaded if machine has pattern info
const patternUploaded = usePatternUploaded();
+ const hasPattern = pesData || uploadedPesData;
return (
@@ -31,7 +33,7 @@ export function LeftSidebar() {
{isConnected && !patternUploaded &&
}
{/* Compact Pattern Summary - Show after upload (during sewing stages) */}
- {isConnected && patternUploaded && pesData &&
}
+ {isConnected && patternUploaded && hasPattern &&
}
{/* Progress Monitor - Show when pattern is uploaded */}
{isConnected && patternUploaded && (
diff --git a/src/components/PatternCanvas/PatternCanvas.tsx b/src/components/PatternCanvas/PatternCanvas.tsx
index b44d3a9..1756186 100644
--- a/src/components/PatternCanvas/PatternCanvas.tsx
+++ b/src/components/PatternCanvas/PatternCanvas.tsx
@@ -190,7 +190,9 @@ export function PatternCanvas() {
{/* Original pattern layer: draggable with transformer (shown before upload starts) */}
-
+
{pesData && (
{/* Uploaded pattern layer: locked, rotation baked in (shown during and after upload) */}
-
+
{uploadedPesData && (
@@ -257,7 +261,10 @@ export function PatternCanvas() {
onZoomReset={handleZoomReset}
onCenterPattern={handleCenterPattern}
canCenterPattern={
- !!pesData && !patternUploaded && !isUploading
+ !!pesData &&
+ !patternUploaded &&
+ !isUploading &&
+ !uploadedPesData
}
/>
>
diff --git a/src/components/PatternSummaryCard.tsx b/src/components/PatternSummaryCard.tsx
index 71874c5..ec02865 100644
--- a/src/components/PatternSummaryCard.tsx
+++ b/src/components/PatternSummaryCard.tsx
@@ -25,14 +25,16 @@ export function PatternSummaryCard() {
);
// Pattern store
- const { pesData, currentFileName } = usePatternStore(
+ const { pesData, uploadedPesData, currentFileName } = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
+ uploadedPesData: state.uploadedPesData,
currentFileName: state.currentFileName,
})),
);
- if (!pesData) return null;
+ const displayPattern = uploadedPesData || pesData;
+ if (!displayPattern) return null;
const canDelete = canDeletePattern(machineStatus);
return (
@@ -52,7 +54,7 @@ export function PatternSummaryCard() {
-
+
{canDelete && (