mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 02:13:41 +00:00
fix: Combine COLOR_END and CUT flags on same stitch for color changes
The COLOR_END and CUT commands must be on the same stitch, not separate stitches. This was causing the machine to execute an extra stitch with the new color before the jump to the new position. Changes: - Combine COLOR_END (X flag) and CUT (Y flag) into single stitch at old position - Machine now correctly pauses after cut, before jumping to new color section - Update all color change tests to expect combined COLOR_END+CUT stitch The correct sequence is now: 1. Finishing lock stitches (old color) 2. COLOR_END+CUT stitch (old color) ← Machine pauses here 3. Jump to new position (new color) 4. Starting lock stitches (new color) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6048a61230
commit
7a1178166a
2 changed files with 39 additions and 60 deletions
|
|
@ -298,21 +298,15 @@ describe('encodeStitchesToPen', () => {
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Cut command
|
// 4. COLOR_END + CUT command (combined on same stitch)
|
||||||
const cutStitch = decoded[idx];
|
const colorEndCutStitch = decoded[idx];
|
||||||
expect(cutStitch.x).toBe(10);
|
expect(colorEndCutStitch.x).toBe(10);
|
||||||
expect(cutStitch.y).toBe(0);
|
expect(colorEndCutStitch.y).toBe(0);
|
||||||
expect(cutStitch.isCut).toBe(true);
|
expect(colorEndCutStitch.isCut).toBe(true);
|
||||||
|
expect(colorEndCutStitch.isColorEnd).toBe(true);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
// 5. COLOR_END marker (no jump needed since same position)
|
// 5. 8 starting lock stitches for new color
|
||||||
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
|
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
const lockStitch = decoded[idx];
|
const lockStitch = decoded[idx];
|
||||||
expect(lockStitch.x).to.be.closeTo(10, LOCK_STITCH_JUMP_SIZE);
|
expect(lockStitch.x).to.be.closeTo(10, LOCK_STITCH_JUMP_SIZE);
|
||||||
|
|
@ -320,7 +314,7 @@ describe('encodeStitchesToPen', () => {
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. First stitch of new color
|
// 6. First stitch of new color
|
||||||
expect(decoded[idx].x).toBe(10);
|
expect(decoded[idx].x).toBe(10);
|
||||||
expect(decoded[idx].y).toBe(0);
|
expect(decoded[idx].y).toBe(0);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
@ -353,9 +347,11 @@ describe('encodeStitchesToPen', () => {
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Cut command at (10, 0)
|
// 2. COLOR_END + CUT command at (10, 0) - combined on same stitch
|
||||||
expect(decoded[idx].isCut).toBe(true);
|
|
||||||
expect(decoded[idx].x).toBe(10);
|
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++;
|
idx++;
|
||||||
|
|
||||||
// 3. Jump to new position (30, 10)
|
// 3. Jump to new position (30, 10)
|
||||||
|
|
@ -364,20 +360,14 @@ describe('encodeStitchesToPen', () => {
|
||||||
expect(decoded[idx].isFeed).toBe(true);
|
expect(decoded[idx].isFeed).toBe(true);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
// 4. COLOR_END marker at (30, 10)
|
// 4. 8 starting lock stitches 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)
|
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
expect(decoded[idx].x).to.be.closeTo(30, LOCK_STITCH_JUMP_SIZE);
|
expect(decoded[idx].x).to.be.closeTo(30, LOCK_STITCH_JUMP_SIZE);
|
||||||
expect(decoded[idx].y).to.be.closeTo(10, LOCK_STITCH_JUMP_SIZE);
|
expect(decoded[idx].y).to.be.closeTo(10, LOCK_STITCH_JUMP_SIZE);
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Continue with new color stitches
|
// 5. Continue with new color stitches
|
||||||
expect(decoded[idx].x).toBe(30);
|
expect(decoded[idx].x).toBe(30);
|
||||||
expect(decoded[idx].y).toBe(10);
|
expect(decoded[idx].y).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
@ -421,10 +411,11 @@ describe('encodeStitchesToPen', () => {
|
||||||
idx++;
|
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].x).toBe(10);
|
||||||
expect(decoded[idx].y).toBe(0);
|
expect(decoded[idx].y).toBe(0);
|
||||||
expect(decoded[idx].isCut).toBe(true);
|
expect(decoded[idx].isCut).toBe(true);
|
||||||
|
expect(decoded[idx].isColorEnd).toBe(true);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
// 5. Jump to new location (50, 20) - extracted from the MOVE stitch
|
// 5. Jump to new location (50, 20) - extracted from the MOVE stitch
|
||||||
|
|
@ -433,26 +424,20 @@ describe('encodeStitchesToPen', () => {
|
||||||
expect(decoded[idx].isFeed).toBe(true);
|
expect(decoded[idx].isFeed).toBe(true);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
// 6. COLOR_END marker at (50, 20)
|
// 6. 8 starting lock stitches 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)
|
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
expect(decoded[idx].x).to.be.closeTo(50, LOCK_STITCH_JUMP_SIZE);
|
expect(decoded[idx].x).to.be.closeTo(50, LOCK_STITCH_JUMP_SIZE);
|
||||||
expect(decoded[idx].y).to.be.closeTo(20, LOCK_STITCH_JUMP_SIZE);
|
expect(decoded[idx].y).to.be.closeTo(20, LOCK_STITCH_JUMP_SIZE);
|
||||||
idx++;
|
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].x).toBe(50);
|
||||||
expect(decoded[idx].y).toBe(20);
|
expect(decoded[idx].y).toBe(20);
|
||||||
expect(decoded[idx].isFeed).toBe(false);
|
expect(decoded[idx].isFeed).toBe(false);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
// 9. Last stitch with DATA_END
|
// 8. Last stitch with DATA_END
|
||||||
expect(decoded[idx].x).toBe(60);
|
expect(decoded[idx].x).toBe(60);
|
||||||
expect(decoded[idx].y).toBe(20);
|
expect(decoded[idx].y).toBe(20);
|
||||||
expect(decoded[idx].isDataEnd).toBe(true);
|
expect(decoded[idx].isDataEnd).toBe(true);
|
||||||
|
|
@ -666,7 +651,7 @@ describe('encodeStitchesToPen', () => {
|
||||||
|
|
||||||
const result = encodeStitchesToPen(stitches);
|
const result = encodeStitchesToPen(stitches);
|
||||||
const decoded = decodeAllPenStitches(result.penBytes);
|
const decoded = decodeAllPenStitches(result.penBytes);
|
||||||
console.log(decoded);
|
|
||||||
// Expected sequence:
|
// Expected sequence:
|
||||||
// 0. Feed move to proper location (should always happen here)
|
// 0. Feed move to proper location (should always happen here)
|
||||||
// 1. 8 starting lock stitches at (10, 20)
|
// 1. 8 starting lock stitches at (10, 20)
|
||||||
|
|
|
||||||
|
|
@ -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 FEED_LENGTH = 50; // Long jump threshold requiring lock stitches and cut
|
||||||
const TARGET_LENGTH = 8.0; // Target accumulated length for lock stitch direction
|
const TARGET_LENGTH = 8.0; // Target accumulated length for lock stitch direction
|
||||||
const MAX_POINTS = 5; // Maximum points to accumulate 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
|
const LOCK_STITCH_SCALE = LOCK_STITCH_JUMP_SIZE / 8.0; // Scale the magnitude-8 vector down to 4
|
||||||
|
|
||||||
export interface StitchData {
|
export interface StitchData {
|
||||||
|
|
@ -286,7 +286,7 @@ export function encodeStitchesToPen(stitches: number[][]): PenEncodingResult {
|
||||||
penStitches.push(...generateLockStitches(absX, absY, startDir.dirX, startDir.dirY));
|
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) {
|
if (isColorChange) {
|
||||||
const nextStitchCmd = nextStitch[2];
|
const nextStitchCmd = nextStitch[2];
|
||||||
const nextStitchX = Math.round(nextStitch[0]);
|
const nextStitchX = Math.round(nextStitch[0]);
|
||||||
|
|
@ -299,17 +299,26 @@ export function encodeStitchesToPen(stitches: number[][]): PenEncodingResult {
|
||||||
const finishDir = calculateLockDirection(stitches, i, true);
|
const finishDir = calculateLockDirection(stitches, i, true);
|
||||||
penStitches.push(...generateLockStitches(absX, absY, finishDir.dirX, finishDir.dirY));
|
penStitches.push(...generateLockStitches(absX, absY, finishDir.dirX, finishDir.dirY));
|
||||||
|
|
||||||
// Step 2: Add cut command at current position
|
// Step 2: Add COLOR_END + CUT command at CURRENT position (same stitch!)
|
||||||
const cutXEncoded = (absX << 3) & 0xffff;
|
// This is where the machine pauses and waits for the user to change thread color
|
||||||
const cutYEncoded = ((absY << 3) & 0xffff) | PEN_CUT_DATA;
|
// 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(
|
penStitches.push(
|
||||||
cutXEncoded & 0xff,
|
colorEndCutXEncoded & 0xff,
|
||||||
(cutXEncoded >> 8) & 0xff,
|
(colorEndCutXEncoded >> 8) & 0xff,
|
||||||
cutYEncoded & 0xff,
|
colorEndCutYEncoded & 0xff,
|
||||||
(cutYEncoded >> 8) & 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
|
// Step 3: If next stitch is a JUMP, encode it and skip it in the loop
|
||||||
// Otherwise, add a jump ourselves if positions differ
|
// Otherwise, add a jump ourselves if positions differ
|
||||||
const jumpToX = nextStitchX;
|
const jumpToX = nextStitchX;
|
||||||
|
|
@ -334,22 +343,7 @@ export function encodeStitchesToPen(stitches: number[][]): PenEncodingResult {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Add COLOR_END marker at NEW position
|
// Step 4: Add starting lock stitches at the 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
|
|
||||||
// Loop A: Jump/Entry Vector - Look FORWARD at upcoming stitches in new color
|
// 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
|
// This hides the knot under the stitches we're about to make
|
||||||
const nextStitchIdx = nextIsJump ? i + 2 : i + 1;
|
const nextStitchIdx = nextIsJump ? i + 2 : i + 1;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue