fix: Calculate time correctly per color block using Brother formula

Implemented proper time calculation matching the Brother app algorithm:
- 150ms per stitch + 3000ms startup time per color block
- Calculate total and elapsed time by summing across color blocks
- This fixes the "999 seconds" issue by calculating time accurately

Created timeCalculation utility with:
- convertStitchesToMinutes: Convert stitches to minutes using PP1 formula
- calculatePatternTime: Calculate total/elapsed time per color blocks

Updated ProgressMonitor to show:
- Total Time (calculated from all color blocks)
- Elapsed Time / Total Time (based on current stitch position)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Henrik Bruhn 2025-12-17 12:19:24 +01:00
parent f2d05c2714
commit bc46fe0015
2 changed files with 81 additions and 6 deletions

View file

@ -21,6 +21,7 @@ import {
canResumeSewing, canResumeSewing,
getStateVisualInfo, getStateVisualInfo,
} from "../utils/machineStateHelpers"; } from "../utils/machineStateHelpers";
import { calculatePatternTime } from "../utils/timeCalculation";
export function ProgressMonitor() { export function ProgressMonitor() {
// Machine store // Machine store
@ -109,6 +110,15 @@ export function ProgressMonitor() {
currentStitch >= block.startStitch && currentStitch < block.endStitch, currentStitch >= block.startStitch && currentStitch < block.endStitch,
); );
// Calculate time based on color blocks (matches Brother app calculation)
const { totalMinutes, elapsedMinutes } = useMemo(() => {
if (colorBlocks.length === 0) {
return { totalMinutes: 0, elapsedMinutes: 0 };
}
const result = calculatePatternTime(colorBlocks, currentStitch);
return { totalMinutes: result.totalMinutes, elapsedMinutes: result.elapsedMinutes };
}, [colorBlocks, currentStitch]);
// Auto-scroll to current block // Auto-scroll to current block
useEffect(() => { useEffect(() => {
if (currentBlockRef.current) { if (currentBlockRef.current) {
@ -185,11 +195,10 @@ export function ProgressMonitor() {
</div> </div>
<div className="bg-gray-50 dark:bg-gray-700/50 p-2 rounded"> <div className="bg-gray-50 dark:bg-gray-700/50 p-2 rounded">
<span className="text-gray-600 dark:text-gray-400 block"> <span className="text-gray-600 dark:text-gray-400 block">
Est. Time Total Time
</span> </span>
<span className="font-semibold text-gray-900 dark:text-gray-100"> <span className="font-semibold text-gray-900 dark:text-gray-100">
{Math.floor(patternInfo.totalTime / 60)}: {totalMinutes} min
{String(patternInfo.totalTime % 60).padStart(2, "0")}
</span> </span>
</div> </div>
<div className="bg-gray-50 dark:bg-gray-700/50 p-2 rounded"> <div className="bg-gray-50 dark:bg-gray-700/50 p-2 rounded">
@ -225,11 +234,10 @@ export function ProgressMonitor() {
</div> </div>
<div className="bg-gray-50 dark:bg-gray-700/50 p-2 rounded"> <div className="bg-gray-50 dark:bg-gray-700/50 p-2 rounded">
<span className="text-gray-600 dark:text-gray-400 block"> <span className="text-gray-600 dark:text-gray-400 block">
Time Elapsed Time
</span> </span>
<span className="font-semibold text-gray-900 dark:text-gray-100"> <span className="font-semibold text-gray-900 dark:text-gray-100">
{Math.floor(sewingProgress.currentTime / 60)}: {elapsedMinutes} / {totalMinutes} min
{String(sewingProgress.currentTime % 60).padStart(2, "0")}
</span> </span>
</div> </div>
</div> </div>

View file

@ -0,0 +1,67 @@
/**
* Convert stitch count to minutes using Brother PP1 timing formula
* Formula: ((pointCount - 1) * 150 + 3000) / 60000
* - 150ms per stitch
* - 3000ms startup time
* - Result in minutes (rounded up)
*/
export function convertStitchesToMinutes(stitchCount: number): number {
if (stitchCount <= 1) return 0;
const timeMs = (stitchCount - 1) * 150 + 3000;
const timeMin = Math.ceil(timeMs / 60000);
return timeMin < 1 ? 1 : timeMin;
}
/**
* Calculate total and elapsed time for a pattern based on color blocks
* This matches the Brother app's calculation method
*/
export function calculatePatternTime(
colorBlocks: Array<{ stitchCount: number }>,
currentStitch: number
): {
totalMinutes: number;
elapsedMinutes: number;
remainingMinutes: number;
} {
let totalMinutes = 0;
let elapsedMinutes = 0;
let cumulativeStitches = 0;
// Calculate time per color block
for (const block of colorBlocks) {
totalMinutes += convertStitchesToMinutes(block.stitchCount);
cumulativeStitches += block.stitchCount;
if (cumulativeStitches < currentStitch) {
// This entire block is completed
elapsedMinutes += convertStitchesToMinutes(block.stitchCount);
} else if (cumulativeStitches === currentStitch) {
// We just completed this block
elapsedMinutes += convertStitchesToMinutes(block.stitchCount);
break;
} else {
// We're partway through this block
const stitchesInBlock = currentStitch - (cumulativeStitches - block.stitchCount);
elapsedMinutes += convertStitchesToMinutes(stitchesInBlock);
break;
}
}
return {
totalMinutes,
elapsedMinutes,
remainingMinutes: Math.max(0, totalMinutes - elapsedMinutes),
};
}
/**
* Format minutes as MM:SS
*/
export function formatMinutes(minutes: number): string {
const mins = Math.floor(minutes);
const secs = Math.round((minutes - mins) * 60);
return `${mins}:${String(secs).padStart(2, '0')}`;
}