mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 10:23:41 +00:00
- Add lock stitch rotation based on movement direction (matches C# PesxToPen.cs) - Calculate direction by accumulating vectors up to 5 stitches or 8.0 units - Scale direction vectors from magnitude 8.0 down to 0.4 for proper lock stitch size - Generate 8 lock stitches (not 4) alternating between +dir and -dir - Remove PyStitch duplicate position stitches during color changes - Add long jump detection with automatic lock stitches and cut commands - Improve color change sequence: finish locks, cut, jump, COLOR_END, start locks - Parse PEN data to get actual stitches for rendering (fixes jump stitch colors) - Add encodeStitchPosition() helper function for coordinate encoding - Improve pattern info refresh timing after mask trace - Add detailed logging for PEN encoding and pattern info responses 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
135 lines
4.1 KiB
TypeScript
135 lines
4.1 KiB
TypeScript
import type { PenData, PenStitch, PenColorBlock } from '../types/machine';
|
|
|
|
// PEN format flags
|
|
const PEN_FEED_DATA = 0x01; // Y-coordinate low byte, bit 0
|
|
const PEN_COLOR_END = 0x03; // X-coordinate low byte, bits 0-2
|
|
const PEN_DATA_END = 0x05; // X-coordinate low byte, bits 0-2
|
|
|
|
export function parsePenData(data: Uint8Array): PenData {
|
|
if (data.length < 4 || data.length % 4 !== 0) {
|
|
throw new Error(`Invalid PEN data size: ${data.length} bytes`);
|
|
}
|
|
|
|
const stitches: PenStitch[] = [];
|
|
const colorBlocks: PenColorBlock[] = [];
|
|
const stitchCount = data.length / 4;
|
|
|
|
let currentColorStart = 0;
|
|
let currentColor = 0;
|
|
let minX = Infinity, maxX = -Infinity;
|
|
let minY = Infinity, maxY = -Infinity;
|
|
|
|
console.log(`Parsing PEN data: ${data.length} bytes, ${stitchCount} stitches`);
|
|
|
|
for (let i = 0; i < stitchCount; i++) {
|
|
const offset = i * 4;
|
|
|
|
// Extract coordinates (shifted left by 3 bits in PEN format)
|
|
const xRaw = data[offset] | (data[offset + 1] << 8);
|
|
const yRaw = data[offset + 2] | (data[offset + 3] << 8);
|
|
|
|
// Extract flags from low 3 bits
|
|
const xFlags = data[offset] & 0x07;
|
|
const yFlags = data[offset + 2] & 0x07;
|
|
|
|
// Decode coordinates (shift right by 3 to get actual position)
|
|
// The coordinates are stored as signed 16-bit values, left-shifted by 3
|
|
// Step 1: Clear the flag bits (low 3 bits) from the raw values
|
|
const xRawClean = xRaw & 0xFFF8;
|
|
const yRawClean = yRaw & 0xFFF8;
|
|
|
|
// Step 2: Convert from unsigned 16-bit to signed 16-bit
|
|
let xSigned = xRawClean;
|
|
let ySigned = yRawClean;
|
|
if (xSigned > 0x7FFF) xSigned = xSigned - 0x10000;
|
|
if (ySigned > 0x7FFF) ySigned = ySigned - 0x10000;
|
|
|
|
// Step 3: Shift right by 3 (arithmetic shift, preserves sign)
|
|
let x = xSigned >> 3;
|
|
let y = ySigned >> 3;
|
|
|
|
const stitch: PenStitch = {
|
|
x,
|
|
y,
|
|
flags: (xFlags & 0x07) | (yFlags & 0x07),
|
|
isJump: (yFlags & PEN_FEED_DATA) !== 0,
|
|
};
|
|
|
|
stitches.push(stitch);
|
|
|
|
// Track bounds
|
|
if (!stitch.isJump) {
|
|
minX = Math.min(minX, x);
|
|
maxX = Math.max(maxX, x);
|
|
minY = Math.min(minY, y);
|
|
maxY = Math.max(maxY, y);
|
|
}
|
|
|
|
// Check for color change or data end
|
|
if (xFlags === PEN_COLOR_END) {
|
|
const block: PenColorBlock = {
|
|
startStitch: currentColorStart,
|
|
endStitch: i,
|
|
colorIndex: currentColor,
|
|
};
|
|
colorBlocks.push(block);
|
|
|
|
console.log(
|
|
`Color ${currentColor}: stitches ${currentColorStart}-${i} (${
|
|
i - currentColorStart + 1
|
|
} stitches)`
|
|
);
|
|
|
|
currentColor++;
|
|
currentColorStart = i + 1;
|
|
} else if (xFlags === PEN_DATA_END) {
|
|
if (currentColorStart < i) {
|
|
const block: PenColorBlock = {
|
|
startStitch: currentColorStart,
|
|
endStitch: i,
|
|
colorIndex: currentColor,
|
|
};
|
|
colorBlocks.push(block);
|
|
|
|
console.log(
|
|
`Color ${currentColor} (final): stitches ${currentColorStart}-${i} (${
|
|
i - currentColorStart + 1
|
|
} stitches)`
|
|
);
|
|
|
|
currentColor++;
|
|
}
|
|
console.log(`Data end marker at stitch ${i}`);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const result: PenData = {
|
|
stitches,
|
|
colorBlocks,
|
|
totalStitches: stitches.length,
|
|
colorCount: colorBlocks.length,
|
|
bounds: {
|
|
minX: minX === Infinity ? 0 : minX,
|
|
maxX: maxX === -Infinity ? 0 : maxX,
|
|
minY: minY === Infinity ? 0 : minY,
|
|
maxY: maxY === -Infinity ? 0 : maxY,
|
|
},
|
|
};
|
|
|
|
console.log(
|
|
`Parsed: ${result.totalStitches} stitches, ${result.colorCount} colors`
|
|
);
|
|
console.log(`Bounds: (${result.bounds.minX}, ${result.bounds.minY}) to (${result.bounds.maxX}, ${result.bounds.maxY})`);
|
|
|
|
return result;
|
|
}
|
|
|
|
export function getStitchColor(penData: PenData, stitchIndex: number): number {
|
|
for (const block of penData.colorBlocks) {
|
|
if (stitchIndex >= block.startStitch && stitchIndex <= block.endStitch) {
|
|
return block.colorIndex;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|