Compare commits

..

No commits in common. "main" and "v0.12.0" have entirely different histories.

4 changed files with 49 additions and 43 deletions

27
package-lock.json generated
View file

@ -15070,6 +15070,16 @@
"murmur-32": "^0.1.0 || ^0.2.0" "murmur-32": "^0.1.0 || ^0.2.0"
} }
}, },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -16069,6 +16079,16 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
@ -17598,15 +17618,16 @@
} }
}, },
"node_modules/terser-webpack-plugin": { "node_modules/terser-webpack-plugin": {
"version": "5.4.0", "version": "5.3.16",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz",
"integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/trace-mapping": "^0.3.25", "@jridgewell/trace-mapping": "^0.3.25",
"jest-worker": "^27.4.5", "jest-worker": "^27.4.5",
"schema-utils": "^4.3.0", "schema-utils": "^4.3.0",
"serialize-javascript": "^6.0.2",
"terser": "^5.31.1" "terser": "^5.31.1"
}, },
"engines": { "engines": {

View file

@ -71,12 +71,12 @@ export function ProgressMonitor() {
const isMaskTraceComplete = const isMaskTraceComplete =
machineStatus === MachineStatus.MASK_TRACE_COMPLETE; machineStatus === MachineStatus.MASK_TRACE_COMPLETE;
// Use our own PEN stitch count as the source of truth for total stitches. // Use PEN stitch count as fallback when machine reports 0 total stitches
// The machine's patternInfo.totalStitches uses a different counting method than const totalStitches = patternInfo
// currentStitch (e.g. excludes lock stitches), so it can't be used as the max. ? patternInfo.totalStitches === 0 && displayPattern?.penStitches
const totalStitches = displayPattern?.penStitches ? displayPattern.penStitches.stitches.length
? displayPattern.penStitches.stitches.length : patternInfo.totalStitches
: (patternInfo?.totalStitches ?? 0); : 0;
// Use adjustedStitchIndex (from step control) when available, otherwise machine-reported // Use adjustedStitchIndex (from step control) when available, otherwise machine-reported
const currentStitch = const currentStitch =
@ -156,10 +156,8 @@ export function ProgressMonitor() {
totalStitches={totalStitches} totalStitches={totalStitches}
lastRolledBackError={lastRolledBackError} lastRolledBackError={lastRolledBackError}
colorBlocks={colorBlocks} colorBlocks={colorBlocks}
onAdjustPosition={(offset) => onAdjustPosition={adjustStitchPosition}
adjustStitchPosition(offset, totalStitches) onSetPosition={setStitchPosition}
}
onSetPosition={(index) => setStitchPosition(index, totalStitches)}
/> />
)} )}

View file

@ -65,8 +65,8 @@ interface MachineState {
startSewing: () => Promise<void>; startSewing: () => Promise<void>;
resumeSewing: () => Promise<void>; resumeSewing: () => Promise<void>;
deletePattern: () => Promise<void>; deletePattern: () => Promise<void>;
setStitchPosition: (index: number, maxStitches?: number) => Promise<void>; setStitchPosition: (index: number) => Promise<void>;
adjustStitchPosition: (offset: number, maxStitches?: number) => Promise<void>; adjustStitchPosition: (offset: number) => Promise<void>;
// Initialization // Initialization
initialize: () => void; initialize: () => void;
@ -338,11 +338,11 @@ export const useMachineStore = create<MachineState>((set, get) => ({
}, },
// Set stitch position to an absolute index // Set stitch position to an absolute index
setStitchPosition: async (index: number, maxStitches?: number) => { setStitchPosition: async (index: number) => {
const { isConnected, service, patternInfo } = get(); const { isConnected, service, patternInfo } = get();
if (!isConnected) return; if (!isConnected) return;
const totalStitches = maxStitches ?? patternInfo?.totalStitches ?? 0; const totalStitches = patternInfo?.totalStitches || 0;
const clamped = Math.max(0, Math.min(index, totalStitches)); const clamped = Math.max(0, Math.min(index, totalStitches));
try { try {
@ -359,11 +359,11 @@ export const useMachineStore = create<MachineState>((set, get) => ({
}, },
// Adjust stitch position by a relative offset // Adjust stitch position by a relative offset
adjustStitchPosition: async (offset: number, maxStitches?: number) => { adjustStitchPosition: async (offset: number) => {
const { sewingProgress, adjustedStitchIndex } = get(); const { sewingProgress, adjustedStitchIndex } = get();
const currentIndex = const currentIndex =
adjustedStitchIndex ?? sewingProgress?.currentStitch ?? 0; adjustedStitchIndex ?? sewingProgress?.currentStitch ?? 0;
await get().setStitchPosition(currentIndex + offset, maxStitches); await get().setStitchPosition(currentIndex + offset);
}, },
// Handle automatic stitch rollback for thread errors // Handle automatic stitch rollback for thread errors

View file

@ -57,12 +57,6 @@ export interface ThreadColorInfo {
// Type-safe Brother color data // Type-safe Brother color data
const brotherColors = brotherColorData as BrotherColor[]; const brotherColors = brotherColorData as BrotherColor[];
// Maximum RGB Euclidean distance to accept a catalog number match.
// PES catalog numbers from pystitch use a different numbering scheme than our
// Brother color database, so a catalog hit can resolve to the wrong color.
// A match is only trusted if the database entry's RGB is within this distance.
const CATALOG_COLOR_MATCH_THRESHOLD = 50;
/** /**
* Convert RGB values to hex color string * Convert RGB values to hex color string
*/ */
@ -272,32 +266,25 @@ export function enhanceThreadWithBrotherColor(
): ThreadColorInfo { ): ThreadColorInfo {
const { matchByRGB = true } = options; const { matchByRGB = true } = options;
// First, try to match by catalog number
if (thread.catalogNumber) {
const brotherInfo = mapThreadCode(thread.catalogNumber);
if (brotherInfo) {
// Found a Brother color match by catalog number - use Brother data
return brotherInfo;
}
}
// Second, try exact RGB matching if enabled (replicates BrotherColor.FromColor logic)
const cleanHex = thread.hex.replace("#", ""); const cleanHex = thread.hex.replace("#", "");
const r = parseInt(cleanHex.slice(0, 2), 16); const r = parseInt(cleanHex.slice(0, 2), 16);
const g = parseInt(cleanHex.slice(2, 4), 16); const g = parseInt(cleanHex.slice(2, 4), 16);
const b = parseInt(cleanHex.slice(4, 6), 16); const b = parseInt(cleanHex.slice(4, 6), 16);
// First, try to match by catalog number — but only accept the match if the
// catalog entry's RGB is close to the thread's actual color. PES catalog
// numbers from pystitch use a different numbering scheme than our Brother
// color database, so a catalog hit can be the wrong color entirely.
if (thread.catalogNumber) {
const brotherInfo = mapThreadCode(thread.catalogNumber);
if (brotherInfo?.rgb) {
const dr = brotherInfo.rgb.r - r;
const dg = brotherInfo.rgb.g - g;
const db = brotherInfo.rgb.b - b;
const distance = Math.sqrt(dr * dr + dg * dg + db * db);
if (distance <= CATALOG_COLOR_MATCH_THRESHOLD) {
return brotherInfo;
}
}
}
// Try exact RGB matching (replicates BrotherColor.FromColor logic)
if (matchByRGB) { if (matchByRGB) {
const brotherColor = findBrotherColorByRGB(r, g, b); const brotherColor = findBrotherColorByRGB(r, g, b);
if (brotherColor) { if (brotherColor) {
// Found exact RGB match - use Brother data
return brotherColorToThreadInfo(brotherColor); return brotherColorToThreadInfo(brotherColor);
} }
} }