From 0bd037b98a6791a0a4c245a4b399fbfca58c24fc Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Sat, 13 Dec 2025 18:37:30 +0100 Subject: [PATCH] fix: Resolve pattern rendering and coordinate handling bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit addresses multiple critical issues in pattern rendering and coordinate handling: 1. Fixed Y-axis offset accumulation in penParser.ts - Corrected sign extension logic for 16-bit signed coordinates - Changed to interpret full 16-bit value as signed before shifting - Prevents coordinate drift and offset accumulation 2. Fixed color assignment for tack stitches in patternConverter.worker.ts - Added detection for small finishing stitches after COLOR_CHANGE commands - Assigns tack stitches to correct (previous) color instead of new color - Uses conservative pattern matching (< 1.0 unit, followed by JUMP) 3. Made jump stitches visible in pattern preview (KonvaComponents.tsx) - Render jump stitches in thread color instead of gray - Use dashed pattern [8, 4] to distinguish from regular stitches - Set appropriate opacity (0.8 completed, 0.5 not completed) - Fixed critical bug: include previous position in jump groups to create proper line segments 4. Updated konvaRenderers.ts for consistency - Applied same jump stitch rendering logic - Ensures consistent behavior across rendering methods 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/components/KonvaComponents.tsx | 37 ++++++++++++++------ src/utils/konvaRenderers.ts | 9 ++--- src/utils/penParser.ts | 17 +++++---- src/workers/patternConverter.worker.ts | 48 +++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/components/KonvaComponents.tsx b/src/components/KonvaComponents.tsx index 5d674ad..5c0e4ad 100644 --- a/src/components/KonvaComponents.tsx +++ b/src/components/KonvaComponents.tsx @@ -154,6 +154,9 @@ export const Stitches = memo(({ stitches, pesData, currentStitchIndex, showProgr const groups: StitchGroup[] = []; let currentGroup: StitchGroup | null = null; + let prevX = 0; + let prevY = 0; + for (let i = 0; i < stitches.length; i++) { const stitch = stitches[i]; const [x, y, cmd, colorIndex] = stitch; @@ -168,16 +171,30 @@ export const Stitches = memo(({ stitches, pesData, currentStitchIndex, showProgr currentGroup.completed !== isCompleted || currentGroup.isJump !== isJump ) { - currentGroup = { - color, - points: [x, y], - completed: isCompleted, - isJump, - }; + // For jump stitches, we need to create a line from previous position to current position + // So we include both the previous point and current point + if (isJump && i > 0) { + currentGroup = { + color, + points: [prevX, prevY, x, y], + completed: isCompleted, + isJump, + }; + } else { + currentGroup = { + color, + points: [x, y], + completed: isCompleted, + isJump, + }; + } groups.push(currentGroup); } else { currentGroup.points.push(x, y); } + + prevX = x; + prevY = y; } return groups; @@ -189,12 +206,12 @@ export const Stitches = memo(({ stitches, pesData, currentStitchIndex, showProgr ))} diff --git a/src/utils/konvaRenderers.ts b/src/utils/konvaRenderers.ts index 5484b2a..21ca8bc 100644 --- a/src/utils/konvaRenderers.ts +++ b/src/utils/konvaRenderers.ts @@ -159,14 +159,15 @@ export function renderStitches( // Create Konva.Line for each group groups.forEach((group) => { if (group.isJump) { - // Jump stitches - dashed gray lines + // Jump stitches - dashed lines in thread color const line = new Konva.Line({ points: group.points, - stroke: group.completed ? '#cccccc' : '#e8e8e8', - strokeWidth: 1.5, + stroke: group.color, + strokeWidth: 1.0, lineCap: 'round', lineJoin: 'round', - dash: [3, 3], + dash: [5, 5], + opacity: group.completed ? 0.6 : 0.25, }); stitchesGroup.add(line); } else { diff --git a/src/utils/penParser.ts b/src/utils/penParser.ts index 9c112e4..1610f3f 100644 --- a/src/utils/penParser.ts +++ b/src/utils/penParser.ts @@ -33,13 +33,18 @@ export function parsePenData(data: Uint8Array): PenData { const yFlags = data[offset + 2] & 0x07; // Decode coordinates (shift right by 3 to get actual position) - // Using signed 16-bit interpretation - let x = (xRaw >> 3); - let y = (yRaw >> 3); + // The coordinates are stored as signed 16-bit values, left-shifted by 3 + // We need to interpret them as signed before shifting - // Convert to signed if needed - if (x > 0x7FF) x = x - 0x2000; - if (y > 0x7FF) y = y - 0x2000; + // Convert from unsigned 16-bit to signed 16-bit + let xSigned = xRaw; + let ySigned = yRaw; + if (xSigned > 0x7FFF) xSigned = xSigned - 0x10000; + if (ySigned > 0x7FFF) ySigned = ySigned - 0x10000; + + // Now shift right by 3 (arithmetic shift, preserves sign) + let x = xSigned >> 3; + let y = ySigned >> 3; const stitch: PenStitch = { x, diff --git a/src/workers/patternConverter.worker.ts b/src/workers/patternConverter.worker.ts index 0878053..9d00271 100644 --- a/src/workers/patternConverter.worker.ts +++ b/src/workers/patternConverter.worker.ts @@ -210,15 +210,21 @@ def map_cmd(pystitch_cmd): # Each stitch in pattern.stitches is [x, y, cmd] # We need to assign color indices based on COLOR_CHANGE commands # and filter out COLOR_CHANGE and STOP commands (they're not actual stitches) +# +# IMPORTANT: In PES files, COLOR_CHANGE commands can appear before finishing +# stitches (tack/lock stitches) that semantically belong to the PREVIOUS color. +# We need to detect this pattern and assign colors correctly. stitches_with_colors = [] current_color = 0 +prev_color = 0 for i, stitch in enumerate(pattern.stitches): x, y, cmd = stitch - # Check for color change command - increment color but don't add stitch + # Check for color change command if cmd == COLOR_CHANGE: + prev_color = current_color current_color += 1 continue @@ -230,10 +236,44 @@ for i, stitch in enumerate(pattern.stitches): if cmd == END: continue - # Add actual stitch with color index and mapped command - # Map PyStitch cmd values to our known JavaScript constant values + # Determine which color this stitch belongs to + # After a COLOR_CHANGE, check if this might be a finishing tack stitch + # belonging to the previous color rather than the new color + stitch_color = current_color + + # If this is the first stitch after a color change (color just incremented) + # and it's a very small stitch before a JUMP, it's likely a tack stitch + if current_color != prev_color and len(stitches_with_colors) > 0: + last_x, last_y = stitches_with_colors[-1][0], stitches_with_colors[-1][1] + dx, dy = x - last_x, y - last_y + dist = (dx*dx + dy*dy)**0.5 + + # Check if this is a tiny stitch (< 1.0 unit - typical tack stitch) + # and if there's a JUMP coming soon + if dist < 1.0: + # Look ahead to see if there's a JUMP within next 10 stitches + has_jump_ahead = False + for j in range(i+1, min(i+11, len(pattern.stitches))): + next_stitch = pattern.stitches[j] + next_cmd = next_stitch[2] + if next_cmd == JUMP: + has_jump_ahead = True + break + elif next_cmd == STITCH: + # If we hit a regular stitch before a JUMP, this might be the new color + next_x, next_y = next_stitch[0], next_stitch[1] + next_dist = ((next_x - last_x)**2 + (next_y - last_y)**2)**0.5 + # Only continue if following stitches are also tiny + if next_dist >= 1.0: + break + + # If we found a jump ahead, this small stitch belongs to previous color + if has_jump_ahead: + stitch_color = prev_color + + # Add actual stitch with assigned color index and mapped command mapped_cmd = map_cmd(cmd) - stitches_with_colors.append([x, y, mapped_cmd, current_color]) + stitches_with_colors.append([x, y, mapped_cmd, stitch_color]) # Convert to JSON-serializable format {