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++;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue