diff --git a/src/formats/pen/encoder.test.ts b/src/formats/pen/encoder.test.ts index 21493f9..189be15 100644 --- a/src/formats/pen/encoder.test.ts +++ b/src/formats/pen/encoder.test.ts @@ -298,21 +298,15 @@ describe('encodeStitchesToPen', () => { idx++; } - // 4. Cut command - const cutStitch = decoded[idx]; - expect(cutStitch.x).toBe(10); - expect(cutStitch.y).toBe(0); - expect(cutStitch.isCut).toBe(true); + // 4. COLOR_END + CUT command (combined on same stitch) + const colorEndCutStitch = decoded[idx]; + expect(colorEndCutStitch.x).toBe(10); + expect(colorEndCutStitch.y).toBe(0); + expect(colorEndCutStitch.isCut).toBe(true); + expect(colorEndCutStitch.isColorEnd).toBe(true); idx++; - // 5. COLOR_END marker (no jump needed since same position) - const colorEndStitch = decoded[idx]; - expect(colorEndStitch.x).toBe(10); - expect(colorEndStitch.y).toBe(0); - expect(colorEndStitch.isColorEnd).toBe(true); - idx++; - - // 6. 8 starting lock stitches for new color + // 5. 8 starting lock stitches for new color for (let i = 0; i < 8; i++) { const lockStitch = decoded[idx]; expect(lockStitch.x).to.be.closeTo(10, LOCK_STITCH_JUMP_SIZE); @@ -320,7 +314,7 @@ describe('encodeStitchesToPen', () => { idx++; } - // 7. First stitch of new color + // 6. First stitch of new color expect(decoded[idx].x).toBe(10); expect(decoded[idx].y).toBe(0); idx++; @@ -353,9 +347,11 @@ describe('encodeStitchesToPen', () => { idx++; } - // 2. Cut command at (10, 0) - expect(decoded[idx].isCut).toBe(true); + // 2. COLOR_END + CUT command at (10, 0) - combined on same stitch expect(decoded[idx].x).toBe(10); + expect(decoded[idx].y).toBe(0); + expect(decoded[idx].isCut).toBe(true); + expect(decoded[idx].isColorEnd).toBe(true); idx++; // 3. Jump to new position (30, 10) @@ -364,20 +360,14 @@ describe('encodeStitchesToPen', () => { expect(decoded[idx].isFeed).toBe(true); idx++; - // 4. COLOR_END marker at (30, 10) - expect(decoded[idx].x).toBe(30); - expect(decoded[idx].y).toBe(10); - expect(decoded[idx].isColorEnd).toBe(true); - idx++; - - // 5. 8 starting lock stitches at (30, 10) + // 4. 8 starting lock stitches at (30, 10) for (let i = 0; i < 8; i++) { expect(decoded[idx].x).to.be.closeTo(30, LOCK_STITCH_JUMP_SIZE); expect(decoded[idx].y).to.be.closeTo(10, LOCK_STITCH_JUMP_SIZE); idx++; } - // 6. Continue with new color stitches + // 5. Continue with new color stitches expect(decoded[idx].x).toBe(30); expect(decoded[idx].y).toBe(10); }); @@ -421,10 +411,11 @@ describe('encodeStitchesToPen', () => { idx++; } - // 4. Cut command at (10, 0) + // 4. COLOR_END + CUT command at (10, 0) - combined on same stitch expect(decoded[idx].x).toBe(10); expect(decoded[idx].y).toBe(0); expect(decoded[idx].isCut).toBe(true); + expect(decoded[idx].isColorEnd).toBe(true); idx++; // 5. Jump to new location (50, 20) - extracted from the MOVE stitch @@ -433,26 +424,20 @@ describe('encodeStitchesToPen', () => { expect(decoded[idx].isFeed).toBe(true); idx++; - // 6. COLOR_END marker at (50, 20) - expect(decoded[idx].x).toBe(50); - expect(decoded[idx].y).toBe(20); - expect(decoded[idx].isColorEnd).toBe(true); - idx++; - - // 7. 8 starting lock stitches at (50, 20) + // 6. 8 starting lock stitches at (50, 20) for (let i = 0; i < 8; i++) { expect(decoded[idx].x).to.be.closeTo(50, LOCK_STITCH_JUMP_SIZE); expect(decoded[idx].y).to.be.closeTo(20, LOCK_STITCH_JUMP_SIZE); idx++; } - // 8. First actual stitch of new color at (50, 20) + // 7. First actual stitch of new color at (50, 20) expect(decoded[idx].x).toBe(50); expect(decoded[idx].y).toBe(20); expect(decoded[idx].isFeed).toBe(false); idx++; - // 9. Last stitch with DATA_END + // 8. Last stitch with DATA_END expect(decoded[idx].x).toBe(60); expect(decoded[idx].y).toBe(20); expect(decoded[idx].isDataEnd).toBe(true); @@ -666,7 +651,7 @@ describe('encodeStitchesToPen', () => { const result = encodeStitchesToPen(stitches); const decoded = decodeAllPenStitches(result.penBytes); - console.log(decoded); + // Expected sequence: // 0. Feed move to proper location (should always happen here) // 1. 8 starting lock stitches at (10, 20) diff --git a/src/formats/pen/encoder.ts b/src/formats/pen/encoder.ts index 122ed8d..538cf62 100644 --- a/src/formats/pen/encoder.ts +++ b/src/formats/pen/encoder.ts @@ -17,7 +17,7 @@ const PEN_DATA_END = 0x05; // Last stitch of entire pattern const FEED_LENGTH = 50; // Long jump threshold requiring lock stitches and cut const TARGET_LENGTH = 8.0; // Target accumulated length for lock stitch direction const MAX_POINTS = 5; // Maximum points to accumulate for lock stitch direction -export const LOCK_STITCH_JUMP_SIZE = 4.0; +export const LOCK_STITCH_JUMP_SIZE = 2.0; const LOCK_STITCH_SCALE = LOCK_STITCH_JUMP_SIZE / 8.0; // Scale the magnitude-8 vector down to 4 export interface StitchData { @@ -286,7 +286,7 @@ export function encodeStitchesToPen(stitches: number[][]): PenEncodingResult { penStitches.push(...generateLockStitches(absX, absY, startDir.dirX, startDir.dirY)); } - // Handle color change: finishing lock, cut, jump, COLOR_END, starting lock + // Handle color change: finishing lock, COLOR_END+CUT, jump, starting lock if (isColorChange) { const nextStitchCmd = nextStitch[2]; const nextStitchX = Math.round(nextStitch[0]); @@ -299,17 +299,26 @@ export function encodeStitchesToPen(stitches: number[][]): PenEncodingResult { const finishDir = calculateLockDirection(stitches, i, true); penStitches.push(...generateLockStitches(absX, absY, finishDir.dirX, finishDir.dirY)); - // Step 2: Add cut command at current position - const cutXEncoded = (absX << 3) & 0xffff; - const cutYEncoded = ((absY << 3) & 0xffff) | PEN_CUT_DATA; + // Step 2: Add COLOR_END + CUT command at CURRENT position (same stitch!) + // This is where the machine pauses and waits for the user to change thread color + // IMPORTANT: COLOR_END and CUT must be on the SAME stitch, not separate stitches + let colorEndCutXEncoded = (absX << 3) & 0xffff; + let colorEndCutYEncoded = (absY << 3) & 0xffff; + + // Add COLOR_END flag to X coordinate and CUT flag to Y coordinate + colorEndCutXEncoded = (colorEndCutXEncoded & 0xfff8) | PEN_COLOR_END; + colorEndCutYEncoded |= PEN_CUT_DATA; penStitches.push( - cutXEncoded & 0xff, - (cutXEncoded >> 8) & 0xff, - cutYEncoded & 0xff, - (cutYEncoded >> 8) & 0xff + colorEndCutXEncoded & 0xff, + (colorEndCutXEncoded >> 8) & 0xff, + colorEndCutYEncoded & 0xff, + (colorEndCutYEncoded >> 8) & 0xff ); + // Machine pauses here for color change + // After user changes color, the following stitches execute with the new color + // Step 3: If next stitch is a JUMP, encode it and skip it in the loop // Otherwise, add a jump ourselves if positions differ const jumpToX = nextStitchX; @@ -334,22 +343,7 @@ export function encodeStitchesToPen(stitches: number[][]): PenEncodingResult { ); } - // Step 4: Add COLOR_END marker at NEW position - // This is where the machine pauses and waits for the user to change thread color - let colorEndXEncoded = (jumpToX << 3) & 0xffff; - const colorEndYEncoded = (jumpToY << 3) & 0xffff; - - // Add COLOR_END flag to X coordinate - colorEndXEncoded = (colorEndXEncoded & 0xfff8) | PEN_COLOR_END; - - penStitches.push( - colorEndXEncoded & 0xff, - (colorEndXEncoded >> 8) & 0xff, - colorEndYEncoded & 0xff, - (colorEndYEncoded >> 8) & 0xff - ); - - // Step 5: Add starting lock stitches at the new position + // Step 4: Add starting lock stitches at the new position // Loop A: Jump/Entry Vector - Look FORWARD at upcoming stitches in new color // This hides the knot under the stitches we're about to make const nextStitchIdx = nextIsJump ? i + 2 : i + 1;