From 3abb01c6fcc42929f18e43af2affdd73f927cf7f Mon Sep 17 00:00:00 2001 From: maehw Date: Mon, 6 Jan 2025 19:23:49 +0100 Subject: [PATCH 1/5] Start working on support for PES v1 respectively PEC --- stitchcode/gui.js | 18 +++++++++- stitchcode/turtleShepherd.js | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/stitchcode/gui.js b/stitchcode/gui.js index 50854a558f..e55132f8e5 100644 --- a/stitchcode/gui.js +++ b/stitchcode/gui.js @@ -1473,9 +1473,17 @@ IDE_Morph.prototype.createStatusDisplay = function () { function () { myself.downloadDST(); }, 'Export as Tajima/DST' ); - downloadDSTButton.newLines = 2.7; + downloadDSTButton.newLines = 1.7; elements.push(downloadDSTButton); + var downloadPESButton = new PushButtonMorph( + null, + function () { myself.downloadPES(); }, + 'Export as Brother/PES' + ); + downloadPESButton.newLines = 1.7; + elements.push(downloadPESButton); + if (DEBUG) { elements.push(' DEBUG MODE: true'); element = new StringMorph(""); @@ -1593,6 +1601,14 @@ IDE_Morph.prototype.downloadDST = function() { saveAs(blob, name + '.dst'); }; +// PES export +IDE_Morph.prototype.downloadPES = function() { + var name = this.projectName ? this.projectName : 'turtlestitch'; + expUintArr = this.stage.turtleShepherd.toPES(name); + blob = new Blob([expUintArr], {type: 'application/octet-stream'}); + saveAs(blob, name + '.pes'); +}; + // PNG export IDE_Morph.prototype.downloadPNG = function() { var name = this.projectName ? this.projectName : 'turtlestitch'; diff --git a/stitchcode/turtleShepherd.js b/stitchcode/turtleShepherd.js index 470be63eae..8b25478609 100644 --- a/stitchcode/turtleShepherd.js +++ b/stitchcode/turtleShepherd.js @@ -843,6 +843,70 @@ TurtleShepherd.prototype.toDST = function(name="noname") { return expUintArr; }; +TurtleShepherd.prototype.toPES = function(name="noname") { + var expArr = []; + + function writeString(str, length=null, padWithSpace=true) { + if(length === null) { + length = str.length; + } + for(var i = 0; i> 0) & 0xFF); + expArr.push((value >> 8) & 0xFF); + } + + // identification and version + writeString("#PES0001"); + + // remaining PES v1 header section + expArr.push(0x16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + // from here on, it's all PEC + + // PEC header + writeString("LA:" + name.substr(0, 8), 19, true); // 20 bytes name, typically truncated at 8 chars, padded with spaces + expArr.push(0x0D); // carriage return + writeString("", 12, true); + expArr.push(0xFF, 0x00); + expArr.push(48/8); // icon width: 48; divided by 8 (pixels per byte) + expArr.push(38); // icon height: 38 + writeString("", 12, true); + // number of thread colors minus one, 0xFF means 0 colors; + // assume thread color and therefore write value 0 here + expArr.push(0); + // color index of that one thread color + expArr.push(1); + // fill the remaining space reserved for colors + writeString("", 462, true); + + // PEC block + expArr.push(0, 0, 0, 0, 0, 0x31, 0xFF, 0xF0); + width = 42; // FIXME + height = 42; // FIXME + writeInt16Le(width); + writeInt16Le(height); + writeInt16Le(0x1E0); + writeInt16Le(0x1B0); + // to be continued ... + + expUintArr = new Uint8Array(expArr.length); + for (i=0;i Date: Mon, 6 Jan 2025 21:19:17 +0100 Subject: [PATCH 2/5] Trying to get the numbers for PES/PEC right (stitch list still missing) --- stitchcode/turtleShepherd.js | 50 +++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/stitchcode/turtleShepherd.js b/stitchcode/turtleShepherd.js index 8b25478609..94449f69ad 100644 --- a/stitchcode/turtleShepherd.js +++ b/stitchcode/turtleShepherd.js @@ -868,19 +868,43 @@ TurtleShepherd.prototype.toPES = function(name="noname") { expArr.push((value >> 8) & 0xFF); } + function writeInt24Le(value) { + expArr.push((value >> 0) & 0xFF); + expArr.push((value >> 8) & 0xFF); + expArr.push((value >> 16) & 0xFF); + } + + function writeInt32Le(value) { + expArr.push((value >> 0) & 0xFF); + expArr.push((value >> 8) & 0xFF); + expArr.push((value >> 16) & 0xFF); + expArr.push((value >> 24) & 0xFF); + } + + function updateInt24Le(value, pos) { + expArr[pos] = (value >> 0) & 0xFF; + expArr[pos+1] = (value >> 8) & 0xFF; + expArr[pos+2] = (value >> 16) & 0xFF; + } + // identification and version writeString("#PES0001"); // remaining PES v1 header section - expArr.push(0x16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - // from here on, it's all PEC + writeInt32Le(0x16); // location of PEC block + writeInt16Le(0); + writeInt16Le(0); + writeInt16Le(0); + expArr.push(0xFF, 0xFF, 0, 0); // end of the header + // from here on, it's all PEC + // PEC header writeString("LA:" + name.substr(0, 8), 19, true); // 20 bytes name, typically truncated at 8 chars, padded with spaces expArr.push(0x0D); // carriage return writeString("", 12, true); expArr.push(0xFF, 0x00); - expArr.push(48/8); // icon width: 48; divided by 8 (pixels per byte) + expArr.push(6); // icon width: 48; divided by 8 (pixels per byte) expArr.push(38); // icon height: 38 writeString("", 12, true); // number of thread colors minus one, 0xFF means 0 colors; @@ -892,14 +916,26 @@ TurtleShepherd.prototype.toPES = function(name="noname") { writeString("", 462, true); // PEC block - expArr.push(0, 0, 0, 0, 0, 0x31, 0xFF, 0xF0); - width = 42; // FIXME - height = 42; // FIXME + stitchBlockStartPos = expArr.length; + expArr.push(0, 0); + stitchBlockLenPos = expArr.length; // recall byte position where we place the spaceholder + writeInt24Le(0); // spaceholder for stitch block length + expArr.push(0x31, 0xFF, 0xF0); + width = 1000; // FIXME (is this in multiple of 0.1mm?) + height = 1000; // FIXME (is this in multiple of 0.1mm?) writeInt16Le(width); writeInt16Le(height); writeInt16Le(0x1E0); writeInt16Le(0x1B0); - // to be continued ... + // pecEncode(); // TODO: implement + // calculate stitch block length value and update in the binary data + stitchBlockLen = expArr.length - stitchBlockStartPos; + updateInt24Le(stitchBlockLen, stitchBlockLenPos); + + // PEC thumbnail images for every color (use blank for now) + for (i=0;i<6*38*2;i++) { + expArr.push(0); + } expUintArr = new Uint8Array(expArr.length); for (i=0;i Date: Sat, 11 Jan 2025 21:37:27 +0100 Subject: [PATCH 3/5] PoC for PES files --- stitchcode/turtleShepherd.js | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/stitchcode/turtleShepherd.js b/stitchcode/turtleShepherd.js index 94449f69ad..4b65f09897 100644 --- a/stitchcode/turtleShepherd.js +++ b/stitchcode/turtleShepherd.js @@ -886,6 +886,28 @@ TurtleShepherd.prototype.toPES = function(name="noname") { expArr[pos+1] = (value >> 8) & 0xFF; expArr[pos+2] = (value >> 16) & 0xFF; } + + function pecEncode() { + // PES stitch list + // TODO: implement proper functionality + + /* + // FIXME: cannot access this.cache here! + for (var i=0; i < this.cache.length; i++) { + // TODO: step through this code! + if (this.cache[i].cmd == "color") { + } else if (this.cache[i].cmd == "pensize") { + } else if (this.cache[i].cmd == "move") { + } else { + } + } + */ + + // Mockery with dx and dy coordinates (in short form) + expArr.push(0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08); + + expArr.push(0xFF); // file end + } // identification and version writeString("#PES0001"); @@ -927,14 +949,19 @@ TurtleShepherd.prototype.toPES = function(name="noname") { writeInt16Le(height); writeInt16Le(0x1E0); writeInt16Le(0x1B0); - // pecEncode(); // TODO: implement + pecEncode(); // TODO: implement proper functionality // calculate stitch block length value and update in the binary data stitchBlockLen = expArr.length - stitchBlockStartPos; updateInt24Le(stitchBlockLen, stitchBlockLenPos); - // PEC thumbnail images for every color (use blank for now) + // PEC thumbnail images for every color (use chessboard alike pattern for now) + patternByte = 0xAA; for (i=0;i<6*38*2;i++) { - expArr.push(0); + if(i % 6 == 0) { + // swap pattern bytes + patternByte = (patternByte == 0xAA) ? 0x55 : 0xAA; + } + expArr.push(patternByte); } expUintArr = new Uint8Array(expArr.length); From ffe115f573ffa30b59ccb60bd6c9d54312881f3b Mon Sep 17 00:00:00 2001 From: maehw Date: Sun, 12 Jan 2025 13:47:58 +0100 Subject: [PATCH 4/5] Remove nested function pecEncode() from TurtleShepherd.prototype.toPES; work on PES/PEC PoC --- stitchcode/turtleShepherd.js | 64 +++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/stitchcode/turtleShepherd.js b/stitchcode/turtleShepherd.js index 4b65f09897..ba013d852a 100644 --- a/stitchcode/turtleShepherd.js +++ b/stitchcode/turtleShepherd.js @@ -5,6 +5,7 @@ Embroidery function for Javscript ------------------------------------------------------------------ Copyright (C) 2016-2021 Michael Aschauer + Copyright (C) 2025 maehw */ @@ -886,28 +887,6 @@ TurtleShepherd.prototype.toPES = function(name="noname") { expArr[pos+1] = (value >> 8) & 0xFF; expArr[pos+2] = (value >> 16) & 0xFF; } - - function pecEncode() { - // PES stitch list - // TODO: implement proper functionality - - /* - // FIXME: cannot access this.cache here! - for (var i=0; i < this.cache.length; i++) { - // TODO: step through this code! - if (this.cache[i].cmd == "color") { - } else if (this.cache[i].cmd == "pensize") { - } else if (this.cache[i].cmd == "move") { - } else { - } - } - */ - - // Mockery with dx and dy coordinates (in short form) - expArr.push(0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08); - - expArr.push(0xFF); // file end - } // identification and version writeString("#PES0001"); @@ -922,12 +901,15 @@ TurtleShepherd.prototype.toPES = function(name="noname") { // from here on, it's all PEC // PEC header + const iconWidth = 48; + const iconWidthBytes = iconWidth/8; // icon width in bytes: icon width in pixels divided by 8 pixels per byte + const iconHeight = 38; // icon height (in pixels) writeString("LA:" + name.substr(0, 8), 19, true); // 20 bytes name, typically truncated at 8 chars, padded with spaces expArr.push(0x0D); // carriage return writeString("", 12, true); expArr.push(0xFF, 0x00); - expArr.push(6); // icon width: 48; divided by 8 (pixels per byte) - expArr.push(38); // icon height: 38 + expArr.push(iconWidthBytes); + expArr.push(iconHeight); writeString("", 12, true); // number of thread colors minus one, 0xFF means 0 colors; // assume thread color and therefore write value 0 here @@ -949,14 +931,44 @@ TurtleShepherd.prototype.toPES = function(name="noname") { writeInt16Le(height); writeInt16Le(0x1E0); writeInt16Le(0x1B0); - pecEncode(); // TODO: implement proper functionality + + // Perform actual encoding of PEC stitch list subsection + // TODO: implement proper functionality + for (var i=0; i < this.cache.length; i++) { + // TODO: step through this code! + if (this.cache[i].cmd == "color") { + } else if (this.cache[i].cmd == "pensize") { + } else if (this.cache[i].cmd == "move") { + } else { + } + } + // Mockery with dx and dy coordinates (in short form) + // delta of 0x0A represents 10 units of 0.1 mm each, i.e. 1 mm + // delta of 0x76 represents -10 units (MSBit 0; then 7 bit two's complement) of 0.1 mm each, i.e. 1 mm + for (var i=0; i<20; i++) { + expArr.push(0x0A, 0x0A); // go diagonally + } + for (var i=0; i<20; i++) { + expArr.push(0x0A, 0x00); // go x only + } + for (var i=0; i<20; i++) { + expArr.push(0x00, 0x0A); // go y only + } + for (var i=0; i<20; i++) { + expArr.push(0x76, 0x00); // go -x only + } + for (var i=0; i<20; i++) { + expArr.push(0x00, 0x76); // go -y only + } + expArr.push(0xFF); // file end + // calculate stitch block length value and update in the binary data stitchBlockLen = expArr.length - stitchBlockStartPos; updateInt24Le(stitchBlockLen, stitchBlockLenPos); // PEC thumbnail images for every color (use chessboard alike pattern for now) patternByte = 0xAA; - for (i=0;i<6*38*2;i++) { + for (i=0;i Date: Mon, 13 Jan 2025 14:04:11 +0100 Subject: [PATCH 5/5] Work on PES/PEC stitch encoding --- stitchcode/turtleShepherd.js | 53 ++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/stitchcode/turtleShepherd.js b/stitchcode/turtleShepherd.js index ba013d852a..79cf7c9b2a 100644 --- a/stitchcode/turtleShepherd.js +++ b/stitchcode/turtleShepherd.js @@ -933,18 +933,64 @@ TurtleShepherd.prototype.toPES = function(name="noname") { writeInt16Le(0x1B0); // Perform actual encoding of PEC stitch list subsection + lastStitch = null; + hasFirst = false; + scale = this.scale; // TODO: implement proper functionality for (var i=0; i < this.cache.length; i++) { - // TODO: step through this code! if (this.cache[i].cmd == "color") { - } else if (this.cache[i].cmd == "pensize") { } else if (this.cache[i].cmd == "move") { - } else { + stitch = this.cache[i]; + if (!hasFirst) { + x0 = Math.round(stitch.x * scale); + y0 = -Math.round(stitch.y * scale); + + dx = x0; + dy = y0; + lastStitch = {cmd: "move", x: x0, y: y0, penDown: stitch.penDown} + hasFirst = true; + } else if (hasFirst) { + x1 = Math.round(stitch.x * scale); + y1 = -Math.round(stitch.y * scale); + x0 = Math.round(lastStitch.x * scale); + y0 = -Math.round(lastStitch.y * scale); + + dx = x1 - x0 + dy = y1 - y0 + } + + // check whether to encode in short or long form; + // jump and trim stitches are probably not handled correctly at the moment (FIXME!) + dmax = Math.max(Math.abs(dx), Math.abs(dy)); + + if(stitch.penDown && dmax < 127) { + // short form (1 byte) indicated by MSBit=0; 7 bit for delta + expArr.push(dx & 0x7F, dy & 0x7F); + + } else if(dmax < 2047) { + // long form (2 bytes) indicated by MSBit=1; 12 bit for delta + let msBits = 0x80; + //if(!stitch.penDown) { + // msBits |= 0x10; // make it a jump stitch (FIXME: doesn't work as expected) + //} + expArr.push(msBits | ((dx >> 8) & 0x0F)); + expArr.push(dx & 0xFF); + expArr.push(msBits | ((dy >> 8) & 0x0F)); + expArr.push(dy & 0xFF); + } else { + + // TODO: cannot encode; break this down into multiple stitches?! + // are there other ways of communicating errors to the user? + console.log("PES/PEC export: cannot encode stitch!"); + } + + lastStitch = stitch; } } // Mockery with dx and dy coordinates (in short form) // delta of 0x0A represents 10 units of 0.1 mm each, i.e. 1 mm // delta of 0x76 represents -10 units (MSBit 0; then 7 bit two's complement) of 0.1 mm each, i.e. 1 mm + /* for (var i=0; i<20; i++) { expArr.push(0x0A, 0x0A); // go diagonally } @@ -960,6 +1006,7 @@ TurtleShepherd.prototype.toPES = function(name="noname") { for (var i=0; i<20; i++) { expArr.push(0x00, 0x76); // go -y only } + */ expArr.push(0xFF); // file end // calculate stitch block length value and update in the binary data