mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 10:23:41 +00:00
fix: Resolve pattern rendering and coordinate handling bugs
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 <noreply@anthropic.com>
This commit is contained in:
parent
8c0b893599
commit
0bd037b98a
4 changed files with 87 additions and 24 deletions
|
|
@ -154,6 +154,9 @@ export const Stitches = memo(({ stitches, pesData, currentStitchIndex, showProgr
|
||||||
const groups: StitchGroup[] = [];
|
const groups: StitchGroup[] = [];
|
||||||
let currentGroup: StitchGroup | null = null;
|
let currentGroup: StitchGroup | null = null;
|
||||||
|
|
||||||
|
let prevX = 0;
|
||||||
|
let prevY = 0;
|
||||||
|
|
||||||
for (let i = 0; i < stitches.length; i++) {
|
for (let i = 0; i < stitches.length; i++) {
|
||||||
const stitch = stitches[i];
|
const stitch = stitches[i];
|
||||||
const [x, y, cmd, colorIndex] = stitch;
|
const [x, y, cmd, colorIndex] = stitch;
|
||||||
|
|
@ -168,16 +171,30 @@ export const Stitches = memo(({ stitches, pesData, currentStitchIndex, showProgr
|
||||||
currentGroup.completed !== isCompleted ||
|
currentGroup.completed !== isCompleted ||
|
||||||
currentGroup.isJump !== isJump
|
currentGroup.isJump !== isJump
|
||||||
) {
|
) {
|
||||||
currentGroup = {
|
// For jump stitches, we need to create a line from previous position to current position
|
||||||
color,
|
// So we include both the previous point and current point
|
||||||
points: [x, y],
|
if (isJump && i > 0) {
|
||||||
completed: isCompleted,
|
currentGroup = {
|
||||||
isJump,
|
color,
|
||||||
};
|
points: [prevX, prevY, x, y],
|
||||||
|
completed: isCompleted,
|
||||||
|
isJump,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
currentGroup = {
|
||||||
|
color,
|
||||||
|
points: [x, y],
|
||||||
|
completed: isCompleted,
|
||||||
|
isJump,
|
||||||
|
};
|
||||||
|
}
|
||||||
groups.push(currentGroup);
|
groups.push(currentGroup);
|
||||||
} else {
|
} else {
|
||||||
currentGroup.points.push(x, y);
|
currentGroup.points.push(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevX = x;
|
||||||
|
prevY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
|
|
@ -189,12 +206,12 @@ export const Stitches = memo(({ stitches, pesData, currentStitchIndex, showProgr
|
||||||
<Line
|
<Line
|
||||||
key={i}
|
key={i}
|
||||||
points={group.points}
|
points={group.points}
|
||||||
stroke={group.isJump ? (group.completed ? '#cccccc' : '#e8e8e8') : group.color}
|
stroke={group.color}
|
||||||
strokeWidth={1.5}
|
strokeWidth={group.isJump ? 1.5 : 1.5}
|
||||||
lineCap="round"
|
lineCap="round"
|
||||||
lineJoin="round"
|
lineJoin="round"
|
||||||
dash={group.isJump ? [3, 3] : undefined}
|
dash={group.isJump ? [8, 4] : undefined}
|
||||||
opacity={group.isJump ? 1 : (showProgress && !group.completed ? 0.75 : 1.0)}
|
opacity={group.isJump ? (group.completed ? 0.8 : 0.5) : (showProgress && !group.completed ? 0.3 : 1.0)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
|
||||||
|
|
@ -159,14 +159,15 @@ export function renderStitches(
|
||||||
// Create Konva.Line for each group
|
// Create Konva.Line for each group
|
||||||
groups.forEach((group) => {
|
groups.forEach((group) => {
|
||||||
if (group.isJump) {
|
if (group.isJump) {
|
||||||
// Jump stitches - dashed gray lines
|
// Jump stitches - dashed lines in thread color
|
||||||
const line = new Konva.Line({
|
const line = new Konva.Line({
|
||||||
points: group.points,
|
points: group.points,
|
||||||
stroke: group.completed ? '#cccccc' : '#e8e8e8',
|
stroke: group.color,
|
||||||
strokeWidth: 1.5,
|
strokeWidth: 1.0,
|
||||||
lineCap: 'round',
|
lineCap: 'round',
|
||||||
lineJoin: 'round',
|
lineJoin: 'round',
|
||||||
dash: [3, 3],
|
dash: [5, 5],
|
||||||
|
opacity: group.completed ? 0.6 : 0.25,
|
||||||
});
|
});
|
||||||
stitchesGroup.add(line);
|
stitchesGroup.add(line);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,18 @@ export function parsePenData(data: Uint8Array): PenData {
|
||||||
const yFlags = data[offset + 2] & 0x07;
|
const yFlags = data[offset + 2] & 0x07;
|
||||||
|
|
||||||
// Decode coordinates (shift right by 3 to get actual position)
|
// Decode coordinates (shift right by 3 to get actual position)
|
||||||
// Using signed 16-bit interpretation
|
// The coordinates are stored as signed 16-bit values, left-shifted by 3
|
||||||
let x = (xRaw >> 3);
|
// We need to interpret them as signed before shifting
|
||||||
let y = (yRaw >> 3);
|
|
||||||
|
|
||||||
// Convert to signed if needed
|
// Convert from unsigned 16-bit to signed 16-bit
|
||||||
if (x > 0x7FF) x = x - 0x2000;
|
let xSigned = xRaw;
|
||||||
if (y > 0x7FF) y = y - 0x2000;
|
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 = {
|
const stitch: PenStitch = {
|
||||||
x,
|
x,
|
||||||
|
|
|
||||||
|
|
@ -210,15 +210,21 @@ def map_cmd(pystitch_cmd):
|
||||||
# Each stitch in pattern.stitches is [x, y, cmd]
|
# Each stitch in pattern.stitches is [x, y, cmd]
|
||||||
# We need to assign color indices based on COLOR_CHANGE commands
|
# We need to assign color indices based on COLOR_CHANGE commands
|
||||||
# and filter out COLOR_CHANGE and STOP commands (they're not actual stitches)
|
# 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 = []
|
stitches_with_colors = []
|
||||||
current_color = 0
|
current_color = 0
|
||||||
|
prev_color = 0
|
||||||
|
|
||||||
for i, stitch in enumerate(pattern.stitches):
|
for i, stitch in enumerate(pattern.stitches):
|
||||||
x, y, cmd = stitch
|
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:
|
if cmd == COLOR_CHANGE:
|
||||||
|
prev_color = current_color
|
||||||
current_color += 1
|
current_color += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -230,10 +236,44 @@ for i, stitch in enumerate(pattern.stitches):
|
||||||
if cmd == END:
|
if cmd == END:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Add actual stitch with color index and mapped command
|
# Determine which color this stitch belongs to
|
||||||
# Map PyStitch cmd values to our known JavaScript constant values
|
# 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)
|
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
|
# Convert to JSON-serializable format
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue