├── .gitmodules ├── CellMachine.js ├── LICENSE ├── README.md ├── VectorTiles.js ├── VectorTilesLib.js ├── cross-test.k ├── evalJS.js ├── extract-vector-tiles.py ├── face-notes.svg ├── faces.png ├── faces.svg ├── index.html ├── knitout-to-mesh.js ├── parseKnitout.js ├── show-knitout.js ├── simplified-visualizer.html ├── tiles-test.k ├── vector-tiles.svg ├── visualizer.html └── write-test.js /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ace-builds"] 2 | path = ace-builds 3 | url = https://github.com/ajaxorg/ace-builds 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Knitout (Live) Visualizer 2 | 3 | Visualizer for knitout files, javascript files that print knitout with `console.log`, and javascript files that output knitout using knitout-frontend-js. Includes code editing support so you can edit-and-test within the interface. 4 | 5 | You can use the visualizer online by visiting the following site: https://textiles-lab.github.io/knitout-live-visualizer/ 6 | 7 | 8 | 9 |
InstallationUsageTroubleshooting
10 | 11 | ## Local Installation 12 | 13 | In the command line, type: 14 | ```console 15 | git clone --recursive https://github.com/textiles-lab/knitout-live-visualizer 16 | ``` 17 | 18 | See the github documentation on [cloning a repository](https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/cloning-a-repository) if you need assistance with installation. 19 | 20 | ## Usage 21 | 22 | Open the [online version](https://textiles-lab.github.io/knitout-live-visualizer/), or -- if installed locally -- the 'knitout-live-visualizer/visualizer.html' file. 23 | 24 | Click the 'Load a knitout or js file' button to open a local file. 25 | 26 | Once a file is loaded, you can add/delete lines of code (with live error checking/support), and update the visualizer by pressing the 'Run/Show' button. The 'Reload' button will discard those edits and restore the file to its original state. (*note: editing the file in the visualizer will not make alterations to the local file.*) 27 | 28 | If using a javascript file, you can check the 'Show Knitout' box for a preview of the output knitout, or press 'Save Knitout' to download the file to your computer. 29 | 30 | Highlighting a line of source code highlights the stitches made by that line. 31 | Clicking a stitch should take you to the line of code that made that stitch. 32 | 33 | `visualizer.html` is the does-everything visualizer. 34 | 35 | `simplified-visualizer.html` only handles knitout files and doesn't have a code editor. 36 | 37 | ## Troubleshooting 38 | If you have any trouble, discover a bug, or want to provide feedback, feel free to use the [Issues](https://github.com/textiles-lab/knitout-live-visualizer/issues) page.\ 39 | -------------------------------------------------------------------------------- /VectorTiles.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const VectorTiles = {}; 4 | 5 | VectorTiles.LoopWidth = 13; 6 | VectorTiles.YarnWidth = 7; 7 | VectorTiles.TileHeight = 9; 8 | 9 | //Check that VectorTilesLib is included before this: 10 | let libSize = 0; 11 | for (const tileName in VectorTilesLib) { 12 | const tile = VectorTilesLib[tileName]; 13 | for (const label in tile) { 14 | const lines = tile[label]; 15 | for (const line of lines) { 16 | console.assert(line.length % 2 == 0, "Line has pairs of coordinates."); 17 | console.assert(line.length >= 4, "Line has at least two points."); 18 | } 19 | } 20 | ++libSize; 21 | } 22 | console.log("VectorTilesLib contains " + libSize + " tiles."); 23 | 24 | //tile lines will be stored into a larger grid to accelerate clipping: 25 | const GridSize = 200; 26 | 27 | function Drawing() { 28 | this.back = {}; //{ style:{style: , lines:[ [x0, y0, x1, y1, ... ], ... ]}, ... } 29 | this.backSliders = {}; 30 | this.middle = {}; 31 | this.frontSliders = {}; 32 | this.front = {}; 33 | } 34 | 35 | let defaultStyle = {color:'#f0f'}; 36 | let freshKey = 0; 37 | 38 | Drawing.prototype.addLine = function Drawing_addLine(layer, styles, label, pts, ofs, z) { 39 | if (typeof(ofs) === 'undefined') ofs = {x:0.0, y:0.0}; 40 | if (typeof(z) === 'undefined') z = 0; 41 | 42 | //add ofs to points: 43 | pts = pts.slice(); 44 | for (let i = 0; i < pts.length; i += 2) { 45 | pts[i] += ofs.x; 46 | pts[i+1] += ofs.y; 47 | } 48 | 49 | /* 50 | //NOTE: bounds logic doesn't work with the crossing-lines hack (which uses a [x,y,fs, x,y,bs] format) 51 | //compute bounds of points: 52 | let min = {x:Infinity, y:Infinity}; 53 | let max = {x:-Infinity, y:-Infinity}; 54 | for (let i = 0; i < pts.length; i += 2) { 55 | min.x = Math.min(min.x, pts[i]); 56 | min.y = Math.min(min.y, pts[i+1]); 57 | max.x = Math.max(max.x, pts[i]); 58 | max.y = Math.max(max.y, pts[i+1]); 59 | } 60 | 61 | if (min.x > max.x) return; //empty! 62 | */ 63 | 64 | let style = styles[label]; 65 | if (!style) style = defaultStyle; 66 | if (!('key' in style)) { 67 | style.key = (freshKey++).toString(); 68 | } 69 | if (!(style.key in layer)) { 70 | layer[style.key] = {style:style, grid:[]}; 71 | } 72 | 73 | //update style layer to include bounds: 74 | let ls = layer[style.key]; 75 | let gridKey = (Math.floor(pts[0] / GridSize)) + " " + (Math.floor(pts[1] / GridSize)); 76 | 77 | if (!(gridKey in ls.grid)) { 78 | ls.grid[gridKey] = []; 79 | } 80 | 81 | let g = ls.grid[gridKey]; 82 | while (z >= g.length) { 83 | g.push([]); 84 | } 85 | g[z].push(pts); 86 | }; 87 | 88 | VectorTiles.makeDrawing = function Vectortiles_makeDrawing() { 89 | return new Drawing(); 90 | }; 91 | 92 | 93 | /*let STYLES = { 94 | '1':'#f00', 95 | '2':'#0f0', 96 | '3':'#800', 97 | '4':'#080', 98 | '5':'#afe9af', 99 | }; 100 | function yarnStyle(y, colors) { 101 | if (y in colors) { 102 | return colors[y]; 103 | } 104 | if (!(y in STYLES)) { 105 | STYLES[y] = '#f0f'; 106 | } 107 | return STYLES[y]; 108 | };*/ 109 | 110 | VectorTiles.draw = function VectorTiles_draw(ctx, drawing, options) { 111 | let frontOfs = options.frontOfs || {x:0.0, y:0.0}; 112 | let backOfs = options.backOfs || {x:0.0, y:0.0}; 113 | let frontSlidersOfs = options.frontSlidersOfs || {x:0.75 * frontOfs.x + 0.25 * backOfs.x, y: 0.75 * frontOfs.y + 0.25 * backOfs.y}; 114 | let backSlidersOfs = options.backSlidersOfs || {x:0.25 * frontOfs.x + 0.75 * backOfs.x, y: 0.25 * frontOfs.y + 0.75 * backOfs.y}; 115 | let frontTintRGBA = options.frontTintRGBA || [1.0, 1.0, 1.0, 0.0]; 116 | let middleTintRGBA = options.middleTintRGBA || [1.0, 1.0, 1.0, 0.0]; 117 | let backTintRGBA = options.backTintRGBA || [1.0, 1.0, 1.0, 0.0]; 118 | 119 | let viewMin = {x:Infinity, y:Infinity}; 120 | let viewMax = {x:-Infinity, y:-Infinity}; 121 | { //read view from context: 122 | let xf = ctx.getTransform(); 123 | xf.invertSelf(); 124 | 125 | function doPt(x,y) { 126 | let tx = xf.a * x + xf.c * y + xf.e; 127 | let ty = xf.b * x + xf.d * y + xf.f; 128 | viewMin.x = Math.min(viewMin.x, tx); 129 | viewMin.y = Math.min(viewMin.y, ty); 130 | viewMax.x = Math.max(viewMax.x, tx); 131 | viewMax.y = Math.max(viewMax.y, ty); 132 | } 133 | doPt(0,0); 134 | doPt(0,ctx.canvas.height); 135 | doPt(ctx.canvas.width,0); 136 | doPt(ctx.canvas.width,ctx.canvas.height); 137 | 138 | if (viewMin.x > viewMax.x) { 139 | //empty view 140 | viewMin.x = viewMax.x = viewMin.y = viewMax.y = 0; 141 | } 142 | } 143 | 144 | ctx.save(); 145 | ctx.lineCap = "round"; 146 | ctx.lineJoin = "round"; 147 | ctx.strokeWidth = 1.0; 148 | 149 | let gridKeys = []; 150 | //NOTE: could extend viewMin / viewMax to avoid lines that start outside the view being clipped. 151 | for (let gx = Math.floor(viewMin.x / GridSize); gx <= Math.ceil(viewMax.x / GridSize); ++gx) { 152 | for (let gy = Math.floor(viewMin.y / GridSize); gy <= Math.ceil(viewMax.y / GridSize); ++gy) { 153 | gridKeys.push(gx + " " + gy); 154 | /* 155 | //DEBUG: show grid 156 | ctx.fillStyle = '#f0f'; 157 | ctx.fillRect(gx * GridSize + 2, gy * GridSize + 2, GridSize - 4, GridSize - 4); 158 | */ 159 | } 160 | } 161 | 162 | 163 | 164 | function tint(color, tintRGBA) { 165 | let colorRGB; 166 | 167 | console.assert( 168 | /^#[0-9A-Fa-f]{3}$/.test(color) 169 | || /^#[0-9A-Fa-f]{6}$/.test(color), "tint only works with hex 6/3-tuples"); 170 | if (color.length === 7) { 171 | colorRGB = [ 172 | parseInt(color.substr(1,2), 16) / 255.0, 173 | parseInt(color.substr(3,2), 16) / 255.0, 174 | parseInt(color.substr(5,2), 16) / 255.0 175 | ]; 176 | } else if (color.length === 4) { 177 | colorRGB = [ 178 | parseInt(color.substr(1,1) + color.substr(1,1), 16) / 255.0, 179 | parseInt(color.substr(2,1) + color.substr(2,1), 16) / 255.0, 180 | parseInt(color.substr(3,1) + color.substr(3,1), 16) / 255.0 181 | ]; 182 | } 183 | colorRGB[0] = (tintRGBA[0] - colorRGB[0]) * tintRGBA[3] + colorRGB[0]; 184 | colorRGB[1] = (tintRGBA[1] - colorRGB[1]) * tintRGBA[3] + colorRGB[1]; 185 | colorRGB[2] = (tintRGBA[2] - colorRGB[2]) * tintRGBA[3] + colorRGB[2]; 186 | 187 | function h2(v) { 188 | let r = Math.max(0, Math.min(255, Math.round(255 * v))).toString(16); 189 | if (r.length < 2) r = '0' + r; 190 | return r; 191 | } 192 | 193 | return '#' + h2(colorRGB[0]) + h2(colorRGB[1]) + h2(colorRGB[2]); 194 | 195 | } 196 | 197 | function drawGroups(layer, layerTintRGBA) { 198 | let again = true; 199 | let z = 0; 200 | while (again) { 201 | again = false; 202 | for (let sk in layer) { 203 | let ls = layer[sk]; 204 | ctx.strokeStyle = tint(ls.style.color, layerTintRGBA); 205 | ctx.beginPath(); 206 | gridKeys.forEach(function(gk){ 207 | if (!(gk in ls.grid)) return; 208 | if (z >= ls.grid[gk].length) return; 209 | again = true; 210 | ls.grid[gk][z].forEach(function(line){ 211 | ctx.moveTo(line[0], line[1]); 212 | for (let i = 2; i < line.length; i += 2) { 213 | ctx.lineTo(line[i], line[i+1]); 214 | } 215 | }); 216 | }); 217 | ctx.stroke(); 218 | } 219 | z += 1; 220 | } 221 | } 222 | 223 | ctx.translate(backOfs.x, backOfs.y); 224 | drawGroups(drawing.back, backTintRGBA); 225 | ctx.translate(-backOfs.x, -backOfs.y); 226 | 227 | ctx.translate(backSlidersOfs.x, backSlidersOfs.y); 228 | drawGroups(drawing.backSliders, backTintRGBA); 229 | ctx.translate(-backSlidersOfs.x, -backSlidersOfs.y); 230 | 231 | for (let sk in drawing.middle) { 232 | let ls = drawing.middle[sk]; 233 | ctx.strokeStyle = tint(ls.style.color, middleTintRGBA); 234 | ctx.beginPath(); 235 | gridKeys.forEach(function(gk){ 236 | if (!(gk in ls.grid)) return; 237 | ls.grid[gk].forEach(function(lines,z) { 238 | lines.forEach(function(line){ 239 | console.assert(line.length === 6, "bridges should be [fx,fy,fs,bx,by,bs]"); 240 | ctx.moveTo(line[0] + (line[2] ? frontSlidersOfs.x : frontOfs.x), line[1] + (line[2] ? frontSlidersOfs.y : frontOfs.y)); 241 | ctx.lineTo(line[3] + (line[5] ? backSlidersOfs.x : backOfs.x), line[4] + (line[5] ? backSlidersOfs.y : backOfs.y)); 242 | }); 243 | }); 244 | }); 245 | ctx.stroke(); 246 | } 247 | 248 | ctx.translate(frontSlidersOfs.x, frontSlidersOfs.y); 249 | drawGroups(drawing.frontSliders, frontTintRGBA); 250 | ctx.translate(-frontSlidersOfs.x, -frontSlidersOfs.y); 251 | 252 | ctx.translate(frontOfs.x, frontOfs.y); 253 | drawGroups(drawing.front, frontTintRGBA); 254 | ctx.translate(-frontOfs.x, -frontOfs.y); 255 | 256 | ctx.restore(); 257 | }; 258 | 259 | //helpers: 260 | 261 | //index and bed y to lower left corner position: 262 | function tileLowerLeft(i, y) { 263 | return { 264 | x: Math.floor(i / 2) * (VectorTiles.LoopWidth + VectorTiles.YarnWidth) + (i % 2 === 0 ? 0 : VectorTiles.LoopWidth) - 0.5 * VectorTiles.LoopWidth, 265 | y: y * VectorTiles.TileHeight 266 | }; 267 | } 268 | 269 | VectorTiles.tileLowerLeft = tileLowerLeft; 270 | 271 | // 1 1 1 272 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 273 | // 8 . . . . a b . b a . . . . 274 | // 7 . A A A A A A A A A A A . 275 | // 6 . A B B B B B B B B B A . 276 | // 5 . A B . a b . b a . B A . 277 | // 4 a a a a a b . b a a a a a 278 | // 3 b b b b b b . b b b b b b 279 | // 2 . A B . . . . . . . B A . 280 | // 1 . A A A A . . . A A A A . 281 | // 0 . . B B A B . B A B B . . 282 | 283 | VectorTiles.addCross = function VectorTiles_addCross(drawing, cross) { 284 | console.assert(typeof(cross.type) === 'string', "Expecting cross to have 'type' string"); 285 | console.assert(typeof(cross.styles) === 'object', "Expecting cross to have 'styles' reference"); 286 | console.assert(typeof(cross.i) === 'number' && typeof(cross.i2) === 'number', "Expecting cross to have i, i2 indices"); 287 | console.assert(typeof(cross.b) === 'string' && typeof(cross.b2) === 'string', "Expecting cross to have b, b2 strings"); 288 | console.assert(Array.isArray(cross.yarns), "Expecting cross to have 'yarns' array."); 289 | 290 | if (cross.type === 'y') { 291 | console.assert(typeof(cross.port) === 'string' && typeof(cross.port2) === 'string', "Expecting cross of yarn type to have port, port2 strings"); 292 | 293 | let ports = { 294 | 'o-':{x:0.5, y0:6.5, y1:5.5}, 295 | 'x-':{x:0.5, y0:6.5, y1:5.5}, 296 | 'O-':{x:3.5, y0:6.5, y1:5.5}, 297 | 'X-':{x:3.5, y0:6.5, y1:5.5}, 298 | 'O+':{x:3.5, y0:6.5, y1:5.5}, 299 | 'X+':{x:3.5, y0:6.5, y1:5.5}, 300 | 'o+':{x:6.5, y0:6.5, y1:5.5}, 301 | 'x+':{x:6.5, y0:6.5, y1:5.5} 302 | }; 303 | 304 | console.assert(cross.port in ports && cross.port2 in ports, "Expecting cross of yarn type to have known port, port2 strings"); 305 | 306 | let pf, pb; 307 | 308 | let front, back; 309 | let fs, bs; 310 | if (cross.b[0] === 'f') { 311 | fs = (cross.b === 'fs'); 312 | bs = (cross.b2 === 'bs'); 313 | front = tileLowerLeft(cross.i, cross.y); 314 | pf = ports[cross.port]; 315 | back = tileLowerLeft(cross.i2, cross.y); 316 | pb = ports[cross.port2]; 317 | } else { console.assert(cross.b[0] === 'b', "yarn crosses should be f* <-> b*"); 318 | bs = (cross.b === 'bs'); 319 | fs = (cross.b2 === 'fs'); 320 | front = tileLowerLeft(cross.i2, cross.y); 321 | pf = ports[cross.port2]; 322 | back = tileLowerLeft(cross.i, cross.y); 323 | pb = ports[cross.port]; 324 | } 325 | 326 | if (cross.yarns.length >= 1) { 327 | let l0 = cross.yarns[cross.yarns.length-1]; //frontmost yarn 328 | drawing.addLine(drawing.middle, cross.styles, l0, [front.x+pf.x, front.y+pf.y0, fs, back.x+pb.x, back.y+pb.y0, bs]); 329 | } 330 | if (cross.yarns.length >= 2) { 331 | let l1 = cross.yarns.slice(0,cross.yarns.length-1).join(' '); //rearmost yarns(s) 332 | drawing.addLine(drawing.middle, cross.styles, l1, [front.x+pf.x, front.y+pf.y1, fs, back.x+pb.x, back.y+pb.y1, bs]); 333 | } 334 | return; 335 | } 336 | 337 | let l,r; 338 | let y1 = 5.5; 339 | let y2 = 6.5; 340 | if (cross.type === 'x') { 341 | l = 4.5; 342 | r = 8.5; 343 | } else if (cross.type === 's') { 344 | l = 1.5; 345 | r = 11.5; 346 | } else { 347 | console.assert(false, "Unknown cross type."); 348 | } 349 | 350 | let front, back; 351 | let fs, bs; 352 | if (cross.b[0] === 'f') { 353 | fs = (cross.b === 'fs'); 354 | bs = (cross.b2 === 'bs'); 355 | front = tileLowerLeft(cross.i, cross.y); 356 | front.y += y1; 357 | back = tileLowerLeft(cross.i2, cross.y); 358 | back.y += y2; 359 | } else { console.assert(cross.b[0] === 'b', "loop crosses should be f* <-> b*"); 360 | bs = (cross.b === 'bs'); 361 | fs = (cross.b2 === 'fs'); 362 | front = tileLowerLeft(cross.i2, cross.y); 363 | front.y += y2; 364 | back = tileLowerLeft(cross.i, cross.y); 365 | back.y += y1; 366 | } 367 | 368 | if (cross.yarns.length >= 1) { 369 | let l0 = cross.yarns[cross.yarns.length-1]; //frontmost yarn 370 | drawing.addLine(drawing.middle, cross.styles, l0, [front.x+l, front.y, fs, back.x+l, back.y, bs ]); 371 | drawing.addLine(drawing.middle, cross.styles, l0, [front.x+r, front.y, fs, back.x+r, back.y, bs ]); 372 | } 373 | if (cross.yarns.length >= 2) { 374 | let l1 = cross.yarns.slice(0,cross.yarns.length-1).join(' '); //rearmost yarns(s) 375 | drawing.addLine(drawing.middle, cross.styles, l1, [front.x+l+1.0, front.y, fs, back.x+l+1.0, back.y, bs ]); 376 | drawing.addLine(drawing.middle, cross.styles, l1, [front.x+r-1.0, front.y, fs, back.x+r-1.0, back.y, bs ]); 377 | } 378 | }; 379 | 380 | VectorTiles.addLoopTile = function VectorTiles_addLoopTile(drawing, styles, tile) { 381 | console.assert(typeof(tile.y) === 'number', "Expecting bed height in loop tile"); 382 | console.assert(typeof(tile.i) === 'number', "Expecting index in loop tile"); 383 | console.assert(typeof(tile.type) === 'string', "Expecting type string in loop tile"); 384 | console.assert(typeof(tile.bed) === 'string', "Expecting bed string in loop tile"); 385 | console.assert(Array.isArray(tile.loops), "Expecting loops array in loop tile"); 386 | console.assert(Array.isArray(tile.yarns), "Expecting yarns array in loop tile"); 387 | console.assert(Array.isArray(tile.across), "Expecting across array in loop tile"); 388 | //loops, yarns, etc are listed in *back-to-front* order! 389 | 390 | const type = tile.type; 391 | const bed = tile.bed; 392 | let loops = tile.loops; //gets moved aside in xfer source hack case 393 | const yarns = tile.yarns; 394 | const across = tile.across; 395 | if (loops.length === 0 && yarns.length === 0 && across.length === 0) return null; 396 | 397 | let layer = { 398 | 'b': drawing.back, 399 | 'bs': drawing.backSliders, 400 | 'fs': drawing.frontSliders, 401 | 'f': drawing.front 402 | }[bed]; 403 | let ll = tileLowerLeft(tile.i, tile.y); 404 | 405 | function doLib(tileName) { 406 | if (loops.length >= 2) tileName += "-l2"; 407 | else if (loops.length >= 1) tileName += "-l1"; 408 | 409 | if (yarns.length >= 2) tileName += "-y2"; 410 | else if (yarns.length >= 1) tileName += "-y1"; 411 | 412 | if (across.length >= 2) tileName += "-a2"; 413 | else if (across.length >= 1) tileName += "-a1"; 414 | 415 | console.assert(tileName in VectorTilesLib, "Tile '" + tileName + "' is in the library."); 416 | 417 | const tile = VectorTilesLib[tileName]; 418 | 419 | function yarnLines(z) { 420 | if (yarns.length >= 2 && "y2" in tile) { 421 | let y1 = yarns.slice(0,yarns.length-1).join(' '); //rearmost yarn(s) 422 | for (const line of tile.y2) { 423 | drawing.addLine(layer, styles, y1, line, ll, z); 424 | } 425 | } 426 | if (yarns.length >= 1 && "y1" in tile) { 427 | let y0 = yarns[yarns.length-1]; //frontmost yarn 428 | for (const line of tile.y1) { 429 | drawing.addLine(layer, styles, y0, line, ll, z); 430 | } 431 | } 432 | } 433 | 434 | function loopLines(z) { 435 | if (loops.length >= 2 && "l2" in tile) { 436 | let l1 = loops.slice(0,loops.length-1).join(' '); //rearmost loop(s) 437 | for (const line of tile.l2) { 438 | drawing.addLine(layer, styles, l1, line, ll, z); 439 | } 440 | } 441 | if (loops.length >= 1 && "l1" in tile) { 442 | let l0 = loops[loops.length-1]; //frontmost loop 443 | for (const line of tile.l1) { 444 | drawing.addLine(layer, styles, l0, line, ll, z); 445 | } 446 | } 447 | } 448 | 449 | function acrossLines(z) { 450 | if (across.length >= 2 && "a2" in tile) { 451 | let a1 = across.slice(0,across.length-1).join(' '); //rearmost across(s) 452 | for (const line of tile.a2) { 453 | drawing.addLine(layer, styles, a1, line, ll, z); 454 | } 455 | } 456 | if (across.length >= 1 && "a1" in tile) { 457 | let a0 = across[across.length-1]; //frontmost across 458 | for (const line of tile.a1) { 459 | drawing.addLine(layer, styles, a0, line, ll, z); 460 | } 461 | } 462 | } 463 | 464 | 465 | if (bed[0] == 'f') { 466 | yarnLines(0); 467 | acrossLines(1); 468 | loopLines(2); 469 | } else { 470 | loopLines(0); 471 | acrossLines(1); 472 | yarnLines(2); 473 | } 474 | 475 | } 476 | 477 | let g = {lines:[]}; 478 | if (type === 'k') { 479 | doLib('k-' + bed[0]); 480 | } else if (type === 't') { 481 | doLib(type + "-" + bed[0]); 482 | } else if (type === 'm') { 483 | doLib(type + "-" + bed[0]); 484 | } else if (type === 'S') { //split target: 485 | doLib('S-' + bed[0]); 486 | } else if (type === 's') { //split source: 487 | //TO CHECK: across === loops, right? 488 | if (JSON.stringify(across) !== JSON.stringify(loops)) { 489 | console.warn("Expecting across === loops"); 490 | } 491 | let temp = loops; 492 | loops = []; 493 | doLib('s-' + bed[0]); 494 | loops = temp; 495 | } else if (type === 'X') { //xfer target: 496 | doLib('X-' + bed[0]); 497 | } else if (type === 'x') { //xfer source: 498 | //TO CHECK: across === loops, right? 499 | if (JSON.stringify(across) !== JSON.stringify(loops)) { 500 | console.warn("Expecting across === loops"); 501 | } 502 | let temp = loops; 503 | loops = []; 504 | doLib('x-' + bed[0]); 505 | loops = temp; 506 | } else if (type === 'i') { //magic loop in 507 | doLib(type + "-" + bed[0]); 508 | } else if (type === 'o') { //magic loop out 509 | doLib(type + "-" + bed[0]); 510 | } else { 511 | //unknown! 512 | drawing.addLine(layer, styles, '#f0f', [ 0.0, 0.0, 13.0, 9.0 ], ll); 513 | drawing.addLine(layer, styles, '#f0f', [ 0.0, 9.0, 13.0, 0.0 ], ll); 514 | } 515 | return g; 516 | }; 517 | 518 | // 519 | // 0 1 2 3 4 5 6 520 | // 8 . B A . B A . 521 | // 7 . B A . B A . 522 | // 6 x B A x B A x 523 | // 5 x B A x B A x 524 | // 4 a B A a B A a 525 | // 3 b B A b B A b 526 | // 2 . B A . B A . 527 | // 1 . B A . B A . 528 | // 0 . B A . B A . 529 | 530 | VectorTiles.addYarnTile = function VectorTiles_addYarnTile(drawing, styles, tile, carriers) { 531 | console.assert(typeof(tile.y) === 'number', "Expecting bed height in yarn tile"); 532 | console.assert(typeof(tile.i) === 'number', "Expecting index in yarn tile"); 533 | console.assert(typeof(tile.bed) === 'string', "Expecting bed in yarn tile"); 534 | console.assert(typeof(tile.ports) === 'object', "Expecting ports in yarn tile"); 535 | console.assert(typeof(tile.segs) === 'object', "Expecting segs in yarn tile"); 536 | console.assert(Array.isArray(carriers), "Expecting carriers list for sorting"); 537 | 538 | const bed = tile.bed; 539 | const ports = tile.ports; 540 | const segs = tile.segs; 541 | 542 | let locs = {}; 543 | function addLoc(yarn, port, x, y) { 544 | console.assert(!((yarn + port) in locs), 'yarns visit ports once'); 545 | locs[yarn + port] = {x:x, y:y}; 546 | } 547 | ports['^+'].forEach(function(y, yi){ addLoc(y, '^+', (yi === 0 ? 5.5 : 4.5), 9.0); }); 548 | ports['v+'].forEach(function(y, yi){ addLoc(y, 'v+', (yi === 0 ? 5.5 : 4.5), 0.0); }); 549 | ports['^-'].forEach(function(y, yi){ addLoc(y, '^-', (yi === 0 ? 2.5 : 1.5), 9.0); }); 550 | ports['v-'].forEach(function(y, yi){ addLoc(y, 'v-', (yi === 0 ? 2.5 : 1.5), 0.0); }); 551 | 552 | ports['o-'].forEach(function(y, yi){ addLoc(y, 'o-', 0.5, (yi === 0 ? 6.5 : 5.5)); }); 553 | ports['x-'].forEach(function(y, yi){ addLoc(y, 'x-', 0.5, (yi === 0 ? 6.5 : 5.5)); }); 554 | ports['O-'].forEach(function(y, yi){ addLoc(y, 'O-', 3.5, (yi === 0 ? 6.5 : 5.5)); }); 555 | ports['X-'].forEach(function(y, yi){ addLoc(y, 'X-', 3.5, (yi === 0 ? 6.5 : 5.5)); }); 556 | ports['O+'].forEach(function(y, yi){ addLoc(y, 'O+', 3.5, (yi === 0 ? 6.5 : 5.5)); }); 557 | ports['X+'].forEach(function(y, yi){ addLoc(y, 'X+', 3.5, (yi === 0 ? 6.5 : 5.5)); }); 558 | ports['o+'].forEach(function(y, yi){ addLoc(y, 'o+', 6.5, (yi === 0 ? 6.5 : 5.5)); }); 559 | ports['x+'].forEach(function(y, yi){ addLoc(y, 'x+', 6.5, (yi === 0 ? 6.5 : 5.5)); }); 560 | 561 | ports['-'].forEach(function(y, yi){ addLoc(y, '-', 0.0, (yi === 0 ? 4.5 : 3.5)); }); 562 | ports['+'].forEach(function(y, yi){ addLoc(y, '+', 7.0, (yi === 0 ? 4.5 : 3.5)); }); 563 | 564 | let layer = { 565 | 'b': drawing.back, 566 | 'bs': drawing.backSliders, 567 | 'fs': drawing.frontSliders, 568 | 'f': drawing.front 569 | }[bed]; 570 | 571 | let ll = tileLowerLeft(tile.i, tile.y); 572 | 573 | segs.forEach(function(seg) { 574 | let from = {x:3.5, y:5.5}; 575 | let to = {x:3.5, y:5.5}; 576 | if (seg.from != '') { 577 | console.assert((seg.cn + seg.from) in locs, "Must have loc for " + seg.cn + seg.from); 578 | from = locs[seg.cn + seg.from]; 579 | } 580 | if (seg.to != '') { 581 | console.assert((seg.cn + seg.to) in locs, "Must have loc for " + seg.cn + seg.to); 582 | to = locs[seg.cn + seg.to]; 583 | } 584 | 585 | let index = -1; 586 | carriers.forEach(function(c) { 587 | if (c.name == seg.cn) index = c.index; 588 | }); 589 | console.assert(index !== -1, "yarns should be in carriers"); 590 | 591 | let z = carriers.length - 1 - index; //> z => more in front 592 | 593 | drawing.addLine(layer, styles, seg.cn, [ 594 | from.x, from.y, to.x, to.y 595 | ], ll, z); 596 | }); 597 | }; 598 | 599 | 600 | -------------------------------------------------------------------------------- /VectorTilesLib.js: -------------------------------------------------------------------------------- 1 | window.VectorTilesLib = {"k-b-l2-y2":{ 2 | "y1":[ 3 | [8.5,8.5, 8.5,9], 4 | [13,4.5, 8.5,4.5, 8.5,5.5], 5 | [4.5,8.5, 4.5,9], 6 | [0,4.5, 4.5,4.5, 4.5,5.5] 7 | ], 8 | "y2":[ 9 | [7.5,8.5, 7.5,9], 10 | [13,3.5, 7.5,3.5, 7.5,5.5], 11 | [5.5,8.5, 5.5,9], 12 | [0,3.5, 5.5,3.5, 5.5,5.5] 13 | ], 14 | "l1":[ 15 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,2.5], 16 | [1.5,5.5, 1.5,7.5, 11.5,7.5, 11.5,5.5], 17 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,2.5] 18 | ], 19 | "l2":[ 20 | [9.5,0.5, 10.5,0.5], 21 | [7.5,0, 7.5,0.5], 22 | [2.5,5.5, 2.5,6.5, 10.5,6.5, 10.5,5.5], 23 | [3.5,0.5, 2.5,0.5], 24 | [5.5,0, 5.5,0.5] 25 | ] 26 | }, 27 | "k-f-l2-y2":{ 28 | "y1":[ 29 | [0,4.5, 0.5,4.5], 30 | [13,4.5, 12.5,4.5], 31 | [9.5,4.5, 8.5,4.5, 8.5,9], 32 | [3.5,4.5, 4.5,4.5, 4.5,9] 33 | ], 34 | "y2":[ 35 | [0,3.5, 0.5,3.5], 36 | [13,3.5, 12.5,3.5], 37 | [9.5,3.5, 7.5,3.5, 7.5,9], 38 | [3.5,3.5, 5.5,3.5, 5.5,9] 39 | ], 40 | "l1":[ 41 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,7.5, 9.5,7.5], 42 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,7.5, 3.5,7.5] 43 | ], 44 | "l2":[ 45 | [9.5,0.5, 10.5,0.5], 46 | [7.5,0, 7.5,0.5], 47 | [2.5,2.5, 2.5,6.5, 3.5,6.5], 48 | [9.5,6.5, 10.5,6.5, 10.5,2.5], 49 | [3.5,0.5, 2.5,0.5], 50 | [5.5,0, 5.5,0.5] 51 | ] 52 | }, 53 | "k-b-l2-y1":{ 54 | "y1":[ 55 | [8.5,8.5, 8.5,9], 56 | [13,4.5, 8.5,4.5, 8.5,5.5], 57 | [4.5,8.5, 4.5,9], 58 | [0,4.5, 4.5,4.5, 4.5,5.5] 59 | ], 60 | "l1":[ 61 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,3.5], 62 | [1.5,5.5, 1.5,7.5, 11.5,7.5, 11.5,5.5], 63 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,3.5] 64 | ], 65 | "l2":[ 66 | [9.5,0.5, 10.5,0.5], 67 | [7.5,0, 7.5,0.5], 68 | [10.5,2.5, 10.5,3.5], 69 | [2.5,5.5, 2.5,6.5, 10.5,6.5, 10.5,5.5], 70 | [3.5,0.5, 2.5,0.5], 71 | [5.5,0, 5.5,0.5], 72 | [2.5,2.5, 2.5,3.5] 73 | ] 74 | }, 75 | "k-b-l1-y2":{ 76 | "y1":[ 77 | [8.5,8.5, 8.5,9], 78 | [13,4.5, 8.5,4.5, 8.5,6.5], 79 | [4.5,8.5, 4.5,9], 80 | [0,4.5, 4.5,4.5, 4.5,6.5] 81 | ], 82 | "y2":[ 83 | [7.5,8.5, 7.5,9], 84 | [13,3.5, 7.5,3.5, 7.5,6.5], 85 | [5.5,8.5, 5.5,9], 86 | [0,3.5, 5.5,3.5, 5.5,6.5] 87 | ], 88 | "l1":[ 89 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,2.5], 90 | [1.5,5.5, 1.5,7.5, 11.5,7.5, 11.5,5.5], 91 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,2.5] 92 | ] 93 | }, 94 | "k-b-l1-y1":{ 95 | "y1":[ 96 | [8.5,8.5, 8.5,9], 97 | [13,4.5, 8.5,4.5, 8.5,6.5], 98 | [4.5,8.5, 4.5,9], 99 | [0,4.5, 4.5,4.5, 4.5,6.5] 100 | ], 101 | "l1":[ 102 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,3.5], 103 | [1.5,5.5, 1.5,7.5, 11.5,7.5, 11.5,5.5], 104 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,3.5] 105 | ] 106 | }, 107 | "k-b-l2":{ 108 | "l1":[ 109 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,7.5, 1.5,7.5, 1.5,1.5, 4.5,1.5, 4.5,0] 110 | ], 111 | "l2":[ 112 | [9.5,0.5, 10.5,0.5], 113 | [7.5,0, 7.5,0.5], 114 | [2.5,2.5, 2.5,6.5, 10.5,6.5, 10.5,2.5], 115 | [3.5,0.5, 2.5,0.5], 116 | [5.5,0, 5.5,0.5] 117 | ] 118 | }, 119 | "k-b-l1":{ 120 | "l1":[ 121 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,7.5, 11.5,7.5, 11.5,1.5, 8.5,1.5, 8.5,0] 122 | ] 123 | }, 124 | "k-b-y2":{ 125 | "y1":[ 126 | [13,4.5, 8.5,4.5, 8.5,9], 127 | [0,4.5, 4.5,4.5, 4.5,9] 128 | ], 129 | "y2":[ 130 | [13,3.5, 7.5,3.5, 7.5,9], 131 | [0,3.5, 5.5,3.5, 5.5,9] 132 | ] 133 | }, 134 | "k-b-y1":{ 135 | "y1":[ 136 | [13,4.5, 8.5,4.5, 8.5,9], 137 | [0,4.5, 4.5,4.5, 4.5,9] 138 | ] 139 | }, 140 | "k-f-l2":{ 141 | "l1":[ 142 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,7.5, 1.5,7.5, 1.5,1.5, 4.5,1.5, 4.5,0] 143 | ], 144 | "l2":[ 145 | [9.5,0.5, 10.5,0.5], 146 | [7.5,0, 7.5,0.5], 147 | [2.5,2.5, 2.5,6.5, 10.5,6.5, 10.5,2.5], 148 | [3.5,0.5, 2.5,0.5], 149 | [5.5,0, 5.5,0.5] 150 | ] 151 | }, 152 | "k-f-l1":{ 153 | "l1":[ 154 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,7.5, 11.5,7.5, 11.5,1.5, 8.5,1.5, 8.5,0] 155 | ] 156 | }, 157 | "k-f-y2":{ 158 | "y1":[ 159 | [13,4.5, 8.5,4.5, 8.5,9], 160 | [0,4.5, 4.5,4.5, 4.5,9] 161 | ], 162 | "y2":[ 163 | [13,3.5, 7.5,3.5, 7.5,9], 164 | [0,3.5, 5.5,3.5, 5.5,9] 165 | ] 166 | }, 167 | "k-f-y1":{ 168 | "y1":[ 169 | [13,4.5, 8.5,4.5, 8.5,9], 170 | [0,4.5, 4.5,4.5, 4.5,9] 171 | ] 172 | }, 173 | "k-f-l2-y1":{ 174 | "y1":[ 175 | [0,4.5, 0.5,4.5], 176 | [13,4.5, 12.5,4.5], 177 | [9.5,4.5, 8.5,4.5, 8.5,9], 178 | [3.5,4.5, 4.5,4.5, 4.5,9] 179 | ], 180 | "l1":[ 181 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,7.5, 9.5,7.5], 182 | [7.5,7.5, 5.5,7.5], 183 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,7.5, 3.5,7.5] 184 | ], 185 | "l2":[ 186 | [9.5,0.5, 10.5,0.5], 187 | [7.5,0, 7.5,0.5], 188 | [2.5,2.5, 2.5,6.5, 3.5,6.5], 189 | [7.5,6.5, 5.5,6.5], 190 | [9.5,6.5, 10.5,6.5, 10.5,2.5], 191 | [3.5,0.5, 2.5,0.5], 192 | [5.5,0, 5.5,0.5] 193 | ] 194 | }, 195 | "k-f-l1-y2":{ 196 | "y1":[ 197 | [0,4.5, 0.5,4.5], 198 | [13,4.5, 12.5,4.5], 199 | [10.5,4.5, 8.5,4.5, 8.5,9], 200 | [2.5,4.5, 4.5,4.5, 4.5,9] 201 | ], 202 | "y2":[ 203 | [0,3.5, 0.5,3.5], 204 | [13,3.5, 12.5,3.5], 205 | [10.5,3.5, 7.5,3.5, 7.5,9], 206 | [2.5,3.5, 5.5,3.5, 5.5,9] 207 | ], 208 | "l1":[ 209 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,7.5, 9.5,7.5], 210 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,7.5, 3.5,7.5] 211 | ] 212 | }, 213 | "k-f-l1-y1":{ 214 | "y1":[ 215 | [0,4.5, 0.5,4.5], 216 | [13,4.5, 12.5,4.5], 217 | [10.5,4.5, 8.5,4.5, 8.5,9], 218 | [2.5,4.5, 4.5,4.5, 4.5,9] 219 | ], 220 | "l1":[ 221 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,7.5, 9.5,7.5], 222 | [5.5,7.5, 7.5,7.5], 223 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,7.5, 3.5,7.5] 224 | ] 225 | }, 226 | "t-b-l2-y2":{ 227 | "y1":[ 228 | [13,4.5, 10.75,4.5, 8.5,9], 229 | [0,4.5, 2.25,4.5, 4.5,9] 230 | ], 231 | "y2":[ 232 | [13,3.5, 10.25,3.5, 7.5,9], 233 | [0,3.5, 2.75,3.5, 5.5,9] 234 | ], 235 | "l1":[ 236 | [8.5,0, 7.5,9], 237 | [4.5,0, 5.5,9] 238 | ], 239 | "l2":[ 240 | [7.5,0, 7.5,9], 241 | [5.5,0, 5.5,9] 242 | ] 243 | }, 244 | "t-b-l2-y1":{ 245 | "y1":[ 246 | [13,4.5, 10.75,4.5, 8.5,9], 247 | [0,4.5, 2.25,4.5, 4.5,9] 248 | ], 249 | "l1":[ 250 | [8.5,0, 7.5,9], 251 | [4.5,0, 5.5,9] 252 | ], 253 | "l2":[ 254 | [7.5,0, 7.5,9], 255 | [5.5,0, 5.5,9] 256 | ] 257 | }, 258 | "t-b-l2":{ 259 | "l1":[ 260 | [8.5,0, 8.5,9], 261 | [4.5,0, 4.5,9] 262 | ], 263 | "l2":[ 264 | [7.5,0, 7.5,9], 265 | [5.5,0, 5.5,9] 266 | ] 267 | }, 268 | "t-b-l1-y2":{ 269 | "y1":[ 270 | [13,4.5, 10.75,4.5, 8.5,9], 271 | [0,4.5, 2.25,4.5, 4.5,9] 272 | ], 273 | "y2":[ 274 | [13,3.5, 10.25,3.5, 7.5,9], 275 | [0,3.5, 2.75,3.5, 5.5,9] 276 | ], 277 | "l1":[ 278 | [8.5,0, 7.5,9], 279 | [4.5,0, 5.5,9] 280 | ] 281 | }, 282 | "t-b-l1-y1":{ 283 | "y1":[ 284 | [13,4.5, 10.75,4.5, 8.5,9], 285 | [0,4.5, 2.25,4.5, 4.5,9] 286 | ], 287 | "l1":[ 288 | [8.5,0, 7.5,9], 289 | [4.5,0, 5.5,9] 290 | ] 291 | }, 292 | "t-b-l1":{ 293 | "l1":[ 294 | [8.5,0, 8.5,9], 295 | [4.5,0, 4.5,9] 296 | ] 297 | }, 298 | "t-b-y2":{ 299 | "y1":[ 300 | [13,4.5, 10.75,4.5, 8.5,9], 301 | [0,4.5, 2.25,4.5, 4.5,9] 302 | ], 303 | "y2":[ 304 | [13,3.5, 10.25,3.5, 7.5,9], 305 | [0,3.5, 2.75,3.5, 5.5,9] 306 | ] 307 | }, 308 | "t-b-y1":{ 309 | "y1":[ 310 | [13,4.5, 10.75,4.5, 8.5,9], 311 | [0,4.5, 2.25,4.5, 4.5,9] 312 | ] 313 | }, 314 | "t-f-l2-y2":{ 315 | "y1":[ 316 | [13,4.5, 10.75,4.5, 7.5,9], 317 | [0,4.5, 2.25,4.5, 5.5,9] 318 | ], 319 | "y2":[ 320 | [13,3.5, 10.25,3.5, 7.5,9], 321 | [0,3.5, 2.75,3.5, 5.5,9] 322 | ], 323 | "l1":[ 324 | [8.5,0, 8.5,9], 325 | [4.5,0, 4.5,9] 326 | ], 327 | "l2":[ 328 | [7.5,0, 7.5,9], 329 | [5.5,0, 5.5,9] 330 | ] 331 | }, 332 | "t-f-l2-y1":{ 333 | "y1":[ 334 | [13,4.5, 9.75,4.5, 7.5,9], 335 | [0,4.5, 3.25,4.5, 5.5,9] 336 | ], 337 | "l1":[ 338 | [8.5,0, 8.5,9], 339 | [4.5,0, 4.5,9] 340 | ], 341 | "l2":[ 342 | [7.5,0, 7.5,9], 343 | [5.5,0, 5.5,9] 344 | ] 345 | }, 346 | "t-f-l2":{ 347 | "l1":[ 348 | [8.5,0, 8.5,9], 349 | [4.5,0, 4.5,9] 350 | ], 351 | "l2":[ 352 | [7.5,0, 7.5,9], 353 | [5.5,0, 5.5,9] 354 | ] 355 | }, 356 | "t-f-l1-y2":{ 357 | "y1":[ 358 | [13,4.5, 10.75,4.5, 7.5,9], 359 | [0,4.5, 2.25,4.5, 5.5,9] 360 | ], 361 | "y2":[ 362 | [13,3.5, 10.25,3.5, 7.5,9], 363 | [0,3.5, 2.75,3.5, 5.5,9] 364 | ], 365 | "l1":[ 366 | [8.5,0, 8.5,9], 367 | [4.5,0, 4.5,9] 368 | ] 369 | }, 370 | "t-f-l1-y1":{ 371 | "y1":[ 372 | [13,4.5, 9.75,4.5, 7.5,9], 373 | [0,4.5, 3.25,4.5, 5.5,9] 374 | ], 375 | "l1":[ 376 | [8.5,0, 8.5,9], 377 | [4.5,0, 4.5,9] 378 | ] 379 | }, 380 | "t-f-l1":{ 381 | "l1":[ 382 | [8.5,0, 8.5,9], 383 | [4.5,0, 4.5,9] 384 | ] 385 | }, 386 | "t-f-y2":{ 387 | "y1":[ 388 | [13,4.5, 10.75,4.5, 8.5,9], 389 | [0,4.5, 2.25,4.5, 4.5,9] 390 | ], 391 | "y2":[ 392 | [13,3.5, 10.25,3.5, 7.5,9], 393 | [0,3.5, 2.75,3.5, 5.5,9] 394 | ] 395 | }, 396 | "t-f-y1":{ 397 | "y1":[ 398 | [13,4.5, 10.75,4.5, 8.5,9], 399 | [0,4.5, 2.25,4.5, 4.5,9] 400 | ] 401 | }, 402 | "s-f-y2-a2":{ 403 | "y1":[ 404 | [0,4.5, 0.5,4.5], 405 | [13,4.5, 12.5,4.5], 406 | [9.5,4.5, 8.5,4.5, 8.5,9], 407 | [3.5,4.5, 4.5,4.5, 4.5,9] 408 | ], 409 | "y2":[ 410 | [0,3.5, 0.5,3.5], 411 | [13,3.5, 12.5,3.5], 412 | [9.5,3.5, 7.5,3.5, 7.5,9], 413 | [3.5,3.5, 5.5,3.5, 5.5,9] 414 | ], 415 | "a1":[ 416 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,5.5], 417 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,5.5] 418 | ], 419 | "a2":[ 420 | [9.5,0.5, 10.5,0.5], 421 | [7.5,0, 7.5,0.5], 422 | [2.5,2.5, 2.5,5.5], 423 | [10.5,5.5, 10.5,2.5], 424 | [3.5,0.5, 2.5,0.5], 425 | [5.5,0, 5.5,0.5] 426 | ] 427 | }, 428 | "s-f-a2":{ 429 | "a1":[ 430 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,5.5], 431 | [1.5,5.5, 1.5,1.5, 4.5,1.5, 4.5,0] 432 | ], 433 | "a2":[ 434 | [9.5,0.5, 10.5,0.5], 435 | [7.5,0, 7.5,0.5], 436 | [2.5,2.5, 2.5,5.5], 437 | [10.5,5.5, 10.5,2.5], 438 | [3.5,0.5, 2.5,0.5], 439 | [5.5,0, 5.5,0.5] 440 | ] 441 | }, 442 | "s-f-a1":{ 443 | "a1":[ 444 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,5.5], 445 | [11.5,5.5, 11.5,1.5, 8.5,1.5, 8.5,0] 446 | ] 447 | }, 448 | "s-f-y2":{ 449 | "y1":[ 450 | [13,4.5, 8.5,4.5, 8.5,9], 451 | [0,4.5, 4.5,4.5, 4.5,9] 452 | ], 453 | "y2":[ 454 | [13,3.5, 7.5,3.5, 7.5,9], 455 | [0,3.5, 5.5,3.5, 5.5,9] 456 | ] 457 | }, 458 | "s-f-y1":{ 459 | "y1":[ 460 | [13,4.5, 8.5,4.5, 8.5,9], 461 | [0,4.5, 4.5,4.5, 4.5,9] 462 | ] 463 | }, 464 | "s-f-y1-a2":{ 465 | "y1":[ 466 | [0,4.5, 0.5,4.5], 467 | [13,4.5, 12.5,4.5], 468 | [9.5,4.5, 8.5,4.5, 8.5,9], 469 | [3.5,4.5, 4.5,4.5, 4.5,9] 470 | ], 471 | "a1":[ 472 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,5.5], 473 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,5.5] 474 | ], 475 | "a2":[ 476 | [9.5,0.5, 10.5,0.5], 477 | [7.5,0, 7.5,0.5], 478 | [2.5,2.5, 2.5,5.5], 479 | [10.5,5.5, 10.5,2.5], 480 | [3.5,0.5, 2.5,0.5], 481 | [5.5,0, 5.5,0.5] 482 | ] 483 | }, 484 | "s-f-y2-a1":{ 485 | "y1":[ 486 | [0,4.5, 0.5,4.5], 487 | [13,4.5, 12.5,4.5], 488 | [10.5,4.5, 8.5,4.5, 8.5,9], 489 | [2.5,4.5, 4.5,4.5, 4.5,9] 490 | ], 491 | "y2":[ 492 | [0,3.5, 0.5,3.5], 493 | [13,3.5, 12.5,3.5], 494 | [10.5,3.5, 7.5,3.5, 7.5,9], 495 | [2.5,3.5, 5.5,3.5, 5.5,9] 496 | ], 497 | "a1":[ 498 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,5.5], 499 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,5.5] 500 | ] 501 | }, 502 | "s-f-y1-a1":{ 503 | "y1":[ 504 | [0,4.5, 0.5,4.5], 505 | [13,4.5, 12.5,4.5], 506 | [10.5,4.5, 8.5,4.5, 8.5,9], 507 | [2.5,4.5, 4.5,4.5, 4.5,9] 508 | ], 509 | "a1":[ 510 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,5.5], 511 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,5.5] 512 | ] 513 | }, 514 | "s-f":{ 515 | 516 | }, 517 | "t-b":{ 518 | 519 | }, 520 | "k-f":{ 521 | 522 | }, 523 | "k-b":{ 524 | 525 | }, 526 | "t-f":{ 527 | 528 | }, 529 | "s-b-y2-a2":{ 530 | "y1":[ 531 | [13,4.5, 8.5,4.5, 8.5,9], 532 | [0,4.5, 4.5,4.5, 4.5,9] 533 | ], 534 | "y2":[ 535 | [13,3.5, 7.5,3.5, 7.5,9], 536 | [0,3.5, 5.5,3.5, 5.5,9] 537 | ], 538 | "a1":[ 539 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,2.5], 540 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,2.5] 541 | ], 542 | "a2":[ 543 | [9.5,0.5, 10.5,0.5], 544 | [7.5,0, 7.5,0.5], 545 | [3.5,0.5, 2.5,0.5], 546 | [5.5,0, 5.5,0.5] 547 | ] 548 | }, 549 | "s-b-a2":{ 550 | "a1":[ 551 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,5.5], 552 | [1.5,5.5, 1.5,1.5, 4.5,1.5, 4.5,0] 553 | ], 554 | "a2":[ 555 | [9.5,0.5, 10.5,0.5], 556 | [7.5,0, 7.5,0.5], 557 | [2.5,2.5, 2.5,5.5], 558 | [10.5,5.5, 10.5,2.5], 559 | [3.5,0.5, 2.5,0.5], 560 | [5.5,0, 5.5,0.5] 561 | ] 562 | }, 563 | "s-b-a1":{ 564 | "a1":[ 565 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,5.5], 566 | [11.5,5.5, 11.5,1.5, 8.5,1.5, 8.5,0] 567 | ] 568 | }, 569 | "s-b-y2":{ 570 | "y1":[ 571 | [13,4.5, 8.5,4.5, 8.5,9], 572 | [0,4.5, 4.5,4.5, 4.5,9] 573 | ], 574 | "y2":[ 575 | [13,3.5, 7.5,3.5, 7.5,9], 576 | [0,3.5, 5.5,3.5, 5.5,9] 577 | ] 578 | }, 579 | "s-b-y1":{ 580 | "y1":[ 581 | [13,4.5, 8.5,4.5, 8.5,9], 582 | [0,4.5, 4.5,4.5, 4.5,9] 583 | ] 584 | }, 585 | "S-f-l2-a2":{ 586 | "l1":[ 587 | [8.5,0, 8.5,9], 588 | [4.5,0, 4.5,9] 589 | ], 590 | "l2":[ 591 | [7.5,0, 7.5,9], 592 | [5.5,0, 5.5,9] 593 | ], 594 | "a1":[ 595 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 596 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 597 | ], 598 | "a2":[ 599 | [2.5,6.5, 5.4483,6.5, 5.5,9], 600 | [7.5,9, 7.5,6.5, 10.5,6.5] 601 | ] 602 | }, 603 | "S-f-l2-a1":{ 604 | "l1":[ 605 | [8.5,0, 8.5,9], 606 | [4.5,0, 4.5,9] 607 | ], 608 | "l2":[ 609 | [7.5,0, 7.5,9], 610 | [5.5,0, 5.5,9] 611 | ], 612 | "a1":[ 613 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 614 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 615 | ] 616 | }, 617 | "S-f-l2":{ 618 | "l1":[ 619 | [8.5,0, 8.5,9], 620 | [4.5,0, 4.5,9] 621 | ], 622 | "l2":[ 623 | [7.5,0, 7.5,9], 624 | [5.5,0, 5.5,9] 625 | ] 626 | }, 627 | "s-b":{ 628 | 629 | }, 630 | "s-b-y1-a2":{ 631 | "y1":[ 632 | [13,4.5, 8.5,4.5, 8.5,9], 633 | [0,4.5, 4.5,4.5, 4.5,9] 634 | ], 635 | "a1":[ 636 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,3.5], 637 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,3.5] 638 | ], 639 | "a2":[ 640 | [9.5,0.5, 10.5,0.5], 641 | [7.5,0, 7.5,0.5], 642 | [2.5,2.5, 2.5,3.5], 643 | [10.5,2.5, 10.5,3.5], 644 | [3.5,0.5, 2.5,0.5], 645 | [5.5,0, 5.5,0.5] 646 | ] 647 | }, 648 | "s-b-y2-a1":{ 649 | "y1":[ 650 | [13,4.5, 8.5,4.5, 8.5,9], 651 | [0,4.5, 4.5,4.5, 4.5,9] 652 | ], 653 | "y2":[ 654 | [13,3.5, 7.5,3.5, 7.5,9], 655 | [0,3.5, 5.5,3.5, 5.5,9] 656 | ], 657 | "a1":[ 658 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,2.5], 659 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,2.5] 660 | ] 661 | }, 662 | "s-b-y1-a1":{ 663 | "y1":[ 664 | [13,4.5, 8.5,4.5, 8.5,9], 665 | [0,4.5, 4.5,4.5, 4.5,9] 666 | ], 667 | "a1":[ 668 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,3.5], 669 | [4.5,0, 4.5,1.5, 1.5,1.5, 1.5,3.5] 670 | ] 671 | }, 672 | "x-f-a2":{ 673 | "a1":[ 674 | [8.5,0, 8.5,5.5], 675 | [4.5,0, 4.5,5.5] 676 | ], 677 | "a2":[ 678 | [7.5,0, 7.5,5.5], 679 | [5.5,0, 5.5,5.5] 680 | ] 681 | }, 682 | "X-b-l2-a2":{ 683 | "l1":[ 684 | [8.5,0, 7.5,9], 685 | [4.5,0, 5.5,9] 686 | ], 687 | "l2":[ 688 | [7.5,0, 7.5,9], 689 | [5.5,0, 5.5,9] 690 | ], 691 | "a1":[ 692 | [4.5,6.5, 4.5,9], 693 | [8.5,9, 8.5,6.5] 694 | ], 695 | "a2":[ 696 | [5.5,6.5, 5.5,9], 697 | [7.5,9, 7.5,6.5] 698 | ] 699 | }, 700 | "S-f-l1-a2":{ 701 | "l1":[ 702 | [8.5,0, 8.5,9], 703 | [4.5,0, 4.5,9] 704 | ], 705 | "a1":[ 706 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 707 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 708 | ], 709 | "a2":[ 710 | [2.5,6.5, 5.4483,6.5, 5.5,9], 711 | [7.5,9, 7.5,6.5, 10.5,6.5] 712 | ] 713 | }, 714 | "S-f-l1-a1":{ 715 | "l1":[ 716 | [8.5,0, 8.5,9], 717 | [4.5,0, 4.5,9] 718 | ], 719 | "a1":[ 720 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 721 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 722 | ] 723 | }, 724 | "S-f-l1":{ 725 | "l1":[ 726 | [8.5,0, 8.5,9], 727 | [4.5,0, 4.5,9] 728 | ] 729 | }, 730 | "S-f-a2":{ 731 | "a1":[ 732 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 733 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 734 | ], 735 | "a2":[ 736 | [2.5,6.5, 5.4483,6.5, 5.5,9], 737 | [7.5,9, 7.5,6.5, 10.5,6.5] 738 | ] 739 | }, 740 | "S-f-a1":{ 741 | "a1":[ 742 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 743 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 744 | ] 745 | }, 746 | "S-f":{ 747 | 748 | }, 749 | "S-b-l2-a2":{ 750 | "l1":[ 751 | [8.5,0, 8.5,9], 752 | [4.5,0, 4.5,9] 753 | ], 754 | "l2":[ 755 | [7.5,0, 7.5,9], 756 | [5.5,0, 5.5,9] 757 | ], 758 | "a1":[ 759 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 760 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 761 | ], 762 | "a2":[ 763 | [2.5,6.5, 5.4483,6.5, 5.5,9], 764 | [7.5,9, 7.5,6.5, 10.5,6.5] 765 | ] 766 | }, 767 | "S-b-l2-a1":{ 768 | "l1":[ 769 | [8.5,0, 8.5,9], 770 | [4.5,0, 4.5,9] 771 | ], 772 | "l2":[ 773 | [7.5,0, 7.5,9], 774 | [5.5,0, 5.5,9] 775 | ], 776 | "a1":[ 777 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 778 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 779 | ] 780 | }, 781 | "S-b-l2":{ 782 | "l1":[ 783 | [8.5,0, 8.5,9], 784 | [4.5,0, 4.5,9] 785 | ], 786 | "l2":[ 787 | [7.5,0, 7.5,9], 788 | [5.5,0, 5.5,9] 789 | ] 790 | }, 791 | "S-b-l1-a2":{ 792 | "l1":[ 793 | [8.5,0, 8.5,9], 794 | [4.5,0, 4.5,9] 795 | ], 796 | "a1":[ 797 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 798 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 799 | ], 800 | "a2":[ 801 | [2.5,6.5, 5.4483,6.5, 5.5,9], 802 | [7.5,9, 7.5,6.5, 10.5,6.5] 803 | ] 804 | }, 805 | "S-b-l1-a1":{ 806 | "l1":[ 807 | [8.5,0, 8.5,9], 808 | [4.5,0, 4.5,9] 809 | ], 810 | "a1":[ 811 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 812 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 813 | ] 814 | }, 815 | "S-b-l1":{ 816 | "l1":[ 817 | [8.5,0, 8.5,9], 818 | [4.5,0, 4.5,9] 819 | ] 820 | }, 821 | "S-b-a2":{ 822 | "a1":[ 823 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 824 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 825 | ], 826 | "a2":[ 827 | [2.5,6.5, 5.4483,6.5, 5.5,9], 828 | [7.5,9, 7.5,6.5, 10.5,6.5] 829 | ] 830 | }, 831 | "S-b-a1":{ 832 | "a1":[ 833 | [1.5,6.5, 1.5,7.5, 4.5,7.5, 4.5,9], 834 | [8.5,9, 8.5,7.5, 11.5,7.5, 11.5,6.5] 835 | ] 836 | }, 837 | "S-b":{ 838 | 839 | }, 840 | "x-f-a1":{ 841 | "a1":[ 842 | [8.5,0, 8.5,5.5], 843 | [4.5,0, 4.5,5.5] 844 | ] 845 | }, 846 | "X-b-l2-a1":{ 847 | "l1":[ 848 | [8.5,0, 7.5,9], 849 | [4.5,0, 5.5,9] 850 | ], 851 | "l2":[ 852 | [7.5,0, 7.5,9], 853 | [5.5,0, 5.5,9] 854 | ], 855 | "a1":[ 856 | [4.5,6.5, 4.5,9], 857 | [8.5,9, 8.5,6.5] 858 | ] 859 | }, 860 | "x-f":{ 861 | 862 | }, 863 | "X-b-l2":{ 864 | "l1":[ 865 | [8.5,0, 8.5,9], 866 | [4.5,0, 4.5,9] 867 | ], 868 | "l2":[ 869 | [7.5,0, 7.5,9], 870 | [5.5,0, 5.5,9] 871 | ] 872 | }, 873 | "X-b-l1-a2":{ 874 | "l1":[ 875 | [8.5,0, 8.5,5, 7.5,6, 7.5,9], 876 | [4.5,0, 4.5,5, 5.5,6, 5.5,9] 877 | ], 878 | "a1":[ 879 | [4.5,6.5, 4.5,9], 880 | [8.5,9, 8.5,6.5] 881 | ], 882 | "a2":[ 883 | [5.5,6.5, 5.5,9], 884 | [7.5,9, 7.5,6.5] 885 | ] 886 | }, 887 | "X-b-l1-a1":{ 888 | "l1":[ 889 | [8.5,0, 8.5,5, 7.5,6, 7.5,9], 890 | [4.5,0, 4.5,5, 5.5,6, 5.5,9] 891 | ], 892 | "a1":[ 893 | [4.5,6.5, 4.5,9], 894 | [8.5,9, 8.5,6.5] 895 | ] 896 | }, 897 | "X-b-l1":{ 898 | "l1":[ 899 | [8.5,0, 8.5,9], 900 | [4.5,0, 4.5,9] 901 | ] 902 | }, 903 | "X-b-a2":{ 904 | "a1":[ 905 | [4.5,6.5, 4.5,9], 906 | [8.5,9, 8.5,6.5] 907 | ], 908 | "a2":[ 909 | [5.5,6.5, 5.5,9], 910 | [7.5,9, 7.5,6.5] 911 | ] 912 | }, 913 | "X-b-a1":{ 914 | "a1":[ 915 | [4.5,6.5, 4.5,9], 916 | [8.5,9, 8.5,6.5] 917 | ] 918 | }, 919 | "X-b":{ 920 | 921 | }, 922 | "x-b-a2":{ 923 | "a1":[ 924 | [8.5,0, 8.5,5.5], 925 | [4.5,0, 4.5,5.5] 926 | ], 927 | "a2":[ 928 | [7.5,0, 7.5,5.5], 929 | [5.5,0, 5.5,5.5] 930 | ] 931 | }, 932 | "x-b-a1":{ 933 | "a1":[ 934 | [8.5,0, 8.5,5.5], 935 | [4.5,0, 4.5,5.5] 936 | ] 937 | }, 938 | "X-f-l2-a1":{ 939 | "l1":[ 940 | [8.5,0, 8.5,9], 941 | [4.5,0, 4.5,9] 942 | ], 943 | "l2":[ 944 | [7.5,0, 7.5,9], 945 | [5.5,0, 5.5,9] 946 | ], 947 | "a1":[ 948 | [4.5,6.5, 4.5,9], 949 | [8.5,9, 8.5,6.5] 950 | ] 951 | }, 952 | "x-b":{ 953 | 954 | }, 955 | "X-f-l2":{ 956 | "l1":[ 957 | [8.5,0, 8.5,9], 958 | [4.5,0, 4.5,9] 959 | ], 960 | "l2":[ 961 | [7.5,0, 7.5,9], 962 | [5.5,0, 5.5,9] 963 | ] 964 | }, 965 | "X-f-l1-a2":{ 966 | "l1":[ 967 | [8.5,0, 8.5,9], 968 | [4.5,0, 4.5,9] 969 | ], 970 | "a1":[ 971 | [4.5,6.5, 5.5,9], 972 | [7.5,9, 8.5,6.5] 973 | ], 974 | "a2":[ 975 | [5.5,6.5, 5.5,9], 976 | [7.5,9, 7.5,6.5] 977 | ] 978 | }, 979 | "X-f-l1-a1":{ 980 | "l1":[ 981 | [8.5,0, 8.5,9], 982 | [4.5,0, 4.5,9] 983 | ], 984 | "a1":[ 985 | [4.5,6.5, 5.5,7.5, 5.5,9], 986 | [7.5,9, 7.5,7.5, 8.5,6.5] 987 | ] 988 | }, 989 | "X-f-l1":{ 990 | "l1":[ 991 | [8.5,0, 8.5,9], 992 | [4.5,0, 4.5,9] 993 | ] 994 | }, 995 | "X-f-a2":{ 996 | "a1":[ 997 | [4.5,6.5, 4.5,9], 998 | [8.5,9, 8.5,6.5] 999 | ], 1000 | "a2":[ 1001 | [5.5,6.5, 5.5,9], 1002 | [7.5,9, 7.5,6.5] 1003 | ] 1004 | }, 1005 | "X-f-a1":{ 1006 | "a1":[ 1007 | [4.5,6.5, 4.5,9], 1008 | [8.5,9, 8.5,6.5] 1009 | ] 1010 | }, 1011 | "X-f":{ 1012 | 1013 | }, 1014 | "X-f-l2-a2":{ 1015 | "l1":[ 1016 | [8.5,0, 8.5,9], 1017 | [4.5,0, 4.5,9] 1018 | ], 1019 | "l2":[ 1020 | [7.5,0, 7.5,9], 1021 | [5.5,0, 5.5,9] 1022 | ], 1023 | "a1":[ 1024 | [4.5,6.5, 4.5,9], 1025 | [8.5,9, 8.5,6.5] 1026 | ], 1027 | "a2":[ 1028 | [5.5,6.5, 5.5,9], 1029 | [7.5,9, 7.5,6.5] 1030 | ] 1031 | }, 1032 | "m-f-l2-y2":{ 1033 | "y1":[ 1034 | [0,4.5, 3.5,4.5], 1035 | [6.5,4.5, 6.51,4.5], 1036 | [9.5,4.5, 13,4.5] 1037 | ], 1038 | "y2":[ 1039 | [0,3.5, 3.5,3.5], 1040 | [6.5,3.5, 6.51,3.5], 1041 | [9.5,3.5, 13,3.5] 1042 | ], 1043 | "l1":[ 1044 | [8.5,0, 8.5,9], 1045 | [4.5,0, 4.5,9] 1046 | ], 1047 | "l2":[ 1048 | [7.5,0, 7.5,9], 1049 | [5.5,0, 5.5,9] 1050 | ] 1051 | }, 1052 | "m-f-l1-y2":{ 1053 | "y1":[ 1054 | [0,4.5, 3.5,4.5], 1055 | [5.5,4.5, 7.5,4.5], 1056 | [9.5,4.5, 13,4.5] 1057 | ], 1058 | "y2":[ 1059 | [0,3.5, 3.5,3.5], 1060 | [5.5,3.5, 7.5,3.5], 1061 | [9.5,3.5, 13,3.5] 1062 | ], 1063 | "l1":[ 1064 | [8.5,0, 8.5,9], 1065 | [4.5,0, 4.5,9] 1066 | ] 1067 | }, 1068 | "m-f-y2":{ 1069 | "y1":[ 1070 | [0,4.5, 13,4.5] 1071 | ], 1072 | "y2":[ 1073 | [13,3.5, 0,3.5] 1074 | ] 1075 | }, 1076 | "m-f-l2-y1":{ 1077 | "y1":[ 1078 | [0,4.5, 3.5,4.5], 1079 | [6.5,4.5, 6.51,4.5], 1080 | [9.5,4.5, 13,4.5] 1081 | ], 1082 | "l1":[ 1083 | [8.5,0, 8.5,9], 1084 | [4.5,0, 4.5,9] 1085 | ], 1086 | "l2":[ 1087 | [7.5,0, 7.5,9], 1088 | [5.5,0, 5.5,9] 1089 | ] 1090 | }, 1091 | "m-f-l1-y1":{ 1092 | "y1":[ 1093 | [0,4.5, 3.5,4.5], 1094 | [5.5,4.5, 7.5,4.5], 1095 | [9.5,4.5, 13,4.5] 1096 | ], 1097 | "l1":[ 1098 | [8.5,0, 8.5,9], 1099 | [4.5,0, 4.5,9] 1100 | ] 1101 | }, 1102 | "m-f-y1":{ 1103 | "y1":[ 1104 | [0,4.5, 13,4.5] 1105 | ] 1106 | }, 1107 | "m-f-l2":{ 1108 | "l1":[ 1109 | [8.5,0, 8.5,9], 1110 | [4.5,0, 4.5,9] 1111 | ], 1112 | "l2":[ 1113 | [7.5,0, 7.5,9], 1114 | [5.5,0, 5.5,9] 1115 | ] 1116 | }, 1117 | "m-f-l1":{ 1118 | "l1":[ 1119 | [8.5,0, 8.5,9], 1120 | [4.5,0, 4.5,9] 1121 | ] 1122 | }, 1123 | "m-f":{ 1124 | 1125 | }, 1126 | "m-b-l2-y2":{ 1127 | "y1":[ 1128 | [0,4.5, 13,4.5] 1129 | ], 1130 | "y2":[ 1131 | [13,3.5, 0,3.5] 1132 | ], 1133 | "l1":[ 1134 | [8.5,0, 8.5,2.5], 1135 | [8.5,5.5, 8.5,9], 1136 | [4.5,0, 4.5,2.5], 1137 | [4.5,5.5, 4.5,9] 1138 | ], 1139 | "l2":[ 1140 | [7.5,0, 7.5,2.5], 1141 | [7.5,5.5, 7.5,9], 1142 | [5.5,0, 5.5,2.5], 1143 | [5.5,5.5, 5.5,9] 1144 | ] 1145 | }, 1146 | "m-b-l1-y2":{ 1147 | "y1":[ 1148 | [0,4.5, 13,4.5] 1149 | ], 1150 | "y2":[ 1151 | [13,3.5, 0,3.5] 1152 | ], 1153 | "l1":[ 1154 | [8.5,0, 8.5,2.5], 1155 | [8.5,5.5, 8.5,9], 1156 | [4.5,0, 4.5,2.5], 1157 | [4.5,5.5, 4.5,9] 1158 | ] 1159 | }, 1160 | "m-b-y2":{ 1161 | "y1":[ 1162 | [0,4.5, 13,4.5] 1163 | ], 1164 | "y2":[ 1165 | [13,3.5, 0,3.5] 1166 | ] 1167 | }, 1168 | "m-b-l2-y1":{ 1169 | "y1":[ 1170 | [0,4.5, 13,4.5] 1171 | ], 1172 | "l1":[ 1173 | [8.5,0, 8.5,3.5], 1174 | [8.5,5.5, 8.5,9], 1175 | [4.5,0, 4.5,3.5], 1176 | [4.5,5.5, 4.5,9] 1177 | ], 1178 | "l2":[ 1179 | [7.5,0, 7.5,3.5], 1180 | [7.5,5.5, 7.5,9], 1181 | [5.5,0, 5.5,3.5], 1182 | [5.5,5.5, 5.5,9] 1183 | ] 1184 | }, 1185 | "m-b-l1-y1":{ 1186 | "y1":[ 1187 | [0,4.5, 13,4.5] 1188 | ], 1189 | "l1":[ 1190 | [8.5,0, 8.5,3.5], 1191 | [8.5,5.5, 8.5,9], 1192 | [4.5,0, 4.5,3.5], 1193 | [4.5,5.5, 4.5,9] 1194 | ] 1195 | }, 1196 | "m-b-y1":{ 1197 | "y1":[ 1198 | [0,4.5, 13,4.5] 1199 | ] 1200 | }, 1201 | "m-b-l2":{ 1202 | "l1":[ 1203 | [8.5,0, 8.5,9], 1204 | [4.5,0, 4.5,9] 1205 | ], 1206 | "l2":[ 1207 | [7.5,0, 7.5,9], 1208 | [5.5,0, 5.5,9] 1209 | ] 1210 | }, 1211 | "m-b-l1":{ 1212 | "l1":[ 1213 | [8.5,0, 8.5,9], 1214 | [4.5,0, 4.5,9] 1215 | ] 1216 | }, 1217 | "m-b":{ 1218 | 1219 | }, 1220 | "i-b-y2":{ 1221 | "y1":[ 1222 | [8.5,5.1, 8.5,5], 1223 | [8.5,6.5, 8.5,9], 1224 | [4.5,9, 4.5,6.5], 1225 | [4.5,5.1, 4.5,5] 1226 | ], 1227 | "y2":[ 1228 | [5.5,4.6, 5.5,4.5], 1229 | [7.5,4.6, 7.5,4.5], 1230 | [7.5,6, 7.5,9], 1231 | [5.5,6, 5.5,9] 1232 | ] 1233 | }, 1234 | "i-b-y1":{ 1235 | "y1":[ 1236 | [8.5,5.1, 8.5,5], 1237 | [8.5,6.5, 8.5,9], 1238 | [4.5,9, 4.5,6.5], 1239 | [4.5,5.1, 4.5,5] 1240 | ] 1241 | }, 1242 | "i-f-y2":{ 1243 | "y1":[ 1244 | [8.5,5.1, 8.5,5], 1245 | [8.5,6.5, 8.5,9], 1246 | [4.5,9, 4.5,6.5], 1247 | [4.5,5.1, 4.5,5] 1248 | ], 1249 | "y2":[ 1250 | [5.5,4.6, 5.5,4.5], 1251 | [7.5,4.6, 7.5,4.5], 1252 | [7.5,6, 7.5,9], 1253 | [5.5,6, 5.5,9] 1254 | ] 1255 | }, 1256 | "i-f-y1":{ 1257 | "y1":[ 1258 | [8.5,5.1, 8.5,5], 1259 | [8.5,6.5, 8.5,9], 1260 | [4.5,9, 4.5,6.5], 1261 | [4.5,5.1, 4.5,5] 1262 | ] 1263 | }, 1264 | "i-f":{ 1265 | 1266 | }, 1267 | "i-b":{ 1268 | 1269 | }, 1270 | "o-b-l2":{ 1271 | "l1":[ 1272 | [11.5,5.5, 11.5,5.6], 1273 | [1.5,5.5, 1.5,5.6], 1274 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,4], 1275 | [1.5,4, 1.5,1.5, 4.5,1.5, 4.5,0] 1276 | ], 1277 | "l2":[ 1278 | [2.5,4.5, 2.5,4.6], 1279 | [10.5,4.5, 10.5,4.6], 1280 | [9.5,0.5, 10.5,0.5], 1281 | [7.5,0, 7.5,0.5], 1282 | [2.5,2.5, 2.5,3], 1283 | [10.5,3, 10.5,2.5], 1284 | [3.5,0.5, 2.5,0.5], 1285 | [5.5,0, 5.5,0.5] 1286 | ] 1287 | }, 1288 | "o-f-l2":{ 1289 | "l1":[ 1290 | [11.5,5.5, 11.5,5.6], 1291 | [1.5,5.5, 1.5,5.6], 1292 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,4], 1293 | [1.5,4, 1.5,1.5, 4.5,1.5, 4.5,0] 1294 | ], 1295 | "l2":[ 1296 | [2.5,4.5, 2.5,4.6], 1297 | [10.5,4.5, 10.5,4.6], 1298 | [9.5,0.5, 10.5,0.5], 1299 | [7.5,0, 7.5,0.5], 1300 | [2.5,2.5, 2.5,3], 1301 | [10.5,3, 10.5,2.5], 1302 | [3.5,0.5, 2.5,0.5], 1303 | [5.5,0, 5.5,0.5] 1304 | ] 1305 | }, 1306 | "o-b-l1":{ 1307 | "l1":[ 1308 | [11.5,5.5, 11.5,5.6], 1309 | [1.5,5.5, 1.5,5.6], 1310 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,4], 1311 | [1.5,4, 1.5,1.5, 4.5,1.5, 4.5,0] 1312 | ] 1313 | }, 1314 | "o-f-l1":{ 1315 | "l1":[ 1316 | [11.5,5.5, 11.5,5.6], 1317 | [1.5,5.5, 1.5,5.6], 1318 | [8.5,0, 8.5,1.5, 11.5,1.5, 11.5,4], 1319 | [1.5,4, 1.5,1.5, 4.5,1.5, 4.5,0] 1320 | ] 1321 | }, 1322 | "o-f":{ 1323 | 1324 | }, 1325 | "o-b":{ 1326 | 1327 | } 1328 | }; -------------------------------------------------------------------------------- /cross-test.k: -------------------------------------------------------------------------------- 1 | ;!knitout-2 2 | ;;Carriers: A B C D 3 | 4 | ; tests for yarn carrier crossing 5 | 6 | inhook A B C D 7 | knit + f1 B 8 | knit + f2 B 9 | knit + f4 B 10 | knit + f5 B 11 | knit - f5 B 12 | 13 | knit + f1 A 14 | knit + f2 A 15 | knit + f3 A 16 | knit + f4 A 17 | knit + f5 A 18 | 19 | knit + f1 C 20 | knit + f2 C 21 | knit + f3 C 22 | knit + f4 C 23 | knit + f5 C 24 | 25 | xfer f1 b1 26 | xfer f2 b2 27 | xfer f3 b3 28 | xfer f4 b4 29 | xfer f5 b5 30 | 31 | knit + b1 A 32 | knit + b2 A 33 | knit + b3 A 34 | knit + b4 A 35 | knit + b5 A 36 | 37 | knit + b1 C 38 | knit + b2 C 39 | knit + b3 C 40 | knit + b4 C 41 | knit + b5 C 42 | 43 | xfer b1 f1 44 | xfer b2 f2 45 | xfer b3 f3 46 | xfer b4 f4 47 | xfer b5 f5 48 | 49 | knit + f1 A C 50 | knit + f2 A C 51 | knit + f3 A C 52 | knit + f4 A C 53 | knit + f5 A C 54 | 55 | xfer f1 b1 56 | xfer f2 b2 57 | xfer f3 b3 58 | xfer f4 b4 59 | xfer f5 b5 60 | 61 | knit + b1 A C 62 | knit + b2 A C 63 | knit + b3 A C 64 | knit + b4 A C 65 | knit + b5 A C 66 | 67 | xfer b1 f1 68 | xfer b2 f2 69 | xfer b3 f3 70 | xfer b4 f4 71 | xfer b5 f5 72 | 73 | drop f1 74 | drop f2 75 | drop f3 76 | drop f4 77 | drop f5 78 | -------------------------------------------------------------------------------- /evalJS.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | //knitout frontend 4 | function evalJS(codeText){ 5 | 6 | //=========== start of knitout.js ============= 7 | let machine = null; //can set this as machine header value (if provided), and use it to warn about unsupported extensions 8 | // basic writer 9 | var Writer = function(opts){ 10 | //public data: 11 | this.carriers = {}; //names of all currently active carriers 12 | this.needles = {}; //names of all currently-holding-loops needles 13 | this.racking = 0; //current racking value 14 | 15 | //private data: 16 | this._carriers = []; //array of carrier names, front-to-back order 17 | this._operations = []; //array of operations, stored as strings 18 | this._headers = []; //array of headers. stored as strings 19 | 20 | 21 | //fill '_carriers' array from opts.carriers: 22 | if (typeof(opts) === 'undefined' || !('carriers' in opts)) { 23 | console.warn("WARNING: options object passed to knitout.Writer does not contain a 'carriers' member. Will assume a default carrier layout (a single carrier named \"A\")"); 24 | this._carriers = ["A"]; 25 | } else { 26 | if (!Array.isArray(opts.carriers)) throw new Error("opts.carriers should be an array of carrier names"); 27 | opts.carriers.forEach((name) => { 28 | if (!(typeof(name) === 'string' && name.indexOf(' ') === -1)) { 29 | throw new Error("Carrier names must be strings that do not contain the space character (' ')."); 30 | } 31 | }); 32 | this._carriers = opts.carriers.slice(); 33 | } 34 | //build a 'carriers' header from the '_carriers' list: 35 | this._headers.push(";;Carriers: " + this._carriers.join(" ")); 36 | }; 37 | 38 | // function that queues header information to header list 39 | Writer.prototype.addHeader = function (name, value) { 40 | if (name === undefined || value === undefined) { 41 | throw new Error("Writer.addHeader should be called with a name and a value"); 42 | } 43 | 44 | if (!(typeof(name) === 'string' && name.indexOf(': ') === -1)) { 45 | throw new Error("Header names must be strings that don't contain the sequence ': '"); 46 | } 47 | if (!(typeof(value) === 'string' && value.indexOf('\n') === -1)) { 48 | throw new Error("Header values must be strings that do not contain the LF character ('\\n')."); 49 | } 50 | 51 | //Check for valid headers: 52 | if (name === "Carriers") { 53 | throw new Error("Writer.addHeader can't set Carriers header (use the 'carriers' option when creating the writer instead)."); 54 | } else if (name === "Machine") { 55 | machine = value; 56 | //no restrictions on value 57 | } else if (name === "Gauge") { 58 | if ((typeof(value) !== 'string' && !/^[0-9 ]+$/.test(value))) throw new Error(`Value of 'Gauge' header must be a string representing a number. Provided value: '${value}' is not valid.`); 59 | } else if (name === "Position") { 60 | let supported_positions = ['Left', 'Center', 'Right', 'Keep']; 61 | if (!supported_positions.includes(value)) throw new Error(`'Position' header must have one of the following values: ${supported_positions.join(', ')}. Provided value: '${value}' is not valid.`); 62 | } else if (name.startsWith("Yarn-")) { 63 | //check for valid carrier name, warn otherwise 64 | let carrierName = name.substr(5); 65 | if (this._carriers.indexOf(carrierName) === -1) { 66 | console.warn("Warning: header '" + name + "' mentions a carrier that isn't in the carriers list."); 67 | } 68 | } else if (name.startsWith('X-')) { 69 | //all extension header values are okay! 70 | } else { 71 | console.warn("Warning: header name '" + name + "' not recognized; header will still be written."); 72 | } 73 | this._headers.push(';;' + name + ': ' + value); 74 | }; 75 | 76 | // escape hatch to dump your custom instruction to knitout 77 | // if you know what you are doing 78 | Writer.prototype.addRawOperation = function( operation ){ 79 | console.warn("Warning: operation added to list as is(string), no error checking performed."); 80 | this._operations.push(operation); 81 | }; 82 | 83 | //helpers to extract parameters from argument arrays: 84 | // (these remove the extracted arguments from the start of the array and throw on errors) 85 | 86 | //shiftDirection interprets the first element of 'args' as a direction, and throws on error: 87 | // returns '+' or '-'. 88 | function shiftDirection(args) { 89 | console.assert(Array.isArray(args)); 90 | if (args.length === 0) { 91 | throw new Error("Direction missing."); 92 | } 93 | if (!(args[0] === '+' || args[0] === '-')) { 94 | throw new Error("Direction should be '+' or '-'."); 95 | } 96 | let dir = args.shift(); 97 | return dir; 98 | } 99 | 100 | //shiftBedNeedle interprets the first one or two arguments of 'args' as a bed+needle number, and throws on error: 101 | // returns a {bed:, needle:} object. 102 | 103 | const BedNeedleRegex = /^([fb]s?)(-?\d+)$/; 104 | const BedRegex = /^[fb]s?$/; 105 | function shiftBedNeedle(args) { 106 | console.assert(Array.isArray(args)); 107 | if (args.length === 0) { 108 | throw new Error("Needle missing."); 109 | } 110 | let bed, needle; 111 | //case: bed string, needle number: 112 | if (typeof(args[0]) === 'string' && BedRegex.test(args[0])) { 113 | if (args.length < 2 || !Number.isInteger(args[1])) { 114 | throw new Error("Expecting bed name to be followed by a needle number."); 115 | } 116 | bed = args.shift(); 117 | needle = args.shift(); 118 | //case: single string "f12", "b-2", "bs66": 119 | } else if (typeof(args[0]) === 'string') { 120 | let m = args[0].match(BedNeedleRegex); 121 | if (m === null) { 122 | throw new Error("String '" + args[0] + "' does not look like a compound bed+needle string."); 123 | } 124 | bed = m[1]; 125 | needle = parseInt(m[2]); 126 | args.shift(); 127 | //case: two-member array ["fs", 2] 128 | } else if (Array.isArray(args[0])) { 129 | if (!( args[0].length === 2 && typeof(args[0][0]) === 'string' && BedRegex.test(args[0][0]) && Number.isInteger(args[0][1]) )) { 130 | throw new Error("Bed+needle array should look like [\"f\", 12]."); 131 | } 132 | bed = args[0][0]; 133 | needle = args[0][1]; 134 | args.shift(); 135 | //case: object {bed:"fs", needle:5} 136 | } else if (typeof(args[0]) === 'object') { 137 | if (!( 'bed' in args[0] && typeof(args[0].bed) === 'string' && BedRegex.test(args[0].bed) )) { 138 | throw new Error("Bed+needle object should have a 'bed' member string naming the bed."); 139 | } 140 | if (!( 'needle' in args[0] && Number.isInteger(args[0].needle) )) { 141 | throw new Error("Bed+needle object should have a 'needle' member integer."); 142 | } 143 | bed = args[0].bed; 144 | needle = args[0].needle; 145 | args.shift(); 146 | } else { 147 | throw new Error("Expecting bed+needle as name+number (\"fs\", 6), string (\"b-2\"), array ([\"f\", 6]), or object ({bed:\"bs\", needle:12}). Got '" + JSON.stringify(args) + "'"); 148 | } 149 | return {bed:bed, needle:needle}; 150 | } 151 | 152 | //shiftCarrierSet interprets the remaining contents of 'args' as an array of carrier names, and throws on error: 153 | // returns an array of carrier names, e.g., ["C", "A"]. 154 | function shiftCarrierSet(args, carrierNames) { 155 | let carrierSet = []; 156 | //carrier set as array, e.g., knit(..., ["A", "B"]): 157 | if (args.length === 1 && Array.isArray(args[0])) { 158 | carrierSet = args.shift().slice(); 159 | } else { 160 | //carrier set as parameters, e.g., knit(..., "A", "B"); 161 | carrierSet = args.splice(0,args.length).slice(); 162 | } 163 | 164 | // slightly ugly handling of various ways of typeing "A B", "A, B" 165 | carrierSet.forEach(function(name, idx){ 166 | let space_split = name.split(" "); 167 | let first = true; 168 | space_split.forEach(function(s, sidx){ 169 | if(s == '') return; 170 | if(first){ 171 | carrierSet[idx] = s; 172 | first = false; 173 | } 174 | else carrierSet.push(s); 175 | }); 176 | }); 177 | 178 | carrierSet.forEach(function(name, idx){ 179 | let comma_split = name.split(","); 180 | let first = true; 181 | comma_split.forEach(function(s, sidx){ 182 | if(s =='') return; 183 | if(first) { 184 | carrierSet[idx] = s; 185 | first = false; 186 | } 187 | else carrierSet.push(s); 188 | }); 189 | 190 | }); 191 | 192 | 193 | carrierSet.forEach(function(name){ 194 | if (carrierNames.indexOf(name) === -1) { 195 | throw new Error("Invalid carrier name '" + name + "'"); 196 | } 197 | }); 198 | 199 | return carrierSet; 200 | } 201 | 202 | Writer.prototype.in = function(...args){ 203 | 204 | let cs = shiftCarrierSet(args, this._carriers); 205 | if (cs.length === 0) { 206 | throw new Error("It doesn't make sense to 'in' on an empty carrier set."); 207 | } 208 | cs.forEach(function(cn){ 209 | if (cn in this.carriers) { 210 | throw new Error("Carrier '" + cn + "' is already in."); 211 | } 212 | this.carriers[cn] = {hook:false}; 213 | }, this); 214 | 215 | this._operations.push('in ' + cs.join(' ')); 216 | 217 | }; 218 | 219 | Writer.prototype.inhook = function(...args){ 220 | 221 | let cs = shiftCarrierSet(args, this._carriers); 222 | if (cs.length === 0) { 223 | throw new Error("It doesn't make sense to 'inhook' on an empty carrier set."); 224 | } 225 | cs.forEach(function(cn){ 226 | if (cn in this.carriers) { 227 | throw new Error("Carrier '" + cn + "' is already in."); 228 | } 229 | this.carriers[cn] = {hook:true}; 230 | }, this); 231 | 232 | this._operations.push('inhook ' + cs.join(' ')); 233 | 234 | }; 235 | 236 | 237 | Writer.prototype.releasehook = function(...args){ 238 | 239 | let cs = shiftCarrierSet(args, this._carriers); 240 | if (cs.length === 0) { 241 | throw new Error("It doesn't make sense to 'releasehook' on an empty carrier set."); 242 | } 243 | cs.forEach(function(cn){ 244 | if (!(cn in this.carriers)) { 245 | throw new Error("Carrier '" + cn + "' isn't in."); 246 | } 247 | if (!this.carriers[cn].hook) { 248 | throw new Error("Carrier '" + cn + "' isn't in the hook."); 249 | } 250 | this.carriers[cn].hook = false; 251 | }, this); 252 | 253 | this._operations.push('releasehook ' + cs.join(' ')); 254 | 255 | }; 256 | 257 | Writer.prototype.out = function(...args){ 258 | 259 | let cs = shiftCarrierSet(args, this._carriers); 260 | if (cs.length === 0) { 261 | throw new Error("It doesn't make sense to 'out' on an empty carrier set."); 262 | } 263 | cs.forEach(function(cn){ 264 | if (!(cn in this.carriers)) { 265 | throw new Error("Carrier '" + cn + "' isn't in."); 266 | } 267 | delete this.carriers[cn]; 268 | }, this); 269 | 270 | this._operations.push('out ' + cs.join(' ')); 271 | 272 | }; 273 | 274 | Writer.prototype.outhook = function(...args){ 275 | 276 | let cs = shiftCarrierSet(args, this._carriers); 277 | if (cs.length === 0) { 278 | throw new Error("It doesn't make sense to 'outhook' on an empty carrier set."); 279 | } 280 | cs.forEach(function(cn){ 281 | if (!(cn in this.carriers)) { 282 | throw new Error("Carrier '" + cn + "' isn't in."); 283 | } 284 | delete this.carriers[cn]; 285 | }, this); 286 | 287 | this._operations.push('outhook ' + cs.join(' ')); 288 | }; 289 | 290 | function isFiniteNumber( n ) { 291 | if (typeof(n) === 'number' && Number.isFinite(n) && !Number.isNaN(n)) return true; 292 | return false; 293 | } 294 | 295 | Writer.prototype.stitch = function(before, after) { 296 | if (!(isFiniteNumber(before) && isFiniteNumber(after))) { 297 | throw new Error("Stitch L and T values must be finite numbers."); 298 | } 299 | 300 | this._operations.push('stitch ' + before.toString() + ' ' + after.toString()); 301 | }; 302 | 303 | //throw warning if ;;Machine: header is included & machine doesn't support extension 304 | function machineSupport(extension, supported) { 305 | if (!machine.toUpperCase().includes(supported)) console.warn(`Warning: ${extension} is not supported on ${machine}. Including it anyway.`); 306 | } 307 | 308 | // --- extensions --- 309 | Writer.prototype.stitchNumber = function (stitchNumber) { 310 | if (!(Number.isInteger(stitchNumber) && stitchNumber >= 0)) { 311 | throw new Error("Stitch numbers are non-negative integer values."); 312 | } 313 | 314 | this._operations.push('x-stitch-number ' + stitchNumber.toString()); 315 | }; 316 | 317 | Writer.prototype.fabricPresser = function (presserMode) { 318 | machineSupport('presser mode', 'SWG'); 319 | if(presserMode === 'auto'){ 320 | this._operations.push('x-presser-mode auto'); 321 | } 322 | else if(presserMode === 'on'){ 323 | this._operations.push('x-presser-mode on'); 324 | } 325 | else if(presserMode === 'off'){ 326 | this._operations.push('x-presser-mode off'); 327 | } 328 | else{ 329 | console.warn('Ignoring presser mode extension, unknown mode ' + presserMode + '. Valid modes: on, off, auto'); 330 | } 331 | } 332 | 333 | Writer.prototype.visColor = function (hex, carrier) { 334 | let warning = false; 335 | if (arguments.length !== 2) { 336 | warning = true; 337 | console.warn(`Ignoring vis color extension, since it is meant to take 2 parameters: 1) #hexColorCode and 2) carrierNumber.`); 338 | } 339 | if (hex.charAt(0) !== '#') { 340 | warning = true; 341 | console.warn(`Ignoring vis color extension, since the first arg is meant to be a hex code to assign the given carrier. Expected e.g. #FF0000`); 342 | } 343 | if (this._carriers.indexOf(carrier) === -1) { 344 | warning = true; 345 | console.warn(`Ignoring vis color extension, since the second arg is meant to be the carrier number to which you are assigning the color. ${carrier} is not listed in the 'Carriers' header.`); 346 | } 347 | if (!warning) this._operations.push(`x-vis-color ${hex} ${carrier}`); 348 | } 349 | 350 | Writer.prototype.speedNumber = function (value) { 351 | //TODO: check to make sure it's within the accepted range 352 | if (!(Number.isInteger(value) && value >= 0)) { 353 | console.warn(`Ignoring speed number extension, since provided value: ${value} is not a non-negative integer.`); 354 | } else this._operations.push(`x-speed-number ${value}`); 355 | }; 356 | 357 | Writer.prototype.rollerAdvance = function (value) { 358 | machineSupport('roller advance', 'KNITERATE'); 359 | //TODO: check to make sure it's within the accepted range 360 | if (!Number.isInteger(value)) { 361 | console.warn(`Ignoring roller advance extension, since provided value: ${value} is not an integer.`); 362 | } else this._operations.push(`x-speed-number ${value}`); 363 | let warning = false; 364 | if (!warning) this._operations.push(`x-roller-advance ${value}`); 365 | }; 366 | 367 | Writer.prototype.addRollerAdvance = function (value) { 368 | machineSupport('add roller advance', 'KNITERATE'); 369 | //TODO: check to make sure it's within the accepted range 370 | if (!Number.isInteger(value)) { 371 | console.warn(`Ignoring add roller advance extension, since provided value: ${value} is not an integer.`); 372 | } else this._operations.push(`x-add-roller-advance ${value}`); 373 | }; 374 | 375 | Writer.prototype.carrierSpacing = function (value) { 376 | machineSupport('carrier spacing', 'KNITERATE'); 377 | if (!(Number.isInteger(value) && value > 0)) { 378 | console.warn(`Ignoring carrier spacing extension, since provided value: ${value} is not a positive integer.`); 379 | } else this._operations.push(`x-carrier-spacing ${value}`); 380 | }; 381 | 382 | Writer.prototype.carrierStoppingDistance = function (value) { 383 | machineSupport('carrier stopping distance', 'KNITERATE'); 384 | if (!(Number.isInteger(value) && value > 0)) { 385 | console.warn(`Ignoring carrier stopping distance extension, since provided value: ${value} is not a positive integer.`); 386 | } else this._operations.push(`x-carrier-stopping-distance ${value}`); 387 | }; 388 | 389 | // --- operations ---// 390 | Writer.prototype.rack = function(rack) { 391 | 392 | if (!(isFiniteNumber(rack))) { 393 | throw new Error("Racking values must be finite numbers."); 394 | } 395 | 396 | this.racking = rack; 397 | 398 | this._operations.push('rack ' + rack.toString()); 399 | }; 400 | 401 | Writer.prototype.knit = function(...args) { 402 | let dir = shiftDirection(args); 403 | let bn = shiftBedNeedle(args); 404 | let cs = shiftCarrierSet(args, this._carriers); 405 | 406 | if (cs.length > 0) { 407 | this.needles[bn.bed + bn.needle.toString()] = true; 408 | } else { 409 | delete this.needles[bn.bed + bn.needle.toString()]; 410 | } 411 | 412 | this._operations.push('knit ' + dir + ' ' + bn.bed + bn.needle.toString() + ' ' + cs.join(' ')); 413 | }; 414 | 415 | Writer.prototype.tuck = function(...args) { 416 | let dir = shiftDirection(args); 417 | let bn = shiftBedNeedle(args); 418 | let cs = shiftCarrierSet(args, this._carriers); 419 | 420 | this.needles[bn.bed + bn.needle.toString()] = true; 421 | 422 | this._operations.push('tuck ' + dir + ' ' + bn.bed + bn.needle.toString() + ' ' + cs.join(' ')); 423 | }; 424 | 425 | Writer.prototype.split = function(...args) { 426 | let dir = shiftDirection(args); 427 | let from = shiftBedNeedle(args); 428 | let to = shiftBedNeedle(args); 429 | let cs = shiftCarrierSet(args, this._carriers); 430 | 431 | if ((from.bed + from.needle.toString()) in this.needles) { 432 | this.needles[to.bed + to.needle.toString()] = true; 433 | delete this.needles[from.bed + from.needle.toString()]; 434 | } 435 | if (cs.length > 0) { 436 | this.needles[from.bed + from.needle.toString()] = true; 437 | } 438 | 439 | this._operations.push('split ' + dir + ' ' + from.bed + from.needle.toString() + ' ' + to.bed + to.needle.toString() + ' ' + cs.join(' ')); 440 | }; 441 | 442 | Writer.prototype.miss = function(...args) { 443 | let dir = shiftDirection(args); 444 | let bn = shiftBedNeedle(args); 445 | let cs = shiftCarrierSet(args, this._carriers); 446 | 447 | if (cs.length === 0) { 448 | throw new Error("It doesn't make sense to miss with no carriers."); 449 | } 450 | 451 | this._operations.push('miss ' + dir + ' ' + bn.bed + bn.needle.toString() + ' ' + cs.join(' ')); 452 | }; 453 | 454 | 455 | // drop -> knit without yarn, but supported in knitout 456 | Writer.prototype.drop = function(...args) { 457 | let bn = shiftBedNeedle(args); 458 | 459 | if (args.length !== 0) { 460 | throw new Error("drop only takes a bed+needle"); 461 | } 462 | 463 | delete this.needles[bn.bed + bn.needle.toString()]; 464 | 465 | this._operations.push('drop ' + bn.bed + bn.needle.toString()); 466 | }; 467 | 468 | // amiss -> tuck without yarn, but supported in knitout 469 | Writer.prototype.amiss = function(...args) { 470 | let bn = shiftBedNeedle(args); 471 | 472 | if (args.length !== 0) { 473 | throw new Error("amiss only takes a bed+needle"); 474 | } 475 | 476 | this._operations.push('amiss ' + bn.bed + bn.needle.toString()); 477 | }; 478 | 479 | // xfer -> split without yarn, but supported in knitout 480 | Writer.prototype.xfer = function(...args) { 481 | 482 | let from = shiftBedNeedle(args); 483 | let to = shiftBedNeedle(args); 484 | 485 | if (args.length !== 0) { 486 | throw new Error("xfer only takes two bed+needles"); 487 | } 488 | 489 | if ((from.bed + from.needle.toString()) in this.needles) { 490 | this.needles[to.bed + to.needle.toString()] = true; 491 | delete this.needles[from.bed + from.needle.toString()]; 492 | } 493 | 494 | this._operations.push('xfer ' + from.bed + from.needle.toString() + ' ' + to.bed + to.needle.toString()); 495 | }; 496 | 497 | // add comments to knitout 498 | Writer.prototype.comment = function( str ){ 499 | 500 | let multi = str.split('\n'); 501 | multi.forEach(function(entry){ 502 | // cannot add header comments with comment 503 | while(entry.startsWith(';')){ 504 | console.warn('Warning: comment starts with ; use addHeader for adding header comments.'); 505 | entry = entry.substr(1, entry.length); 506 | } 507 | this._operations.push(';' + entry.toString()); 508 | }, this); 509 | }; 510 | 511 | Writer.prototype.pause = function(comment){ 512 | // deals with multi-line comments 513 | this.comment(comment); 514 | this._operations.push('pause'); 515 | }; 516 | 517 | Writer.prototype.write = function(filename){ 518 | let version = ';!knitout-2'; 519 | let content = version + '\n' + 520 | this._headers.join('\n') + '\n' + 521 | this._operations.join('\n'); 522 | if (typeof(filename) === 'undefined') { 523 | console.warn("filename not passed to Writer.write; writing to stdout."); 524 | console.log(content); 525 | } else { 526 | try{ 527 | let fs = require('fs'); 528 | fs.writeFileSync(filename, content + '\n'); //default is utf8 529 | } 530 | catch(e){ 531 | console.warn("Can't load 'fs'. Did not write file."); 532 | } 533 | } 534 | return content; 535 | }; 536 | 537 | // browser-compatibility 538 | if(typeof(module) !== 'undefined'){ 539 | module.exports.Writer = Writer; 540 | } 541 | //=========== end of knitout.js ============= 542 | 543 | var knitoutContent = ''; 544 | 545 | //will incrementally store Writer outputs here so partial result can be shown on crash: 546 | let _headers = []; 547 | let _operations = []; 548 | 549 | //returns lineNumber of the code that generated this knitout operation 550 | //only applies for tuck, knit, xfer, split, and miss 551 | //line number,l, will be followed at the end in format: ;l 552 | //note: don't think frontend supports in line comment anyway? so no conflicts? 553 | function findLineNumber(depth = 3){ 554 | let err = new Error(); 555 | let stack = err.stack.split(/\n/); 556 | 557 | //NOTE: if these stop working, could just walk stack to first match 558 | 559 | //Chrome: 560 | let m = stack[depth + 1].match(/:(\d+):(\d+)\)$/); 561 | if (m) { 562 | let line = parseInt(m[1]); 563 | return ' ;!source:'+(line-2); 564 | } 565 | 566 | //Firefox: 567 | m = stack[depth].match(/Function:(\d+):(\d+)$/); 568 | if (m) { 569 | let line = parseInt(m[1]); 570 | return ' ;!source:'+(line-2); 571 | } 572 | 573 | console.log(stack); //DEBUG 574 | 575 | //unknown 576 | return ';!source:' + (-1); 577 | } 578 | 579 | //patch Writer to grab operations / headers (maybe?) 580 | class NewWriter extends Writer { 581 | constructor(opts) { 582 | super(opts); 583 | this._operations.oldPush = this._operations.push; 584 | this._operations.push = function (...args) { 585 | const line = findLineNumber(); 586 | for (const arg of args) { 587 | _operations.push(`${arg}${line}`); 588 | this.oldPush(`${arg}${line}`); 589 | } 590 | }; 591 | _operations.push(...this._operations); //anything already in the array (should be nothing!) 592 | 593 | this._headers.oldPush = this._headers.push; 594 | this._headers.push = function (...args) { 595 | _headers.push(...args); 596 | this.oldPush(...args); 597 | }; 598 | _headers.push(...this._headers); //anything already in the array (carriers header!) 599 | } 600 | } 601 | 602 | function require(path){ 603 | if (path.match("knitout$")) { 604 | return {Writer:NewWriter}; 605 | } 606 | if (path === 'fs') { 607 | return { 608 | writeFileSync:(fn, content) => { 609 | console.log(`Would write '${fn}'.`); 610 | knitoutContent = content; 611 | } 612 | } 613 | } 614 | } 615 | 616 | let consoleContent = []; 617 | 618 | let captureConsole = { 619 | log: function() { 620 | //console.log.apply(console, arguments); 621 | let str = ''; 622 | for (let i = 0; i < arguments.length; ++i) { 623 | if (i !== 0) str += ' '; 624 | str += '' + arguments[i]; 625 | } 626 | //comment-only lines (e.g. comment header, magic number) get preserved: 627 | if (str.match(/^\s*;/)) { 628 | consoleContent.push(str); 629 | } else { 630 | //other lines get comments removed, and ;!source: directive added: 631 | let i = str.indexOf(';'); 632 | if (i !== -1) { 633 | str = str.substr(0,i); 634 | } 635 | consoleContent.push(str + findLineNumber(2)); 636 | } 637 | }, 638 | warn: function() { console.warn.apply(console, arguments); }, 639 | assert: function() { console.assert.apply(console, arguments); }, 640 | error: function() { console.error.apply(console, arguments); } 641 | }; 642 | 643 | if (codeText.startsWith('#!')) { 644 | codeText = '//' + codeText; 645 | } 646 | 647 | try { 648 | const run = new Function('require', 'console', codeText); 649 | run(require, captureConsole); 650 | } catch (e) { 651 | console.error(`Code failed to run fully:\n ${e}`); 652 | } 653 | 654 | if (knitoutContent === '') { 655 | if (_headers.length > 0 || _operations.length > 0) { 656 | knitoutContent = ';!knitout-2\n' + 657 | _headers.join('\n') + '\n' + 658 | _operations.join('\n') + '\n'; 659 | 660 | } 661 | } 662 | if (knitoutContent === '') { 663 | knitoutContent = consoleContent.join('\n'); 664 | } 665 | 666 | return knitoutContent; 667 | } 668 | -------------------------------------------------------------------------------- /extract-vector-tiles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # 4 | # Based on code from Chesskoban (c) 2017-2019 Jim McCann; 5 | # this adapted-for-15-466 code is released into the public domain. 6 | # 7 | 8 | import xml.parsers.expat 9 | import re 10 | import math 11 | import sys 12 | 13 | insvg = 'vector-tiles.svg' 14 | jsname = 'VectorTilesLib.js' 15 | 16 | xmlparser = xml.parsers.expat.ParserCreate() 17 | 18 | def mul(A,B): 19 | if len(A) == 6 and len(B) == 6: 20 | return ( 21 | A[0] * B[0] + A[2] * B[1] + A[4] * 0.0, 22 | A[1] * B[0] + A[3] * B[1] + A[5] * 0.0, 23 | A[0] * B[2] + A[2] * B[3] + A[4] * 0.0, 24 | A[1] * B[2] + A[3] * B[3] + A[5] * 0.0, 25 | A[0] * B[4] + A[2] * B[5] + A[4] * 1.0, 26 | A[1] * B[4] + A[3] * B[5] + A[5] * 1.0 27 | ) 28 | elif len(A) == 6 and len(B) == 2: 29 | return ( 30 | A[0] * B[0] + A[2] * B[1] + A[4] * 1.0, 31 | A[1] * B[0] + A[3] * B[1] + A[5] * 1.0 32 | ) 33 | else: 34 | assert(False) #"Invalid multiply arg lengths " + str(len(A)) + " and " + str(len(B))) 35 | 36 | def inv(A): 37 | assert(len(A) == 6) 38 | a = A[0] 39 | b = A[2] 40 | c = A[1] 41 | d = A[3] 42 | x = A[4] 43 | y = A[5] 44 | 45 | invdet = 1.0 / (a*d - c*b) 46 | a *= invdet 47 | b *= invdet 48 | c *= invdet 49 | d *= invdet 50 | B = ( d, -c, -b, a, -(d * x + -b * y), -(-c * x + a * y) ) 51 | P = mul(A,B) 52 | #print("Matrix: " + repr(A) + " / Inverse: " + repr(B) + " / Product: " + repr(P)) 53 | return B 54 | 55 | def parse_transform(transform): 56 | #print('parsing: ' + transform) 57 | #transform is a list of zero or more commands 58 | wsp = '[\x20\x09\x0D\x0A]' 59 | comma_wsp = '[\x20\x09\x0D\x0A,]' 60 | number = r'[+-]?[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?' 61 | first = True 62 | xf = (1,0, 0,1, 0,0) 63 | while True: 64 | if first: ws = wsp + '*' 65 | else: ws = comma_wsp + '+' 66 | first = False 67 | m = re.match( 68 | ws 69 | + r'(matrix|translate|scale|rotate|skewX|skewY)' 70 | + wsp + '*' 71 | + r'\(' 72 | + wsp + '*' 73 | + '(' + number + '(:?' + comma_wsp + '+' + number + ')*' + ')' 74 | + wsp + '*' 75 | + r'\)' 76 | , transform) 77 | if m == None: break 78 | op = m.group(1) 79 | args = list(map(float, re.split(comma_wsp + '+', m.group(2)))) 80 | #print("op: " + op) 81 | #print("args: " + repr(args)) 82 | mat = (1,0, 0,1, 0,0) 83 | if op == 'matrix': 84 | if len(args) == 6: 85 | mat = tuple(args) 86 | else: 87 | print("WARNING: ignoring matrix with args " + repr(args) + " (non-length-6)") 88 | elif op == 'translate': 89 | if len(args) == 2: 90 | mat = (1,0, 0,1, args[0], args[1]) 91 | elif len(args) == 1: 92 | mat = (1,0, 0,1, args[0], 0) 93 | else: 94 | print("WARNING: ignoring translate with args " + repr(args) + " (non-length-6)") 95 | elif op == 'scale': 96 | if len(args) == 2: 97 | mat = (args[0],0, 0,args[1], 0,0) 98 | elif len(args) == 1: 99 | mat = (args[0],0, 0,args[0], 0,0) 100 | else: 101 | print("WARNING: ignoring scale with args " + repr(args) + " (non-length-6)") 102 | elif op == 'rotate': 103 | if len(args) == 3 or len(args) == 1: 104 | cos = math.cos(math.pi * args[0] / 180.0) 105 | sin = math.sin(math.pi * args[0] / 180.0) 106 | mat = (cos,sin, -sin,cos, 0,0) 107 | if len(args) == 3: 108 | mat = mul( (1,0, 0,1, args[1], args[2]), 109 | mul( mat, (1,0, 0,1, -args[1],-args[2]) ) ) 110 | else: 111 | print("WARNING: ignoring rotate with args " + repr(args) + " (non-length-6)") 112 | elif op == 'skewX': 113 | if len(args) == 1: 114 | tan = math.tan(math.pi * args[0] / 180.0) 115 | mat = (1,0, tan,1, 0,0) 116 | else: 117 | print("WARNING: ignoring skewX with args " + repr(args) + " (non-length-6)") 118 | elif op == 'skewY': 119 | if len(args) == 1: 120 | tan = math.tan(math.pi * args[0] / 180.0) 121 | mat = (1,tan, 0,1, 0,0) 122 | else: 123 | print("WARNING: ignoring skewY with args " + repr(args) + " (non-length-6)") 124 | 125 | else: 126 | assert(False) # "shouldn't have an op named '" + op + "'" 127 | 128 | xf = mul(xf, mat) 129 | 130 | transform = transform[m.end():] 131 | 132 | if not re.fullmatch(wsp + '*', transform): 133 | print("WARNING: ignoring trailing transform information '" + transform + "'") 134 | 135 | return xf 136 | 137 | def parse_path(data): 138 | idx = 0 139 | 140 | def peek(): 141 | nonlocal idx, data 142 | if idx < len(data): return data[idx] 143 | else: return 'EOF' 144 | 145 | def get(): 146 | nonlocal idx, data 147 | assert(idx < len(data)) 148 | idx += 1 149 | return data[idx-1] 150 | 151 | 152 | #divide path into command groups by implementing the grammar from the SVG spec: 153 | def skip_wsp_star(): 154 | while peek() in '\x20\x09\x0D\x0A': 155 | get() 156 | 157 | def read_number(): 158 | if peek() not in '+-0123456789.': return None 159 | num = '' 160 | if peek() in '+-': num += get() 161 | while peek() in '0123456789': num += get() 162 | if peek() == '.': 163 | num += get() 164 | while peek() in '0123456789': num += get() 165 | if peek() in 'eE': 166 | num += get() 167 | if peek() in '+-': num += get() 168 | while peek() in '0123456789': num += get() 169 | return float(num) 170 | 171 | def skip_comma_wsp(): 172 | skip_wsp_star() 173 | if peek() == ',': 174 | get() 175 | skip_wsp_star() 176 | 177 | def read_coordinate_pair(): 178 | x = read_number() 179 | if x == None: return None 180 | skip_comma_wsp() 181 | y = read_number() 182 | assert(y != None) 183 | return (x, y) 184 | 185 | def read_moveto(): 186 | cmd = get() 187 | assert(cmd in 'mM') 188 | skip_wsp_star() 189 | args = [] 190 | while True: 191 | pair = read_coordinate_pair() 192 | if pair == None: 193 | assert(len(args) > 0) 194 | break 195 | args.append(pair) 196 | skip_comma_wsp() 197 | return (cmd, args) 198 | 199 | def read_drawto(): 200 | if peek() not in 'ZzLlHhVvCcSsQqTtAa': return None 201 | cmd = get() 202 | args = [] 203 | if cmd in 'Zz': 204 | pass #no args 205 | elif cmd in 'LlTt': 206 | skip_wsp_star() 207 | while True: 208 | a = read_coordinate_pair() 209 | if a == None: 210 | assert(len(args) > 0) 211 | break 212 | args.append(a) 213 | skip_comma_wsp() 214 | elif cmd in 'HhVv': 215 | skip_wsp_star() 216 | while True: 217 | a = read_number() 218 | if a == None: 219 | assert(len(args) > 0) 220 | break 221 | args.append(a) 222 | skip_comma_wsp() 223 | elif cmd in 'Cc': 224 | skip_wsp_star() 225 | while True: 226 | a = read_coordinate_pair() 227 | if a == None: 228 | assert(len(args) > 0) 229 | break 230 | args.append(a) 231 | skip_comma_wsp() 232 | a = read_coordinate_pair() 233 | assert(a != None) 234 | args.append(a) 235 | skip_comma_wsp() 236 | a = read_coordinate_pair() 237 | assert(a != None) 238 | skip_comma_wsp() 239 | args.append(a) 240 | elif cmd in 'SsQq': 241 | skip_wsp_star() 242 | while True: 243 | a = read_coordinate_pair() 244 | if a == None: 245 | assert(len(args) > 0) 246 | break 247 | args.append(a) 248 | skip_comma_wsp() 249 | a = read_coordinate_pair() 250 | assert(a != None) 251 | args.append(a) 252 | elif cmd in 'Aa': 253 | skip_wsp_star() 254 | while True: 255 | rx = read_nonnegative_number() 256 | if rx == None: 257 | assert(len(args) > 0) 258 | break 259 | skip_comma_wsp() 260 | 261 | ry = read_nonnegative_number() 262 | assert(ry != None) 263 | skip_comma_wsp() 264 | 265 | x_axis_rotation = read_number() 266 | assert(x_axis_rotation != None) 267 | skip_comma_wsp() 268 | 269 | large_arc_flag = read_flag() 270 | assert(large_arc_flag != None) 271 | skip_comma_wsp() 272 | 273 | sweep_flag = read_flag() 274 | assert(sweep_flag != None) 275 | skip_comma_wsp() 276 | 277 | x = read_number() 278 | assert(x != None) 279 | skip_comma_wsp() 280 | 281 | y = read_number() 282 | assert(y != None) 283 | skip_comma_wsp() 284 | args.append((rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y)) 285 | else: 286 | assert(False) 287 | return (cmd, args) 288 | 289 | 290 | cmds = [] 291 | while True: 292 | skip_wsp_star() 293 | if peek() == 'EOF': break 294 | moveto = read_moveto() 295 | assert(moveto) 296 | cmds.append(moveto) 297 | while True: 298 | skip_wsp_star() 299 | drawto = read_drawto() 300 | if drawto == None: break 301 | cmds.append(drawto) 302 | return cmds 303 | 304 | tiles = dict() 305 | 306 | class Tile: 307 | def __init__(self, name): 308 | self.accum = dict() 309 | self.accum['y1'] = [] 310 | self.accum['y2'] = [] 311 | self.accum['l1'] = [] 312 | self.accum['l2'] = [] 313 | self.accum['a1'] = [] 314 | self.accum['a2'] = [] 315 | self.rect_xf = (1,0, 0,1, 0,0) 316 | self.rect = None 317 | self.name = name 318 | 319 | xf_stack = [(1,0, 0,1, 0,0)] 320 | tile_stack = [None] 321 | accum_stack = [None] 322 | 323 | def start_element(name, attrs): 324 | #handle transform: 325 | xf = (1,0, 0,1, 0,0) 326 | if 'transform' in attrs: xf = parse_transform(attrs['transform']) 327 | xf_stack.append(mul(xf_stack[-1], xf)) 328 | 329 | #handle label: 330 | label = '' 331 | if 'inkscape:label' in attrs: label = attrs['inkscape:label'] 332 | 333 | accum = accum_stack[-1] 334 | tile = tile_stack[-1] 335 | 336 | if label.startswith('tile:'): 337 | if tile != None: print("WARNING: nested tiles.") 338 | #start a new tile: 339 | tile = Tile(label[len('tile:'):]) 340 | accum = tile.accum 341 | elif tile and name == 'rect': 342 | #switch to box accumulator: 343 | if tile.rect != None: print("WARNING: multiple rects in tile.") 344 | x = float(attrs['x']) 345 | y = float(attrs['y']) 346 | width = float(attrs['width']) 347 | height = float(attrs['height']) 348 | tile.rect = (x, y, width, height) 349 | tile.rect_xf = xf_stack[-1] 350 | 351 | accum_stack.append(accum) 352 | tile_stack.append(tile) 353 | 354 | d = None 355 | target = None 356 | if name == 'path' and 'd' in attrs: 357 | d = attrs['d'] 358 | style = attrs['style'] 359 | m = re.match(r'(|.*;)\s*stroke:\s*([^;\s]+)\s*;', style) 360 | col = m[2] 361 | if col == '#ff0000': target = 'l1' 362 | elif col == '#00ff00': target = 'l2' 363 | elif col == '#800000': target = 'y1' 364 | elif col == '#008000': target = 'y2' 365 | elif col == '#ff00ff': target = 'a1' 366 | elif col == '#00ffff': target = 'a2' 367 | else: print("Unknown target for color: " + col + "!") 368 | 369 | if d != None and accum != None: 370 | #print(d) 371 | accum[target].append( (xf_stack[-1], parse_path(d) ) ) 372 | 373 | def end_element(name): 374 | tile = tile_stack[-1] 375 | xf_stack.pop() 376 | accum_stack.pop() 377 | tile_stack.pop() 378 | 379 | if tile != None and tile_stack[-1] == None: 380 | if len(tile.accum["l1"]) == 0 and len(tile.accum["l2"]) == 0: pass #tile.name += "-l0" 381 | elif len(tile.accum["l2"]) == 0: tile.name += "-l1" 382 | else: tile.name += "-l2" 383 | 384 | if len(tile.accum["y1"]) == 0 and len(tile.accum["y2"]) == 0: pass #tile.name += "-y0" 385 | elif len(tile.accum["y2"]) == 0: tile.name += "-y1" 386 | else: tile.name += "-y2" 387 | 388 | if len(tile.accum["a1"]) == 0 and len(tile.accum["a2"]) == 0: pass #tile.name += "-y0" 389 | elif len(tile.accum["a2"]) == 0: tile.name += "-a1" 390 | else: tile.name += "-a2" 391 | 392 | 393 | assert(tile.name not in tiles) 394 | tiles[tile.name] = tile 395 | 396 | 397 | xmlparser.StartElementHandler = start_element 398 | xmlparser.EndElementHandler = end_element 399 | 400 | xmlparser.ParseFile(open(insvg, 'rb')) 401 | 402 | 403 | def cmds_to_lines(xf, cmds): 404 | lines = [] 405 | def approx_bezier(a,b,c,d): 406 | nonlocal lines 407 | perp = (d[1] - a[1], -(d[0] - a[0])) 408 | 409 | amt = max( 410 | abs(perp[0] * (b[0]-a[0]) + perp[1] * (b[1]-a[1])), 411 | abs(perp[0] * (c[0]-a[0]) + perp[1] * (c[1]-a[1])) 412 | ) / math.sqrt(perp[0]*perp[0] + perp[1]*perp[1]) 413 | if amt > 0.02: 414 | ab = (0.5 * (a[0] + b[0]), 0.5 * (a[1] + b[1])) 415 | bc = (0.5 * (b[0] + c[0]), 0.5 * (b[1] + c[1])) 416 | cd = (0.5 * (c[0] + d[0]), 0.5 * (c[1] + d[1])) 417 | abc = (0.5 * (ab[0] + bc[0]), 0.5 * (ab[1] + bc[1])) 418 | bcd = (0.5 * (bc[0] + cd[0]), 0.5 * (bc[1] + cd[1])) 419 | abcd = (0.5 * (abc[0] + bcd[0]), 0.5 * (abc[1] + bcd[1])) 420 | approx_bezier(a, ab, abc, abcd) 421 | approx_bezier(abcd, bcd, cd, d) 422 | else: 423 | lines += [a,b, b,c, c,d] 424 | 425 | start = None 426 | prev = (0.0, 0.0) 427 | prev_cp = (0.0, 0.0) 428 | for ca in cmds: 429 | cmd = ca[0] 430 | args = ca[1] 431 | 432 | if cmd in 'Mm': 433 | start = None 434 | for pt in args: 435 | if cmd == 'm': pt = (prev[0] + pt[0], prev[1] + pt[1]) 436 | if start == None: 437 | start = pt 438 | prev_cp = prev = pt 439 | if prev != pt: lines += [mul(xf, prev), mul(xf, pt)] 440 | prev_cp = prev = pt 441 | elif cmd in 'Hh': 442 | for x in args: 443 | if cmd == 'h': pt = (prev[0] + x, prev[1]) 444 | else: pt = (x, prev[1]) 445 | if prev != pt: lines += [mul(xf, prev), mul(xf, pt)] 446 | prev_cp = prev = pt 447 | elif cmd in 'Vv': 448 | for y in args: 449 | if cmd == 'v': pt = (prev[0], prev[1] + y) 450 | else: pt = (prev[0], y) 451 | if prev != pt: lines += [mul(xf, prev), mul(xf, pt)] 452 | prev_cp = prev = pt 453 | 454 | elif cmd in 'Ll': 455 | for pt in args: 456 | if cmd == 'l': pt = (prev[0] + pt[0], prev[1] + pt[1]) 457 | if prev != pt: lines += [mul(xf, prev), mul(xf, pt)] 458 | prev_cp = prev = pt 459 | elif cmd in 'Cc': 460 | for i in range(0, len(args), 3): 461 | p1 = args[i] 462 | p2 = args[i+1] 463 | p3 = args[i+2] 464 | if cmd == 'c': 465 | p1 = (prev[0] + p1[0], prev[1] + p1[1]) 466 | p2 = (prev[0] + p2[0], prev[1] + p2[1]) 467 | p3 = (prev[0] + p3[0], prev[1] + p3[1]) 468 | 469 | approx_bezier(mul(xf, prev), mul(xf, p1), mul(xf, p2), mul(xf, p3)) 470 | 471 | prev_cp = p2 472 | prev = p3 473 | 474 | elif cmd in 'Zz': 475 | pt = start 476 | if prev != pt: lines += [mul(xf, prev), mul(xf, pt)] 477 | prev = pt 478 | else: 479 | print("Skipping unimplemented command '" + cmd + "'") 480 | return lines 481 | 482 | def accum_to_lines(accum, outer_xf): 483 | lines = [] 484 | for xc in accum: 485 | xf = xc[0] 486 | cmds = xc[1] 487 | lines += cmds_to_lines(mul(outer_xf, xf), cmds) 488 | return lines 489 | 490 | tile_size = (13.0, 9.0) 491 | 492 | out = 'window.VectorTilesLib = {' 493 | 494 | first_tile = True 495 | for (name, tile) in tiles.items(): 496 | if first_tile: first_tile = False 497 | else: out += ',\n' 498 | out += '"' + tile.name + '":{\n' 499 | 500 | #find rectangle (in page coordinate system): 501 | inf = float('inf') 502 | rect_min = (inf, inf) 503 | rect_max = (-inf, -inf) 504 | for x in [tile.rect[0], tile.rect[0] + tile.rect[2]]: 505 | for y in [tile.rect[1], tile.rect[1] + tile.rect[3]]: 506 | pt = mul(tile.rect_xf, (x,y)) 507 | rect_min = (min(rect_min[0], pt[0]), min(rect_min[1], pt[1])) 508 | rect_max = (max(rect_max[0], pt[0]), max(rect_max[1], pt[1])) 509 | 510 | #print(" rect_xf is " + str(tile.rect_xf)) 511 | #print(" rect is " + str(rect_min) + " to " + str(rect_max)) 512 | 513 | scale = ( 514 | tile_size[0] / (rect_max[0] - rect_min[0]), 515 | tile_size[1] / (rect_max[1] - rect_min[1]) 516 | ) 517 | 518 | world_to_tile_xf = (scale[0],0, 0,-scale[1], -rect_min[0],rect_max[1]) 519 | 520 | #print(" rect min xforms to " + str(mul(world_to_tile_xf, rect_min))) 521 | #print(" rect max xforms to " + str(mul(world_to_tile_xf, rect_max))) 522 | 523 | first_label = True 524 | for (label, acc) in tile.accum.items(): 525 | if len(acc) == 0: continue 526 | if first_label: first_label = False 527 | else: out += ',\n' 528 | out += '\t"' + label + '":[\n' 529 | for (xf, cmds) in acc: 530 | lines = cmds_to_lines( mul( world_to_tile_xf, xf ), cmds ) 531 | assert(len(lines) % 2 == 0) 532 | chains = [] 533 | chain = None 534 | for i in range(0,len(lines), 2): 535 | def t(num): 536 | s = f"{num:.4f}" 537 | while s.endswith('0'): s = s[0:-1] 538 | if s.endswith('.'): s = s[0:-1] 539 | 540 | return s 541 | a = t(lines[i][0]) + ',' + t(lines[i][1]) 542 | b = t(lines[i+1][0]) + ',' + t(lines[i+1][1]) 543 | if chain == None or chain[len(chain)-1] != a: 544 | chain = [a,b] 545 | chains.append(chain) 546 | else: 547 | chain.append(b) 548 | first = True 549 | for chain in chains: 550 | if first: first = False 551 | else: out += ',\n' 552 | out += '\t\t[' + ', '.join(chain) + ']' 553 | out += '\n\t]' 554 | out += '\n}' 555 | 556 | out += '\n};' 557 | 558 | #print(out) 559 | 560 | with open(jsname, 'wb') as jsfile: 561 | jsfile.write(out.encode('utf8')) 562 | 563 | print("Wrote " + jsname) 564 | -------------------------------------------------------------------------------- /faces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/textiles-lab/knitout-live-visualizer/bb7038b216f548ffefe9d850df314f19c87e26d1/faces.png -------------------------------------------------------------------------------- /faces.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 28 | 32 | 36 | 40 | 44 | 48 | 49 | 52 | 56 | 60 | 61 | 64 | 68 | 72 | 76 | 77 | 80 | 84 | 88 | 92 | 93 | 102 | 111 | 120 | 130 | 140 | 150 | 159 | 169 | 179 | 189 | 199 | 209 | 219 | 229 | 238 | 248 | 257 | 258 | 277 | 282 | 283 | 285 | 286 | 288 | image/svg+xml 289 | 291 | 292 | 293 | 294 | 295 | 300 | 307 | 311 | 315 | 322 | 329 | 336 | 343 | 350 | 357 | 364 | 371 | 378 | 385 | 392 | 399 | 406 | 413 | 420 | 427 | 433 | 439 | knit-b 450 | 456 | 460 | 466 | 471 | 476 | 482 | 487 | knit-f 498 | 503 | 509 | 515 | 521 | 527 | tuck-b 538 | 544 | 550 | tuck-f 561 | 567 | 573 | 579 | 585 | 591 | tuck 602 | drop 613 | 619 | 625 | miss-b 636 | 641 | 646 | 652 | 658 | miss-f 669 | miss 680 | 685 | 691 | 697 | miss 708 | 709 | 710 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | visualizer.html -------------------------------------------------------------------------------- /knitout-to-mesh.js: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@" 3 | "use strict"; 4 | 5 | const CellMachine = require("./CellMachine.js").CellMachine; 6 | const parseKnitout = require("./parseKnitout.js").parseKnitout; 7 | const fs = require("fs"); 8 | 9 | //parse command line 10 | if (process.argv.length != 4) { 11 | console.error("Usage:\nknitout-to-mesh.js "); 12 | process.exitCode = 1; 13 | process.exit(1); 14 | return; 15 | } 16 | let knitoutFile = process.argv[2]; 17 | let objFile = process.argv[3]; 18 | 19 | console.log("Will process knitout from '" + knitoutFile + "' to generate mesh '" + objFile + "'."); 20 | 21 | //-------------------------------------- 22 | //load file, pass to parser: 23 | 24 | const machine = new CellMachine(); 25 | parseKnitout(fs.readFileSync(knitoutFile, 'utf8'), machine); 26 | 27 | machine.dump(); 28 | 29 | fs.writeFileSync(objFile, machine.dumpObj()); 30 | -------------------------------------------------------------------------------- /parseKnitout.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | //parseKnitout will parse knitout, catch syntax errors, and dispatch to calls on 'machine', an abstract knitting machine: 3 | function parseKnitout(codeText, machine, useKnitoutAsSource=false) { 4 | var errors = []; 5 | var warnings = []; 6 | 7 | var carrierNames = []; 8 | 9 | var inCommentHeader = true; 10 | var end = false; 11 | 12 | codeText.split("\n").forEach(function(line, lineNumber) { 13 | if (end) return; 14 | //console.log(lineNumber + ": " + line); //DEBUG 15 | function addError(info) { 16 | console.log("Parse Error on line " + lineNumber + ": " + info); 17 | errors.push({lineNumber:lineNumber, text:info}); 18 | } 19 | function addWarning(info) { 20 | console.log("Parse Warning on line " + lineNumber + ": " + info); 21 | warnings.push({lineNumber:lineNumber, text:info}); 22 | } 23 | 24 | //magic first line: 25 | if (lineNumber == 0) { 26 | //knitout must begin with ';!knitout-N'. 27 | var m = line.match(/^;!knitout-(\d+)$/); 28 | if (m !== null) { 29 | if (parseInt(m[1]) != 2) { 30 | addWarning("Parsed version (" + m.groups(1) + ") is not what was expected."); 31 | } 32 | } else { 33 | addError("Knitout should always start with a ';!knitout-2' line."); 34 | } 35 | //nothing more to do with the first line. 36 | return; 37 | } 38 | 39 | //comment header lines: 40 | var m = line.match(/^;;([^:]+): (.*)$/); 41 | if (m !== null) { 42 | if (!inCommentHeader) { 43 | addWarning("Comment-header-like line outside comment header."); 44 | } else { 45 | var name = m[1]; 46 | var value = m[2]; 47 | //console.log("Comment header: '" + name + "' is '" + value + "'."); 48 | if (name === "Carriers") { 49 | carrierNames = value.split(" "); 50 | machine.setCarriers(carrierNames); 51 | //console.log("Carrier names (front-to-back): ", carrierNames); 52 | } 53 | //TODO: handle other comment headers. 54 | return; //nothing left to do with this line. 55 | } 56 | } else { 57 | inCommentHeader = false; 58 | } 59 | 60 | //split line into op and comment parts: 61 | var m = line.match(/^([^;]*)(;.*)?$/); 62 | if (m === null) { 63 | console.log("Weird, our line regex should have gotten everything."); 64 | return; 65 | } 66 | var tokens = m[1].split(/[ \t]+/); 67 | var comment = m[2]; 68 | 69 | //handle !source: directive in comment 70 | if (comment && comment.startsWith(';!source:')) { 71 | if ('source' in machine) { 72 | machine.source(comment.substr(9)); 73 | } 74 | } else if (useKnitoutAsSource){ 75 | machine.source(lineNumber); 76 | } 77 | 78 | //trim leading/trailing whitespace from operation token list: 79 | if (tokens.length !== 0 && tokens[0] === "") tokens.shift(); 80 | if (tokens.length !== 0 && tokens[tokens.length-1] === "") tokens.pop(); 81 | 82 | if (tokens.length === 0) { 83 | //empty operation: nothing to do 84 | return; 85 | } 86 | 87 | var op = tokens.shift(); 88 | 89 | function parseCarrierSet(tokens) { 90 | //check that tokens are in carrierNames and aren't repeated: 91 | var usedAlready = {}; 92 | tokens.forEach(function(name){ 93 | if (carrierNames.indexOf(name) == -1) { 94 | throw "Carrier name does not appear in Carriers header."; 95 | } 96 | if (name in usedAlready) { 97 | throw "Carrier name appears twice in carrier set."; 98 | } 99 | usedAlready[name] = true; 100 | }); 101 | return tokens.slice(); 102 | } 103 | function parseStitchValue(token) { 104 | if (!/^[-+]?(\.\d+|\d+.\d*|\d+)$/.test(token)) { 105 | throw "Stitch value [" + token + "] must be a simple floating point value."; 106 | } 107 | return parseFloat(token); 108 | } 109 | function parseRackingValue(token) { 110 | if (!/^[-+]?(\.\d+|\d+.\d*|\d+)$/.test(token)) { 111 | throw "Racking value [" + token + "] must be a simple floating point value."; 112 | } 113 | return parseFloat(token); 114 | } 115 | function parseDirection(token) { 116 | if (!(token === '-' || token === '+')) { 117 | throw "Direction [" + token + "] must be '+' or '-'."; 118 | } 119 | return token; 120 | } 121 | function parseNeedle(token) { 122 | if (!/^([fb]s?)([-+]?\d+)$/.test(token)) throw "Needle [" + token + "] must be f,b,fs, or bs followed by an integer."; 123 | return token; 124 | } 125 | 126 | 127 | //dispatch all basic knitout functions to the machine, catch anything thrown and add to errors: 128 | try { 129 | //all the in/out/hook ops take a carrierset as an argument: 130 | if (["in", "out", "inhook", "releasehook", "outhook"].indexOf(op) !== -1) { 131 | var cs = parseCarrierSet(tokens); 132 | machine[op](cs); 133 | } else if (op === "stitch") { 134 | if (tokens.length !== 2) throw "stitch takes exactly two arguments"; 135 | var l = parseStitchValue(tokens[0]); 136 | var t = parseStitchValue(tokens[1]); 137 | machine.stitch(l, t); 138 | } else if (op === "rack") { 139 | if (tokens.length !== 1) throw "rack takes exactly one argument"; 140 | var r = parseRackingValue(tokens[0]); 141 | machine.rack(r); 142 | } else if (op === "knit" || op === "drop") { 143 | if (op === "drop") { 144 | if (tokens.length !== 1) throw "drop takes exactly one argument"; 145 | //interpret drop as "knit + N": 146 | tokens.unshift("+"); 147 | } 148 | if (tokens.length < 2) throw "knit requires at least two arguments"; 149 | var d = parseDirection(tokens.shift()); 150 | var n = parseNeedle(tokens.shift()); 151 | var cs = parseCarrierSet(tokens); 152 | machine.knit(d, n, cs); 153 | } else if (op === "tuck" || op === "amiss") { 154 | if (op === "amiss") { 155 | if (tokens.length !== 1) throw "amiss takes exactly one argument"; 156 | tokens.unshift("+"); 157 | } 158 | if (tokens.length < 2) throw "tuck requires at least two arguments"; 159 | var d = parseDirection(tokens.shift()); 160 | var n = parseNeedle(tokens.shift()); 161 | var cs = parseCarrierSet(tokens); 162 | machine.tuck(d, n, cs); 163 | } else if (op === "split" || op === "xfer") { 164 | if (op === "xfer") { 165 | if (tokens.length !== 2) throw "xfer takes exactly two arguments"; 166 | tokens.unshift("+"); 167 | } 168 | if (tokens.length < 3) throw "split requires at least three arguments"; 169 | var d = parseDirection(tokens.shift()); 170 | var n = parseNeedle(tokens.shift()); 171 | var n2 = parseNeedle(tokens.shift()); 172 | var cs = parseCarrierSet(tokens); 173 | machine.split(d, n, n2, cs); 174 | } else if (op === "miss") { 175 | if (tokens.length < 2) throw "miss requires at least two arguments"; 176 | var d = parseDirection(tokens.shift()); 177 | var n = parseNeedle(tokens.shift()); 178 | var cs = parseCarrierSet(tokens); 179 | machine.miss(d, n, cs); 180 | } else if (op === "pause") { 181 | if (tokens.length !== 0) throw "pause takes no arguments"; 182 | machine.pause(); 183 | } else if (op === "x-end") { 184 | end = true; 185 | } else if (op === "x-yarn-in") { 186 | //yarn carrier stack appears just before a given location 187 | if (tokens.length < 3) throw "x-yarn-in requires at least three arguments"; 188 | var d = parseDirection(tokens.shift()); 189 | var n = parseNeedle(tokens.shift()); 190 | var cs = parseCarrierSet(tokens); 191 | 192 | machine.yarnInEdge(d, n, cs); 193 | } else if (op === "x-yarn-out") { 194 | //yarn carrier stack vanishes just after a given location 195 | if (tokens.length < 3) throw "x-yarn-out requires at least three arguments"; 196 | var d = parseDirection(tokens.shift()); 197 | var n = parseNeedle(tokens.shift()); 198 | var cs = parseCarrierSet(tokens); 199 | 200 | machine.yarnOutEdge(d, n, cs); 201 | } else if (op === "x-loop-in") { 202 | //yarn loop appears at a given location, as if made by given carriers (but not connected) 203 | if (tokens.length < 3) throw "x-loop-in requires at least three arguments"; 204 | var d = parseDirection(tokens.shift()); 205 | var n = parseNeedle(tokens.shift()); 206 | var cs = parseCarrierSet(tokens); 207 | 208 | machine.loopInEdge(d, n, cs); 209 | } else if (op === "x-loop-out") { 210 | //yarn loop vanishes at a given location 211 | if (tokens.length !== 1) throw "x-loop-out requires one argument"; 212 | var n = parseNeedle(tokens.shift()); 213 | 214 | machine.loopOutEdge(n); 215 | } else if (op.startsWith("x-")) { 216 | if (op in machine) { 217 | machine[op](...tokens); 218 | } else { 219 | addWarning("Unrecognized extension operation '" + op + "'."); 220 | } 221 | } else { 222 | addError("Unrecognized operation."); 223 | } 224 | } catch (e) { 225 | if (typeof(e) === "string") { 226 | addError(e); 227 | } else { 228 | addError("[error that wasn't a string]"); 229 | throw e; 230 | //console.log(e); //DEBUG 231 | } 232 | } 233 | 234 | }); 235 | } 236 | 237 | if (typeof(module) !== "undefined") { 238 | module.exports.parseKnitout = parseKnitout; 239 | } 240 | -------------------------------------------------------------------------------- /show-knitout.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const TileSet = VectorTiles; 4 | 5 | const SHOW_FRONT = { front:1.0, back:0.0 }; 6 | const SHOW_BACK = { front:0.0, back:1.0 }; 7 | const SHOW_BOTH = { front:1.0, back:0.5 }; 8 | 9 | //make a canvas into a knitout visualizer: 10 | function ShowKnitout(canvas) { 11 | canvas.showKnitout = this; 12 | canvas.tabIndex = 0; 13 | 14 | this.canvas = canvas; 15 | this.ctx = canvas.getContext('2d'); 16 | 17 | //these are used to do stitch selection and view panning: 18 | this.clearDrawing(); 19 | 20 | this.show = SHOW_BOTH; 21 | 22 | this.camera = { 23 | x: 0.0, 24 | y: 0.0, 25 | radius: 10.0 //in terms of shortest axis 26 | }; 27 | 28 | //this.showTiles(); //DEBUG 29 | 30 | this.mouse = { 31 | x:NaN, 32 | y:NaN 33 | }; 34 | 35 | const me = this; 36 | canvas.addEventListener('mousemove', function(evt){ 37 | evt.preventDefault(); 38 | var rect = canvas.getBoundingClientRect(); 39 | let oldX = me.mouse.x; 40 | let oldY = me.mouse.y; 41 | me.mouse.x = ( (evt.clientX - rect.left) / rect.width * canvas.width ); 42 | me.mouse.y = ( (evt.clientY - rect.top) / rect.height * canvas.height ); 43 | 44 | if (evt.buttons & 1) { 45 | let deltaX = me.mouse.x - oldX; 46 | let deltaY = me.mouse.y - oldY; 47 | if (me.currentTransform && deltaX === deltaX && deltaY === deltaY) { 48 | me.camera.x -= deltaX / me.currentTransform[0]; 49 | me.camera.y -= deltaY / me.currentTransform[3]; 50 | } 51 | } 52 | 53 | me.requestDraw(); 54 | return false; 55 | }); 56 | 57 | canvas.addEventListener('wheel', function(evt){ 58 | evt.preventDefault(); 59 | 60 | let dY = evt.deltaY; 61 | if (evt.deltaMode == 1) dY *= 16.0; //if delta is in lines, make larger 62 | 63 | me.setCurrentTransform(); 64 | 65 | let oldX = (me.mouse.x - me.currentTransform[4]) / me.currentTransform[0]; 66 | let oldY = (me.mouse.y - me.currentTransform[5]) / me.currentTransform[3]; 67 | 68 | me.camera.radius *= Math.pow(0.5, -dY / 300.0); 69 | 70 | me.setCurrentTransform(); 71 | 72 | let newX = (me.mouse.x - me.currentTransform[4]) / me.currentTransform[0]; 73 | let newY = (me.mouse.y - me.currentTransform[5]) / me.currentTransform[3]; 74 | 75 | me.camera.x += (oldX - newX); 76 | me.camera.y += (oldY - newY); 77 | 78 | me.setCurrentTransform(); 79 | 80 | me.requestDraw(); 81 | 82 | return false; 83 | }); 84 | 85 | canvas.addEventListener('keydown', function(evt){ 86 | //console.log(evt); 87 | if (evt.code === "ShiftLeft" || evt.code === "ShiftRight") { 88 | evt.preventDefault(); 89 | me.show = SHOW_BACK; 90 | me.requestDraw(); 91 | return false; 92 | } 93 | /*if (evt.code === "Space") { 94 | evt.preventDefault(); 95 | if (me.hovered) { 96 | console.log(me.hovered.tile.src); 97 | } 98 | me.requestDraw(); 99 | return false; 100 | }*/ 101 | }); 102 | 103 | canvas.addEventListener('keyup', function(evt){ 104 | //console.log(evt); 105 | if (evt.code === "ShiftLeft" || evt.code === "ShiftRight") { 106 | evt.preventDefault(); 107 | me.show = SHOW_BOTH; 108 | me.requestDraw(); 109 | return false; 110 | } 111 | }); 112 | 113 | canvas.addEventListener('dblclick', function(evt){ 114 | if (me.onDoubleClickSource) { 115 | if (me.hovered && me.hovered.tile.source) { 116 | me.onDoubleClickSource(me.hovered.tile.source); 117 | } else { 118 | me.onDoubleClickSource(); 119 | } 120 | } 121 | }); 122 | 123 | canvas.addEventListener('mousedown', function(evt){ 124 | evt.preventDefault(); 125 | if (me.hovered && me.hovered.tile.source) { 126 | if (me.onClickSource) { 127 | me.onClickSource(me.hovered.tile.source); 128 | } 129 | } 130 | return false; 131 | }); 132 | 133 | canvas.addEventListener('mouseover', function(evt){ 134 | canvas.focus(); 135 | return false; 136 | }); 137 | } 138 | 139 | 140 | ShowKnitout.prototype.clearDrawing = function ShowKnitout_clearDrawing() { 141 | this.columns = 0; 142 | this.rows = 0; 143 | this.grids = { b:[], f:[] }; 144 | this.columnX = []; 145 | this.min = { x:0, y:0 }; 146 | this.max = { x:0, y:0 }; 147 | 148 | this.drawing = TileSet.makeDrawing(); 149 | this.highlightFn = function(){ return false; } 150 | }; 151 | 152 | ShowKnitout.prototype.setHighlightFn = function ShowKnitout_setHighlightFn(highlightFn) { 153 | this.highlightFn = highlightFn; 154 | this.requestDraw(); 155 | }; 156 | 157 | ShowKnitout.prototype.setCurrentTransform = function ShowKnitout_setCurrentTransform() { 158 | let rect = this.canvas.getBoundingClientRect(); 159 | const w = Math.round(rect.width * window.devicePixelRatio); 160 | const h = Math.round(rect.height * window.devicePixelRatio); 161 | 162 | const scale = Math.min(w,h) / (2.0 * this.camera.radius); 163 | 164 | this.currentTransform = [scale,0, 0,-scale, 0.5*w-scale*this.camera.x, 0.5*h+scale*this.camera.y]; 165 | }; 166 | 167 | ShowKnitout.prototype.draw = function ShowKnitout_draw() { 168 | let rect = this.canvas.getBoundingClientRect(); 169 | const w = Math.round(rect.width * window.devicePixelRatio); 170 | const h = Math.round(rect.height * window.devicePixelRatio); 171 | 172 | this.canvas.width = w; 173 | this.canvas.height = h; 174 | /* 175 | this.canvas.style.width = (w / window.devicePixelRatio) + "px"; 176 | this.canvas.style.height = (h / window.devicePixelRatio) + "px"; 177 | */ 178 | 179 | //blank and continue: 180 | const ctx = this.ctx; 181 | ctx.setTransform(1,0, 0,1, 0,0); 182 | ctx.fillStyle = "#888"; 183 | ctx.fillRect(0,0, w,h); 184 | 185 | //scale to show whole grid: 186 | this.setCurrentTransform(); 187 | ctx.setTransform(...this.currentTransform); 188 | 189 | //DEBUG: show min/max rectangle [used for camera handling] 190 | //ctx.fillStyle = "#f0f"; 191 | //ctx.fillRect(this.min.x,this.min.y, this.max.x-this.min.x,this.max.y-this.min.y); 192 | 193 | const gridOfs = { 194 | f:{x:0.0, y:0.0}, 195 | b:{x:-0.15 * TileSet.LoopWidth, y:0.2 * TileSet.TileHeight}, 196 | }; 197 | gridOfs.fs = {x: 0.75 * gridOfs.f.x + 0.25 * gridOfs.b.x, y: 0.75 * gridOfs.f.y + 0.25 * gridOfs.b.y}; 198 | gridOfs.bs = {x: 0.25 * gridOfs.f.x + 0.75 * gridOfs.b.x, y: 0.25 * gridOfs.f.y + 0.75 * gridOfs.b.y}; 199 | 200 | TileSet.draw(ctx, this.drawing, { 201 | frontOfs:gridOfs.f, 202 | frontSlidersOfs:gridOfs.fs, 203 | backOfs:gridOfs.b, 204 | backSlidersOfs:gridOfs.bs, 205 | backTintRGBA:[0.53, 0.53, 0.53, 0.6], 206 | middleTintRGBA:[0.53, 0.53, 0.53, 0.3], 207 | frontTintRGBA:[1.0, 1.0, 1.0, 0.0] 208 | }); 209 | 210 | //draw highlights: 211 | ["b","bs","fs","f"].forEach(function(gridName) { 212 | let grid = this.grids[gridName]; 213 | let ofs = gridOfs[gridName]; 214 | for (let row = 0; row < this.rows; ++row) { 215 | for (let col = 0; col < this.columns; ++col) { 216 | 217 | const x = this.columnX[col]; 218 | const width = (col + 1 < this.columnX.length ? this.columnX[col+1] : this.width) - x; 219 | const y = TileSet.TileHeight * row; 220 | 221 | let tile = grid[row * this.columns + col]; 222 | if (tile && tile.source && this.highlightFn(tile.source)) { 223 | ctx.globalAlpha = 0.5; 224 | ctx.fillStyle = '#fff'; 225 | ctx.fillRect(ofs.x+x,ofs.y+y, width,TileSet.TileHeight); 226 | ctx.globalAlpha = 1.0; 227 | } 228 | } 229 | } 230 | }, this); 231 | 232 | 233 | //update selection: 234 | let mx = (this.mouse.x - this.currentTransform[4]) / this.currentTransform[0]; 235 | let my = (this.mouse.y - this.currentTransform[5]) / this.currentTransform[3]; 236 | let oldHovered = this.hovered; 237 | this.hovered = null; 238 | let row = Math.floor((my - this.min.y) / TileSet.TileHeight); 239 | if (row >= 0 && row < this.rows && this.min.x <= mx && mx <= this.max.x) { 240 | let col = this.columnX.length - 1; 241 | while (col > 0 && this.columnX[col] > mx) --col; 242 | if (this.show.front > 0.0) { 243 | if (this.grids.f[row * this.columns + col]) { 244 | this.hovered = { 245 | bed:'f', 246 | row:row, 247 | col:col, 248 | tile:this.grids.f[row * this.columns + col] 249 | }; 250 | } 251 | } 252 | if (this.show.back > 0.0 && !this.hovered) { 253 | if (this.grids.b[row * this.columns + col]) { 254 | this.hovered = { 255 | bed:'b', 256 | row:row, 257 | col:col, 258 | tile:this.grids.b[row * this.columns + col] 259 | }; 260 | } 261 | 262 | } 263 | } 264 | 265 | if (this.hovered) { 266 | let x = this.columnX[this.hovered.col]; 267 | let y = this.min.y; 268 | let w = (this.hovered.col+1 < this.columnX.length ? this.columnX[this.hovered.col+1] : this.width) - x; 269 | let h = this.max.y - this.min.y; 270 | if (this.hovered.bed === 'b') { 271 | x += -0.15 * TileSet.LoopWidth; 272 | y += 0.2 * TileSet.TileHeight; 273 | } 274 | ctx.globalAlpha = 0.1; 275 | ctx.fillStyle = '#fff'; 276 | ctx.fillRect(x,y, w,h); 277 | ctx.globalAlpha = 1.0; 278 | } 279 | 280 | if (oldHovered !== this.hovered) { 281 | if (this.onHoverSource) { 282 | if (this.hovered && this.hovered.tile.source) { 283 | this.onHoverSource(this.hovered.tile.source); 284 | } else { 285 | this.onHoverSource(); 286 | } 287 | } 288 | } 289 | 290 | 291 | //draw selection: 292 | if (this.hovered) { 293 | let x = this.columnX[this.hovered.col]; 294 | const width = (this.hovered.col + 1 < this.columnX.length ? this.columnX[this.hovered.col+1] : this.width) - x; 295 | let y = TileSet.TileHeight * this.hovered.row; 296 | if (this.hovered.bed === 'b') { 297 | x += -0.15 * TileSet.LoopWidth; 298 | y += 0.2 * TileSet.TileHeight; 299 | } 300 | const height = TileSet.TileHeight; 301 | ctx.beginPath(); 302 | ctx.moveTo(x,y); 303 | ctx.lineTo(x+width,y); 304 | /* 305 | if (this.hovered.tile.dir === '+') { 306 | ctx.lineTo(x+width + 0.1 * TileSet.TileHeight,y+0.5*height); 307 | } else if (this.hovered.tile.dir === '-') { 308 | ctx.lineTo(x+width + -0.1 * TileSet.TileHeight,y+0.5*height); 309 | } 310 | */ 311 | ctx.lineTo(x+width,y+height); 312 | ctx.lineTo(x,y+height); 313 | /* 314 | if (this.hovered.tile.dir === '+') { 315 | ctx.lineTo(x + 0.1 * TileSet.TileHeight,y+0.5*height); 316 | } else if (this.hovered.tile.dir === '-') { 317 | ctx.lineTo(x + -0.1 * TileSet.TileHeight,y+0.5*height); 318 | } 319 | */ 320 | ctx.closePath(); 321 | ctx.strokeStyle = (this.hovered.bed === 'f' ? '#fff' : '#ddd'); 322 | ctx.stroke(); 323 | 324 | const s = 0.2 * TileSet.TileHeight; 325 | if (this.hovered.tile.dir === '+') { 326 | ctx.beginPath(); 327 | ctx.moveTo(x + 0.5 * width - 0.25 * s, y + 0.5 * height - s); 328 | ctx.lineTo(x + 0.5 * width + 0.25 * s, y + 0.5 * height); 329 | ctx.lineTo(x + 0.5 * width - 0.25 * s, y + 0.5 * height + s); 330 | ctx.globalAlpha = 0.5; 331 | ctx.stroke(); 332 | ctx.globalAlpha = 1.0; 333 | } else if (this.hovered.tile.dir === '-') { 334 | ctx.beginPath(); 335 | ctx.moveTo(x + 0.5 * width + 0.25 * s, y + 0.5 * height - s); 336 | ctx.lineTo(x + 0.5 * width - 0.25 * s, y + 0.5 * height); 337 | ctx.lineTo(x + 0.5 * width + 0.25 * s, y + 0.5 * height + s); 338 | ctx.globalAlpha = 0.5; 339 | ctx.stroke(); 340 | ctx.globalAlpha = 1.0; 341 | } 342 | } 343 | 344 | if (this.hovered !== oldHovered) { 345 | //if (this.hovered) { console.log(this.hovered.tile); } //DEBUG 346 | if (this.highlightLine) { 347 | this.highlightLine(this.hovered ? this.hovered.tile.source : ""); 348 | } 349 | } 350 | 351 | //DEBUG: draw mouse: 352 | ctx.beginPath(); 353 | ctx.moveTo(mx - 1.0, my - 1.0); 354 | ctx.lineTo(mx + 1.0, my + 1.0); 355 | ctx.moveTo(mx - 1.0, my + 1.0); 356 | ctx.lineTo(mx + 1.0, my - 1.0); 357 | ctx.lineWidth = 1.0; 358 | ctx.strokeStyle = '#fff'; 359 | ctx.stroke(); 360 | 361 | }; 362 | 363 | ShowKnitout.prototype.requestDraw = function ShowKnitout_requestDraw() { 364 | if (this.drawRequested) return; 365 | this.drawRequested = true; 366 | let me = this; 367 | window.requestAnimationFrame(function(){ 368 | delete me.drawRequested; 369 | me.draw(); 370 | }); 371 | }; 372 | 373 | ShowKnitout.prototype.showTiles = function ShowKnitout_showTiles() { 374 | this.requestDraw(); 375 | 376 | //clear grids: 377 | this.clearDrawing(); 378 | 379 | this.rows = 3 * 5; 380 | this.columns = 3 * 4; 381 | 382 | this.grids.b = new Array(this.rows * this.columns); 383 | this.grids.f = new Array(this.rows * this.columns); 384 | for (let col = 0; col < this.columns; ++col) { 385 | this.columnX.push(col * TileSet.LoopWidth); 386 | } 387 | this.min = TileSet.tileLowerLeft(0,0); 388 | this.max = TileSet.tileLowerLeft(this.columns, this.rows); 389 | 390 | for (let row = 0; row < this.rows; ++row) { 391 | let r = this.rows - 1 - row; 392 | for (let col = 0; col < this.columns; ++col) { 393 | let type = ['k','t','s','x','m'][Math.floor(r / 3)]; 394 | let bed = (col < 6 ? 'f' : 'b'); 395 | if (col < 3 || col >= 9) { 396 | if (type === 's') type = 'S'; 397 | else if (type === 'x') type = 'X'; 398 | else continue; 399 | } 400 | let yarns = [['1','2'],['1'],[]][r % 3]; 401 | let loops = [['3','4'],['3'],[]][col % 3]; 402 | let across = []; 403 | if (type === 'X' || type === 'S') { 404 | across = loops; 405 | loops = yarns; 406 | yarns = []; 407 | } else if (type === 'x' || type === 's') { 408 | across = loops; 409 | } 410 | //if (type === 'x' && yarns.length !== 0) continue; 411 | this.grids.f[row * this.columns + col] = {}; 412 | TileSet.addLoopTile(this.drawing, {}, 413 | {i:col, y:row, type:type, bed:bed, loops:loops, yarms:yarns, across:across}); 414 | } 415 | } 416 | }; 417 | 418 | ShowKnitout.prototype.resetZoom = function ShowKnitout_resetZoom() { 419 | const Margin = 0.5 * TileSet.TileHeight; 420 | this.camera.x = 0.5 * (this.max.x + this.min.x); 421 | this.camera.y = 0.5 * (this.max.y + this.min.y); 422 | this.camera.radius = 0.5 * Math.max(this.max.x - this.min.x + 2.0 * Margin, this.max.y - this.min.y + 2.0 * Margin); 423 | this.requestDraw(); 424 | } 425 | 426 | ShowKnitout.prototype.parse = function ShowKnitout_parse(codeText, useKnitoutAsSource=false, resetView=false) { 427 | 428 | let oldMin = this.min; 429 | let oldMax = this.max; 430 | 431 | const machine = new CellMachine(); 432 | try { 433 | parseKnitout(codeText, machine, useKnitoutAsSource); 434 | } catch (e) { 435 | console.log("parse error:",e); 436 | } 437 | 438 | //extend loops/yarns to the top of the machine: 439 | machine.stretchLoops(); 440 | 441 | //This seems really out of place, but I'm leaving it in for now: 442 | let code = document.getElementById('code1'); 443 | if (code) { 444 | code.innerHTML = ""; 445 | let lines = codeText.split("\n"); 446 | let annotatedLines = "" 447 | for (let i = 0; i < lines.length; i++) { 448 | // Remove source line comments 449 | /*let sourceCommentIndex = lines[i].indexOf(";!source:"); 450 | if (sourceCommentIndex >= 0) { 451 | lines[i] = lines[i].substring(0, sourceCommentIndex); 452 | }*/ 453 | annotatedLines += "" + i + "" + lines[i] + "\n"; 454 | } 455 | code.innerHTML = annotatedLines; 456 | } 457 | 458 | this.requestDraw(); 459 | 460 | //clear grids: 461 | this.clearDrawing(); 462 | 463 | let minIndex = Infinity; 464 | let maxIndex = -Infinity; 465 | for (let bn in machine.beds) { 466 | minIndex = Math.min(minIndex, machine.beds[bn].minIndex); 467 | maxIndex = Math.max(maxIndex, machine.beds[bn].maxIndex); 468 | } 469 | if (minIndex > maxIndex) return; 470 | 471 | 472 | this.min = TileSet.tileLowerLeft(minIndex, 0); 473 | this.max = TileSet.tileLowerLeft(maxIndex + 1, machine.topRow + 1); 474 | 475 | const Margin = 0.5 * TileSet.TileHeight; 476 | 477 | //update camera based on old/new min/max: 478 | if (resetView || oldMin.x >= oldMax.x || oldMin.y >= oldMax.y) { 479 | //old min/max invalid, frame whole piece: 480 | this.camera.x = 0.5 * (this.max.x + this.min.x); 481 | this.camera.y = 0.5 * (this.max.y + this.min.y); 482 | this.camera.radius = 0.5 * Math.max(this.max.x - this.min.x + 2.0 * Margin, this.max.y - this.min.y + 2.0 * Margin); 483 | } else { 484 | /* 485 | //if the piece got larger, zoom out: 486 | let stretch = Math.max( 487 | (this.max.x - this.min.x) / (oldMax.x - oldMin.x), 488 | (this.max.y - this.min.y) / (oldMax.y - oldMin.y) 489 | ); 490 | if (stretch > 1.0) { 491 | this.camera.radius *= stretch; 492 | } 493 | */ 494 | //if the piece got smaller so that it can't be seen any longer, zoom out: 495 | this.camera.radius = Math.max( 496 | this.camera.radius, 497 | this.camera.x-this.max.x + Margin, 498 | this.min.x-this.camera.x + Margin, 499 | this.camera.y-this.max.y + Margin, 500 | this.min.y-this.camera.y + Margin 501 | ); 502 | } 503 | 504 | //fill grids from machine's columns: 505 | this.columns = maxIndex - minIndex + 1; 506 | this.rows = machine.topRow + 1; 507 | this.grids.b = new Array(this.columns * this.rows); 508 | this.grids.f = new Array(this.columns * this.rows); 509 | this.grids.bs = new Array(this.columns * this.rows); 510 | this.grids.fs = new Array(this.columns * this.rows); 511 | 512 | for (let i = minIndex; i <= maxIndex; ++i) { 513 | let bColumn = machine.beds.b.getColumn(i); 514 | let fColumn = machine.beds.f.getColumn(i); 515 | let bsColumn = machine.beds.bs.getColumn(i); 516 | let fsColumn = machine.beds.fs.getColumn(i); 517 | let bi = 0; 518 | let fi = 0; 519 | let bsi = 0; 520 | let fsi = 0; 521 | for (let y = 0; y < this.rows; ++y) { 522 | let b = null; 523 | if (bi < bColumn.length && bColumn[bi].y === y) { 524 | b = bColumn[bi]; 525 | ++bi; 526 | } 527 | let f = null; 528 | if (fi < fColumn.length && fColumn[fi].y === y) { 529 | f = fColumn[fi]; 530 | ++fi; 531 | } 532 | let bs = null; 533 | if (bsi < bsColumn.length && bsColumn[bsi].y === y) { 534 | bs = bsColumn[bsi]; 535 | ++bsi; 536 | } 537 | let fs = null; 538 | if (fsi < fsColumn.length && fsColumn[fsi].y === y) { 539 | fs = fsColumn[fsi]; 540 | ++fsi; 541 | } 542 | 543 | if (i % 2 === 0) { 544 | //stitches: 545 | if (f) { 546 | const loops = f.ports['v']; 547 | const yarns = f.ports['+']; 548 | const incoming = f.ports['x']; 549 | this.grids.f[y * this.columns + (i - minIndex)] = f; 550 | TileSet.addLoopTile(this.drawing, f.styles, 551 | {i:i, y:y, type:f.type, bed:'f', loops:loops, yarns:yarns, across:incoming}); 552 | } 553 | if (b) { 554 | const loops = b.ports['v']; 555 | const yarns = b.ports['+']; 556 | const incoming = b.ports['o']; 557 | this.grids.b[y * this.columns + (i - minIndex)] = b; 558 | TileSet.addLoopTile(this.drawing, b.styles, 559 | {i:i, y:y, type:b.type, bed:'b', loops:loops, yarns:yarns, across:incoming}); 560 | } 561 | if (fs) { 562 | const loops = fs.ports['v']; 563 | const yarns = fs.ports['+']; 564 | const incoming = fs.ports['x']; 565 | this.grids.fs[y * this.columns + (i - minIndex)] = fs; 566 | TileSet.addLoopTile(this.drawing, fs.styles, 567 | {i:i, y:y, type:fs.type, bed:'fs', loops:loops, yarns:yarns, across:incoming}); 568 | } 569 | if (bs) { 570 | const loops = bs.ports['v']; 571 | const yarns = bs.ports['+']; 572 | const incoming = bs.ports['o']; 573 | this.grids.bs[y * this.columns + (i - minIndex)] = bs; 574 | TileSet.addLoopTile(this.drawing, bs.styles, 575 | {i:i, y:y, type:bs.type, bed:'bs', loops:loops, yarns:yarns, across:incoming}); 576 | } 577 | } else { 578 | //yarns: 579 | if (f) { 580 | this.grids.f[y * this.columns + (i - minIndex)] = f; 581 | TileSet.addYarnTile(this.drawing, f.styles, {i:i, y:y, bed:'f', ports:f.ports, segs:f.segs}, machine.carriers); 582 | } 583 | if (b) { 584 | this.grids.b[y * this.columns + (i - minIndex)] = b; 585 | TileSet.addYarnTile(this.drawing, b.styles, {i:i, y:y, bed:'b', ports:b.ports, segs:b.segs}, machine.carriers); 586 | } 587 | if (fs) { 588 | this.grids.fs[y * this.columns + (i - minIndex)] = fs; 589 | TileSet.addYarnTile(this.drawing, fs.styles, {i:i, y:y, bed:'fs', ports:fs.ports, segs:fs.segs}, machine.carriers); 590 | } 591 | if (bs) { 592 | this.grids.bs[y * this.columns + (i - minIndex)] = bs; 593 | TileSet.addYarnTile(this.drawing, bs.styles, {i:i, y:y, bed:'bs', ports:bs.ports, segs:bs.segs}, machine.carriers); 594 | } 595 | } 596 | } 597 | } 598 | 599 | //fill in columnX for ... hm... I guess highlighting code uses it? 600 | for (let i = minIndex; i <= maxIndex+1; ++i) { 601 | this.columnX.push(TileSet.tileLowerLeft(i,0).x); 602 | } 603 | 604 | //fill crosses from machine's crosses: 605 | machine.crosses.forEach(function(cross){ 606 | TileSet.addCross(this.drawing, cross); 607 | //this.crosses.push(TileSet.makeCross(cross.type, this.columnX[cross.i-minIndex], this.columnX[cross.i2-minIndex], TileSet.TileHeight * cross.y, cross.yarns, cross.colors)); 608 | }, this); 609 | 610 | 611 | 612 | }; 613 | 614 | //Find all "ShowKnitout" canvases, and attach a ShowKnitout object: 615 | let elts = document.getElementsByClassName("ShowKnitout"); 616 | for (let i = 0; i < elts.length; ++i) { 617 | let elt = elts[i]; 618 | console.assert(elt.tagName === "CANVAS", "ShowKnitouts should be canvases."); 619 | window.SK = new ShowKnitout(elt); 620 | } 621 | -------------------------------------------------------------------------------- /simplified-visualizer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test of knitout visualizer (2) 6 | 95 | 96 | 97 |
98 | 99 |
103 |

Here's some example knitout code:

104 | 105 |
;!knitout-2
106 | ;;Carriers: 1 3 7
107 | ;;Machine: SWGXYZ
108 | ;;Gauge: 15
109 | inhook 7
110 | tuck + f0 7
111 | tuck + f1 7
112 | tuck + f2 7
113 | tuck + f3 7
114 | knit - f3 7
115 | knit - f2 7
116 | knit - f1 7
117 | knit - f0 7
118 | xfer f0 b0
119 | xfer f1 bs1
120 | xfer f2 b2
121 | xfer f3 bs3
122 | xfer b0 fs0
123 | xfer bs1 f1
124 | xfer b2 fs2
125 | xfer bs3 f3
126 | xfer fs0 b0
127 | xfer f1 b1
128 | xfer fs2 b2
129 | xfer f3 b3
130 | knit + b0 7
131 | knit + b1 7
132 | knit + b2 7
133 | knit + b3 7
134 | outhook 7
135 |
136 | 137 | 138 | 139 | 140 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /tiles-test.k: -------------------------------------------------------------------------------- 1 | ;!knitout-2 2 | ;;Carriers: A B C 3 | ; generated by write-test.js 4 | 5 | 6 | ; tuck + 7 | inhook A B C 8 | tuck - f3 9 | tuck - f2 A 10 | tuck - f1 A B 11 | tuck - f0 A B C 12 | tuck + f0 13 | tuck + f1 14 | tuck + f2 15 | tuck + f3 16 | drop f0 17 | drop f1 18 | drop f2 19 | drop f3 20 | outhook A B C 21 | 22 | ; tuck - 23 | inhook A B C 24 | tuck + f0 25 | tuck + f1 A 26 | tuck + f2 A B 27 | tuck + f3 A B C 28 | tuck - f3 29 | tuck - f2 30 | tuck - f1 31 | tuck - f0 32 | drop f0 33 | drop f1 34 | drop f2 35 | drop f3 36 | outhook A B C 37 | 38 | ; tuck + A 39 | inhook A B C 40 | tuck - f3 41 | tuck - f2 A 42 | tuck - f1 A B 43 | tuck - f0 A B C 44 | tuck + f0 A 45 | tuck + f1 A 46 | tuck + f2 A 47 | tuck + f3 A 48 | drop f0 49 | drop f1 50 | drop f2 51 | drop f3 52 | outhook A B C 53 | 54 | ; tuck - A 55 | inhook A B C 56 | tuck + f0 57 | tuck + f1 A 58 | tuck + f2 A B 59 | tuck + f3 A B C 60 | tuck - f3 A 61 | tuck - f2 A 62 | tuck - f1 A 63 | tuck - f0 A 64 | drop f0 65 | drop f1 66 | drop f2 67 | drop f3 68 | outhook A B C 69 | 70 | ; tuck + A B 71 | inhook A B C 72 | tuck - f3 73 | tuck - f2 A 74 | tuck - f1 A B 75 | tuck - f0 A B C 76 | tuck + f0 A B 77 | tuck + f1 A B 78 | tuck + f2 A B 79 | tuck + f3 A B 80 | drop f0 81 | drop f1 82 | drop f2 83 | drop f3 84 | outhook A B C 85 | 86 | ; tuck - A B 87 | inhook A B C 88 | tuck + f0 89 | tuck + f1 A 90 | tuck + f2 A B 91 | tuck + f3 A B C 92 | tuck - f3 A B 93 | tuck - f2 A B 94 | tuck - f1 A B 95 | tuck - f0 A B 96 | drop f0 97 | drop f1 98 | drop f2 99 | drop f3 100 | outhook A B C 101 | 102 | ; tuck + A B C 103 | inhook A B C 104 | tuck - f3 105 | tuck - f2 A 106 | tuck - f1 A B 107 | tuck - f0 A B C 108 | tuck + f0 A B C 109 | tuck + f1 A B C 110 | tuck + f2 A B C 111 | tuck + f3 A B C 112 | drop f0 113 | drop f1 114 | drop f2 115 | drop f3 116 | outhook A B C 117 | 118 | ; tuck - A B C 119 | inhook A B C 120 | tuck + f0 121 | tuck + f1 A 122 | tuck + f2 A B 123 | tuck + f3 A B C 124 | tuck - f3 A B C 125 | tuck - f2 A B C 126 | tuck - f1 A B C 127 | tuck - f0 A B C 128 | drop f0 129 | drop f1 130 | drop f2 131 | drop f3 132 | outhook A B C 133 | 134 | ; knit + 135 | inhook A B C 136 | tuck - f3 137 | tuck - f2 A 138 | tuck - f1 A B 139 | tuck - f0 A B C 140 | knit + f0 141 | knit + f1 142 | knit + f2 143 | knit + f3 144 | drop f0 145 | drop f1 146 | drop f2 147 | drop f3 148 | outhook A B C 149 | 150 | ; knit - 151 | inhook A B C 152 | tuck + f0 153 | tuck + f1 A 154 | tuck + f2 A B 155 | tuck + f3 A B C 156 | knit - f3 157 | knit - f2 158 | knit - f1 159 | knit - f0 160 | drop f0 161 | drop f1 162 | drop f2 163 | drop f3 164 | outhook A B C 165 | 166 | ; knit + A 167 | inhook A B C 168 | tuck - f3 169 | tuck - f2 A 170 | tuck - f1 A B 171 | tuck - f0 A B C 172 | knit + f0 A 173 | knit + f1 A 174 | knit + f2 A 175 | knit + f3 A 176 | drop f0 177 | drop f1 178 | drop f2 179 | drop f3 180 | outhook A B C 181 | 182 | ; knit - A 183 | inhook A B C 184 | tuck + f0 185 | tuck + f1 A 186 | tuck + f2 A B 187 | tuck + f3 A B C 188 | knit - f3 A 189 | knit - f2 A 190 | knit - f1 A 191 | knit - f0 A 192 | drop f0 193 | drop f1 194 | drop f2 195 | drop f3 196 | outhook A B C 197 | 198 | ; knit + A B 199 | inhook A B C 200 | tuck - f3 201 | tuck - f2 A 202 | tuck - f1 A B 203 | tuck - f0 A B C 204 | knit + f0 A B 205 | knit + f1 A B 206 | knit + f2 A B 207 | knit + f3 A B 208 | drop f0 209 | drop f1 210 | drop f2 211 | drop f3 212 | outhook A B C 213 | 214 | ; knit - A B 215 | inhook A B C 216 | tuck + f0 217 | tuck + f1 A 218 | tuck + f2 A B 219 | tuck + f3 A B C 220 | knit - f3 A B 221 | knit - f2 A B 222 | knit - f1 A B 223 | knit - f0 A B 224 | drop f0 225 | drop f1 226 | drop f2 227 | drop f3 228 | outhook A B C 229 | 230 | ; knit + A B C 231 | inhook A B C 232 | tuck - f3 233 | tuck - f2 A 234 | tuck - f1 A B 235 | tuck - f0 A B C 236 | knit + f0 A B C 237 | knit + f1 A B C 238 | knit + f2 A B C 239 | knit + f3 A B C 240 | drop f0 241 | drop f1 242 | drop f2 243 | drop f3 244 | outhook A B C 245 | 246 | ; knit - A B C 247 | inhook A B C 248 | tuck + f0 249 | tuck + f1 A 250 | tuck + f2 A B 251 | tuck + f3 A B C 252 | knit - f3 A B C 253 | knit - f2 A B C 254 | knit - f1 A B C 255 | knit - f0 A B C 256 | drop f0 257 | drop f1 258 | drop f2 259 | drop f3 260 | outhook A B C 261 | 262 | ; miss + 263 | inhook A B C 264 | tuck - f3 265 | tuck - f2 A 266 | tuck - f1 A B 267 | tuck - f0 A B C 268 | miss + f0 269 | miss + f1 270 | miss + f2 271 | miss + f3 272 | drop f0 273 | drop f1 274 | drop f2 275 | drop f3 276 | outhook A B C 277 | 278 | ; miss - 279 | inhook A B C 280 | tuck + f0 281 | tuck + f1 A 282 | tuck + f2 A B 283 | tuck + f3 A B C 284 | miss - f3 285 | miss - f2 286 | miss - f1 287 | miss - f0 288 | drop f0 289 | drop f1 290 | drop f2 291 | drop f3 292 | outhook A B C 293 | 294 | ; miss + A 295 | inhook A B C 296 | tuck - f3 297 | tuck - f2 A 298 | tuck - f1 A B 299 | tuck - f0 A B C 300 | miss + f0 A 301 | miss + f1 A 302 | miss + f2 A 303 | miss + f3 A 304 | drop f0 305 | drop f1 306 | drop f2 307 | drop f3 308 | outhook A B C 309 | 310 | ; miss - A 311 | inhook A B C 312 | tuck + f0 313 | tuck + f1 A 314 | tuck + f2 A B 315 | tuck + f3 A B C 316 | miss - f3 A 317 | miss - f2 A 318 | miss - f1 A 319 | miss - f0 A 320 | drop f0 321 | drop f1 322 | drop f2 323 | drop f3 324 | outhook A B C 325 | 326 | ; miss + A B 327 | inhook A B C 328 | tuck - f3 329 | tuck - f2 A 330 | tuck - f1 A B 331 | tuck - f0 A B C 332 | miss + f0 A B 333 | miss + f1 A B 334 | miss + f2 A B 335 | miss + f3 A B 336 | drop f0 337 | drop f1 338 | drop f2 339 | drop f3 340 | outhook A B C 341 | 342 | ; miss - A B 343 | inhook A B C 344 | tuck + f0 345 | tuck + f1 A 346 | tuck + f2 A B 347 | tuck + f3 A B C 348 | miss - f3 A B 349 | miss - f2 A B 350 | miss - f1 A B 351 | miss - f0 A B 352 | drop f0 353 | drop f1 354 | drop f2 355 | drop f3 356 | outhook A B C 357 | 358 | ; miss + A B C 359 | inhook A B C 360 | tuck - f3 361 | tuck - f2 A 362 | tuck - f1 A B 363 | tuck - f0 A B C 364 | miss + f0 A B C 365 | miss + f1 A B C 366 | miss + f2 A B C 367 | miss + f3 A B C 368 | drop f0 369 | drop f1 370 | drop f2 371 | drop f3 372 | outhook A B C 373 | 374 | ; miss - A B C 375 | inhook A B C 376 | tuck + f0 377 | tuck + f1 A 378 | tuck + f2 A B 379 | tuck + f3 A B C 380 | miss - f3 A B C 381 | miss - f2 A B C 382 | miss - f1 A B C 383 | miss - f0 A B C 384 | drop f0 385 | drop f1 386 | drop f2 387 | drop f3 388 | outhook A B C 389 | 390 | ; split + over 391 | inhook A B C 392 | tuck - b3 393 | tuck - b2 394 | tuck - b1 395 | tuck - b0 396 | outhook A B C 397 | inhook A B C 398 | tuck - f3 399 | tuck - f2 A 400 | tuck - f1 A B 401 | tuck - f0 A B C 402 | split + f0 b0 403 | split + f1 b1 404 | split + f2 b2 405 | split + f3 b3 406 | drop f0 407 | drop f1 408 | drop f2 409 | drop f3 410 | drop b0 411 | drop b1 412 | drop b2 413 | drop b3 414 | outhook A B C 415 | 416 | ; split - over 417 | inhook A B C 418 | tuck + b0 419 | tuck + b1 420 | tuck + b2 421 | tuck + b3 422 | outhook A B C 423 | inhook A B C 424 | tuck + f0 425 | tuck + f1 A 426 | tuck + f2 A B 427 | tuck + f3 A B C 428 | split - f3 b3 429 | split - f2 b2 430 | split - f1 b1 431 | split - f0 b0 432 | drop f0 433 | drop f1 434 | drop f2 435 | drop f3 436 | drop b0 437 | drop b1 438 | drop b2 439 | drop b3 440 | outhook A B C 441 | 442 | ; split + A over 443 | inhook A B C 444 | tuck - b3 445 | tuck - b2 446 | tuck - b1 447 | tuck - b0 448 | outhook A B C 449 | inhook A B C 450 | tuck - f3 451 | tuck - f2 A 452 | tuck - f1 A B 453 | tuck - f0 A B C 454 | split + f0 b0 A 455 | split + f1 b1 A 456 | split + f2 b2 A 457 | split + f3 b3 A 458 | drop f0 459 | drop f1 460 | drop f2 461 | drop f3 462 | drop b0 463 | drop b1 464 | drop b2 465 | drop b3 466 | outhook A B C 467 | 468 | ; split - A over 469 | inhook A B C 470 | tuck + b0 471 | tuck + b1 472 | tuck + b2 473 | tuck + b3 474 | outhook A B C 475 | inhook A B C 476 | tuck + f0 477 | tuck + f1 A 478 | tuck + f2 A B 479 | tuck + f3 A B C 480 | split - f3 b3 A 481 | split - f2 b2 A 482 | split - f1 b1 A 483 | split - f0 b0 A 484 | drop f0 485 | drop f1 486 | drop f2 487 | drop f3 488 | drop b0 489 | drop b1 490 | drop b2 491 | drop b3 492 | outhook A B C 493 | 494 | ; split + A B over 495 | inhook A B C 496 | tuck - b3 497 | tuck - b2 498 | tuck - b1 499 | tuck - b0 500 | outhook A B C 501 | inhook A B C 502 | tuck - f3 503 | tuck - f2 A 504 | tuck - f1 A B 505 | tuck - f0 A B C 506 | split + f0 b0 A B 507 | split + f1 b1 A B 508 | split + f2 b2 A B 509 | split + f3 b3 A B 510 | drop f0 511 | drop f1 512 | drop f2 513 | drop f3 514 | drop b0 515 | drop b1 516 | drop b2 517 | drop b3 518 | outhook A B C 519 | 520 | ; split - A B over 521 | inhook A B C 522 | tuck + b0 523 | tuck + b1 524 | tuck + b2 525 | tuck + b3 526 | outhook A B C 527 | inhook A B C 528 | tuck + f0 529 | tuck + f1 A 530 | tuck + f2 A B 531 | tuck + f3 A B C 532 | split - f3 b3 A B 533 | split - f2 b2 A B 534 | split - f1 b1 A B 535 | split - f0 b0 A B 536 | drop f0 537 | drop f1 538 | drop f2 539 | drop f3 540 | drop b0 541 | drop b1 542 | drop b2 543 | drop b3 544 | outhook A B C 545 | 546 | ; split + A B C over 547 | inhook A B C 548 | tuck - b3 549 | tuck - b2 550 | tuck - b1 551 | tuck - b0 552 | outhook A B C 553 | inhook A B C 554 | tuck - f3 555 | tuck - f2 A 556 | tuck - f1 A B 557 | tuck - f0 A B C 558 | split + f0 b0 A B C 559 | split + f1 b1 A B C 560 | split + f2 b2 A B C 561 | split + f3 b3 A B C 562 | drop f0 563 | drop f1 564 | drop f2 565 | drop f3 566 | drop b0 567 | drop b1 568 | drop b2 569 | drop b3 570 | outhook A B C 571 | 572 | ; split - A B C over 573 | inhook A B C 574 | tuck + b0 575 | tuck + b1 576 | tuck + b2 577 | tuck + b3 578 | outhook A B C 579 | inhook A B C 580 | tuck + f0 581 | tuck + f1 A 582 | tuck + f2 A B 583 | tuck + f3 A B C 584 | split - f3 b3 A B C 585 | split - f2 b2 A B C 586 | split - f1 b1 A B C 587 | split - f0 b0 A B C 588 | drop f0 589 | drop f1 590 | drop f2 591 | drop f3 592 | drop b0 593 | drop b1 594 | drop b2 595 | drop b3 596 | outhook A B C 597 | 598 | ; split + over A 599 | inhook A B C 600 | tuck - b3 A 601 | tuck - b2 A 602 | tuck - b1 A 603 | tuck - b0 A 604 | outhook A B C 605 | inhook A B C 606 | tuck - f3 607 | tuck - f2 A 608 | tuck - f1 A B 609 | tuck - f0 A B C 610 | split + f0 b0 611 | split + f1 b1 612 | split + f2 b2 613 | split + f3 b3 614 | drop f0 615 | drop f1 616 | drop f2 617 | drop f3 618 | drop b0 619 | drop b1 620 | drop b2 621 | drop b3 622 | outhook A B C 623 | 624 | ; split - over A 625 | inhook A B C 626 | tuck + b0 A 627 | tuck + b1 A 628 | tuck + b2 A 629 | tuck + b3 A 630 | outhook A B C 631 | inhook A B C 632 | tuck + f0 633 | tuck + f1 A 634 | tuck + f2 A B 635 | tuck + f3 A B C 636 | split - f3 b3 637 | split - f2 b2 638 | split - f1 b1 639 | split - f0 b0 640 | drop f0 641 | drop f1 642 | drop f2 643 | drop f3 644 | drop b0 645 | drop b1 646 | drop b2 647 | drop b3 648 | outhook A B C 649 | 650 | ; split + A over A 651 | inhook A B C 652 | tuck - b3 A 653 | tuck - b2 A 654 | tuck - b1 A 655 | tuck - b0 A 656 | outhook A B C 657 | inhook A B C 658 | tuck - f3 659 | tuck - f2 A 660 | tuck - f1 A B 661 | tuck - f0 A B C 662 | split + f0 b0 A 663 | split + f1 b1 A 664 | split + f2 b2 A 665 | split + f3 b3 A 666 | drop f0 667 | drop f1 668 | drop f2 669 | drop f3 670 | drop b0 671 | drop b1 672 | drop b2 673 | drop b3 674 | outhook A B C 675 | 676 | ; split - A over A 677 | inhook A B C 678 | tuck + b0 A 679 | tuck + b1 A 680 | tuck + b2 A 681 | tuck + b3 A 682 | outhook A B C 683 | inhook A B C 684 | tuck + f0 685 | tuck + f1 A 686 | tuck + f2 A B 687 | tuck + f3 A B C 688 | split - f3 b3 A 689 | split - f2 b2 A 690 | split - f1 b1 A 691 | split - f0 b0 A 692 | drop f0 693 | drop f1 694 | drop f2 695 | drop f3 696 | drop b0 697 | drop b1 698 | drop b2 699 | drop b3 700 | outhook A B C 701 | 702 | ; split + A B over A 703 | inhook A B C 704 | tuck - b3 A 705 | tuck - b2 A 706 | tuck - b1 A 707 | tuck - b0 A 708 | outhook A B C 709 | inhook A B C 710 | tuck - f3 711 | tuck - f2 A 712 | tuck - f1 A B 713 | tuck - f0 A B C 714 | split + f0 b0 A B 715 | split + f1 b1 A B 716 | split + f2 b2 A B 717 | split + f3 b3 A B 718 | drop f0 719 | drop f1 720 | drop f2 721 | drop f3 722 | drop b0 723 | drop b1 724 | drop b2 725 | drop b3 726 | outhook A B C 727 | 728 | ; split - A B over A 729 | inhook A B C 730 | tuck + b0 A 731 | tuck + b1 A 732 | tuck + b2 A 733 | tuck + b3 A 734 | outhook A B C 735 | inhook A B C 736 | tuck + f0 737 | tuck + f1 A 738 | tuck + f2 A B 739 | tuck + f3 A B C 740 | split - f3 b3 A B 741 | split - f2 b2 A B 742 | split - f1 b1 A B 743 | split - f0 b0 A B 744 | drop f0 745 | drop f1 746 | drop f2 747 | drop f3 748 | drop b0 749 | drop b1 750 | drop b2 751 | drop b3 752 | outhook A B C 753 | 754 | ; split + A B C over A 755 | inhook A B C 756 | tuck - b3 A 757 | tuck - b2 A 758 | tuck - b1 A 759 | tuck - b0 A 760 | outhook A B C 761 | inhook A B C 762 | tuck - f3 763 | tuck - f2 A 764 | tuck - f1 A B 765 | tuck - f0 A B C 766 | split + f0 b0 A B C 767 | split + f1 b1 A B C 768 | split + f2 b2 A B C 769 | split + f3 b3 A B C 770 | drop f0 771 | drop f1 772 | drop f2 773 | drop f3 774 | drop b0 775 | drop b1 776 | drop b2 777 | drop b3 778 | outhook A B C 779 | 780 | ; split - A B C over A 781 | inhook A B C 782 | tuck + b0 A 783 | tuck + b1 A 784 | tuck + b2 A 785 | tuck + b3 A 786 | outhook A B C 787 | inhook A B C 788 | tuck + f0 789 | tuck + f1 A 790 | tuck + f2 A B 791 | tuck + f3 A B C 792 | split - f3 b3 A B C 793 | split - f2 b2 A B C 794 | split - f1 b1 A B C 795 | split - f0 b0 A B C 796 | drop f0 797 | drop f1 798 | drop f2 799 | drop f3 800 | drop b0 801 | drop b1 802 | drop b2 803 | drop b3 804 | outhook A B C 805 | 806 | ; split + over A B 807 | inhook A B C 808 | tuck - b3 A B 809 | tuck - b2 A B 810 | tuck - b1 A B 811 | tuck - b0 A B 812 | outhook A B C 813 | inhook A B C 814 | tuck - f3 815 | tuck - f2 A 816 | tuck - f1 A B 817 | tuck - f0 A B C 818 | split + f0 b0 819 | split + f1 b1 820 | split + f2 b2 821 | split + f3 b3 822 | drop f0 823 | drop f1 824 | drop f2 825 | drop f3 826 | drop b0 827 | drop b1 828 | drop b2 829 | drop b3 830 | outhook A B C 831 | 832 | ; split - over A B 833 | inhook A B C 834 | tuck + b0 A B 835 | tuck + b1 A B 836 | tuck + b2 A B 837 | tuck + b3 A B 838 | outhook A B C 839 | inhook A B C 840 | tuck + f0 841 | tuck + f1 A 842 | tuck + f2 A B 843 | tuck + f3 A B C 844 | split - f3 b3 845 | split - f2 b2 846 | split - f1 b1 847 | split - f0 b0 848 | drop f0 849 | drop f1 850 | drop f2 851 | drop f3 852 | drop b0 853 | drop b1 854 | drop b2 855 | drop b3 856 | outhook A B C 857 | 858 | ; split + A over A B 859 | inhook A B C 860 | tuck - b3 A B 861 | tuck - b2 A B 862 | tuck - b1 A B 863 | tuck - b0 A B 864 | outhook A B C 865 | inhook A B C 866 | tuck - f3 867 | tuck - f2 A 868 | tuck - f1 A B 869 | tuck - f0 A B C 870 | split + f0 b0 A 871 | split + f1 b1 A 872 | split + f2 b2 A 873 | split + f3 b3 A 874 | drop f0 875 | drop f1 876 | drop f2 877 | drop f3 878 | drop b0 879 | drop b1 880 | drop b2 881 | drop b3 882 | outhook A B C 883 | 884 | ; split - A over A B 885 | inhook A B C 886 | tuck + b0 A B 887 | tuck + b1 A B 888 | tuck + b2 A B 889 | tuck + b3 A B 890 | outhook A B C 891 | inhook A B C 892 | tuck + f0 893 | tuck + f1 A 894 | tuck + f2 A B 895 | tuck + f3 A B C 896 | split - f3 b3 A 897 | split - f2 b2 A 898 | split - f1 b1 A 899 | split - f0 b0 A 900 | drop f0 901 | drop f1 902 | drop f2 903 | drop f3 904 | drop b0 905 | drop b1 906 | drop b2 907 | drop b3 908 | outhook A B C 909 | 910 | ; split + A B over A B 911 | inhook A B C 912 | tuck - b3 A B 913 | tuck - b2 A B 914 | tuck - b1 A B 915 | tuck - b0 A B 916 | outhook A B C 917 | inhook A B C 918 | tuck - f3 919 | tuck - f2 A 920 | tuck - f1 A B 921 | tuck - f0 A B C 922 | split + f0 b0 A B 923 | split + f1 b1 A B 924 | split + f2 b2 A B 925 | split + f3 b3 A B 926 | drop f0 927 | drop f1 928 | drop f2 929 | drop f3 930 | drop b0 931 | drop b1 932 | drop b2 933 | drop b3 934 | outhook A B C 935 | 936 | ; split - A B over A B 937 | inhook A B C 938 | tuck + b0 A B 939 | tuck + b1 A B 940 | tuck + b2 A B 941 | tuck + b3 A B 942 | outhook A B C 943 | inhook A B C 944 | tuck + f0 945 | tuck + f1 A 946 | tuck + f2 A B 947 | tuck + f3 A B C 948 | split - f3 b3 A B 949 | split - f2 b2 A B 950 | split - f1 b1 A B 951 | split - f0 b0 A B 952 | drop f0 953 | drop f1 954 | drop f2 955 | drop f3 956 | drop b0 957 | drop b1 958 | drop b2 959 | drop b3 960 | outhook A B C 961 | 962 | ; split + A B C over A B 963 | inhook A B C 964 | tuck - b3 A B 965 | tuck - b2 A B 966 | tuck - b1 A B 967 | tuck - b0 A B 968 | outhook A B C 969 | inhook A B C 970 | tuck - f3 971 | tuck - f2 A 972 | tuck - f1 A B 973 | tuck - f0 A B C 974 | split + f0 b0 A B C 975 | split + f1 b1 A B C 976 | split + f2 b2 A B C 977 | split + f3 b3 A B C 978 | drop f0 979 | drop f1 980 | drop f2 981 | drop f3 982 | drop b0 983 | drop b1 984 | drop b2 985 | drop b3 986 | outhook A B C 987 | 988 | ; split - A B C over A B 989 | inhook A B C 990 | tuck + b0 A B 991 | tuck + b1 A B 992 | tuck + b2 A B 993 | tuck + b3 A B 994 | outhook A B C 995 | inhook A B C 996 | tuck + f0 997 | tuck + f1 A 998 | tuck + f2 A B 999 | tuck + f3 A B C 1000 | split - f3 b3 A B C 1001 | split - f2 b2 A B C 1002 | split - f1 b1 A B C 1003 | split - f0 b0 A B C 1004 | drop f0 1005 | drop f1 1006 | drop f2 1007 | drop f3 1008 | drop b0 1009 | drop b1 1010 | drop b2 1011 | drop b3 1012 | outhook A B C 1013 | 1014 | ; split + over A B C 1015 | inhook A B C 1016 | tuck - b3 A B C 1017 | tuck - b2 A B C 1018 | tuck - b1 A B C 1019 | tuck - b0 A B C 1020 | outhook A B C 1021 | inhook A B C 1022 | tuck - f3 1023 | tuck - f2 A 1024 | tuck - f1 A B 1025 | tuck - f0 A B C 1026 | split + f0 b0 1027 | split + f1 b1 1028 | split + f2 b2 1029 | split + f3 b3 1030 | drop f0 1031 | drop f1 1032 | drop f2 1033 | drop f3 1034 | drop b0 1035 | drop b1 1036 | drop b2 1037 | drop b3 1038 | outhook A B C 1039 | 1040 | ; split - over A B C 1041 | inhook A B C 1042 | tuck + b0 A B C 1043 | tuck + b1 A B C 1044 | tuck + b2 A B C 1045 | tuck + b3 A B C 1046 | outhook A B C 1047 | inhook A B C 1048 | tuck + f0 1049 | tuck + f1 A 1050 | tuck + f2 A B 1051 | tuck + f3 A B C 1052 | split - f3 b3 1053 | split - f2 b2 1054 | split - f1 b1 1055 | split - f0 b0 1056 | drop f0 1057 | drop f1 1058 | drop f2 1059 | drop f3 1060 | drop b0 1061 | drop b1 1062 | drop b2 1063 | drop b3 1064 | outhook A B C 1065 | 1066 | ; split + A over A B C 1067 | inhook A B C 1068 | tuck - b3 A B C 1069 | tuck - b2 A B C 1070 | tuck - b1 A B C 1071 | tuck - b0 A B C 1072 | outhook A B C 1073 | inhook A B C 1074 | tuck - f3 1075 | tuck - f2 A 1076 | tuck - f1 A B 1077 | tuck - f0 A B C 1078 | split + f0 b0 A 1079 | split + f1 b1 A 1080 | split + f2 b2 A 1081 | split + f3 b3 A 1082 | drop f0 1083 | drop f1 1084 | drop f2 1085 | drop f3 1086 | drop b0 1087 | drop b1 1088 | drop b2 1089 | drop b3 1090 | outhook A B C 1091 | 1092 | ; split - A over A B C 1093 | inhook A B C 1094 | tuck + b0 A B C 1095 | tuck + b1 A B C 1096 | tuck + b2 A B C 1097 | tuck + b3 A B C 1098 | outhook A B C 1099 | inhook A B C 1100 | tuck + f0 1101 | tuck + f1 A 1102 | tuck + f2 A B 1103 | tuck + f3 A B C 1104 | split - f3 b3 A 1105 | split - f2 b2 A 1106 | split - f1 b1 A 1107 | split - f0 b0 A 1108 | drop f0 1109 | drop f1 1110 | drop f2 1111 | drop f3 1112 | drop b0 1113 | drop b1 1114 | drop b2 1115 | drop b3 1116 | outhook A B C 1117 | 1118 | ; split + A B over A B C 1119 | inhook A B C 1120 | tuck - b3 A B C 1121 | tuck - b2 A B C 1122 | tuck - b1 A B C 1123 | tuck - b0 A B C 1124 | outhook A B C 1125 | inhook A B C 1126 | tuck - f3 1127 | tuck - f2 A 1128 | tuck - f1 A B 1129 | tuck - f0 A B C 1130 | split + f0 b0 A B 1131 | split + f1 b1 A B 1132 | split + f2 b2 A B 1133 | split + f3 b3 A B 1134 | drop f0 1135 | drop f1 1136 | drop f2 1137 | drop f3 1138 | drop b0 1139 | drop b1 1140 | drop b2 1141 | drop b3 1142 | outhook A B C 1143 | 1144 | ; split - A B over A B C 1145 | inhook A B C 1146 | tuck + b0 A B C 1147 | tuck + b1 A B C 1148 | tuck + b2 A B C 1149 | tuck + b3 A B C 1150 | outhook A B C 1151 | inhook A B C 1152 | tuck + f0 1153 | tuck + f1 A 1154 | tuck + f2 A B 1155 | tuck + f3 A B C 1156 | split - f3 b3 A B 1157 | split - f2 b2 A B 1158 | split - f1 b1 A B 1159 | split - f0 b0 A B 1160 | drop f0 1161 | drop f1 1162 | drop f2 1163 | drop f3 1164 | drop b0 1165 | drop b1 1166 | drop b2 1167 | drop b3 1168 | outhook A B C 1169 | 1170 | ; split + A B C over A B C 1171 | inhook A B C 1172 | tuck - b3 A B C 1173 | tuck - b2 A B C 1174 | tuck - b1 A B C 1175 | tuck - b0 A B C 1176 | outhook A B C 1177 | inhook A B C 1178 | tuck - f3 1179 | tuck - f2 A 1180 | tuck - f1 A B 1181 | tuck - f0 A B C 1182 | split + f0 b0 A B C 1183 | split + f1 b1 A B C 1184 | split + f2 b2 A B C 1185 | split + f3 b3 A B C 1186 | drop f0 1187 | drop f1 1188 | drop f2 1189 | drop f3 1190 | drop b0 1191 | drop b1 1192 | drop b2 1193 | drop b3 1194 | outhook A B C 1195 | 1196 | ; split - A B C over A B C 1197 | inhook A B C 1198 | tuck + b0 A B C 1199 | tuck + b1 A B C 1200 | tuck + b2 A B C 1201 | tuck + b3 A B C 1202 | outhook A B C 1203 | inhook A B C 1204 | tuck + f0 1205 | tuck + f1 A 1206 | tuck + f2 A B 1207 | tuck + f3 A B C 1208 | split - f3 b3 A B C 1209 | split - f2 b2 A B C 1210 | split - f1 b1 A B C 1211 | split - f0 b0 A B C 1212 | drop f0 1213 | drop f1 1214 | drop f2 1215 | drop f3 1216 | drop b0 1217 | drop b1 1218 | drop b2 1219 | drop b3 1220 | outhook A B C 1221 | -------------------------------------------------------------------------------- /visualizer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Knitout Visualizer 6 | 7 | 177 | 178 |
179 | 180 | 181 | (no file loaded) 182 | 183 | 184 | 185 | 186 | 187 | github page; 188 | report a bug 189 |
190 | 191 |
192 | 193 | 194 |
195 |
196 | //import the knitout writer code and instantiate it as an object 197 | const knitout = require('knitout'); 198 | k = new knitout.Writer({carriers:['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']}); 199 | 200 | // add some headers relevant to this job 201 | k.addHeader('Machine','SWGXYZ'); 202 | k.addHeader('Gauge','15'); 203 | const Carrier = '7'; 204 | 205 | k.inhook(Carrier); 206 | for(var i=3;i>=0;i -= 2){ 207 | k.tuck("-","f"+i,Carrier); 208 | } 209 | for(var i=0;i<=3;i += 2){ 210 | k.tuck("+","f"+i,Carrier); 211 | } 212 | 213 | for(var i=3;i>=0;i--){ 214 | k.knit("-","f"+i,Carrier); 215 | } 216 | 217 | k.xfer("f0","b0"); 218 | k.xfer("f1","bs1"); 219 | k.xfer("f2","b2"); 220 | k.xfer("f3","bs3"); 221 | 222 | k.xfer("b0","fs0"); 223 | k.xfer("bs1","f1"); 224 | k.xfer("b2","fs2"); 225 | k.xfer("bs3","f3"); 226 | 227 | k.xfer("fs0", "b0"); 228 | k.xfer("fs2", "b2"); 229 | 230 | for(var i=0;i<2;i++){ 231 | k.knit("+","b0",Carrier); 232 | k.knit("+","b2",Carrier); 233 | k.knit("-","f3",Carrier); 234 | k.knit("-","f1",Carrier); 235 | } 236 | 237 | k.outhook(Carrier); 238 | 239 | k.write('wristband.k'); 240 | 241 |
242 |
243 | 244 | 248 | 249 |
250 | 251 |
252 | 253 |
254 | 255 |
256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 605 | 606 | 607 | -------------------------------------------------------------------------------- /write-test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log( 4 | ";!knitout-2\n"+ 5 | ";;Carriers: A B C\n"+ 6 | "; generated by write-test.js\n" 7 | ); 8 | 9 | ["tuck", "knit", "miss"].forEach(function(op){ 10 | ["", "A", "A B", "A B C"].forEach(function(yarns){ 11 | ["+","-"].forEach(function(dir){ 12 | let n; 13 | let y; 14 | if (dir === '-') { 15 | n = ["f0", "f1", "f2", "f3"]; 16 | y = ["", "A", "A B", "A B C"]; 17 | } else { 18 | n = ["f3", "f2", "f1", "f0"]; 19 | y = ["", "A", "A B", "A B C"]; 20 | } 21 | let d2 = (dir == '+' ? '-' : '+'); 22 | console.log( 23 | "\n"+ 24 | "; " + op + " " + dir + " " + yarns + "\n"+ 25 | "inhook A B C\n"+ 26 | "tuck " + d2 + " " + n[0] + " " + y[0] + "\n"+ 27 | "tuck " + d2 + " " + n[1] + " " + y[1] + "\n"+ 28 | "tuck " + d2 + " " + n[2] + " " + y[2] + "\n"+ 29 | "tuck " + d2 + " " + n[3] + " " + y[3] + "\n"+ 30 | op + " " + dir + " " + n[3] + " " + yarns + "\n"+ 31 | op + " " + dir + " " + n[2] + " " + yarns + "\n"+ 32 | op + " " + dir + " " + n[1] + " " + yarns + "\n"+ 33 | op + " " + dir + " " + n[0] + " " + yarns + "\n"+ 34 | "drop f0\n"+ 35 | "drop f1\n"+ 36 | "drop f2\n"+ 37 | "drop f3\n"+ 38 | "outhook A B C" 39 | ); 40 | }); 41 | }); 42 | }); 43 | 44 | 45 | 46 | ["split"].forEach(function(op){ 47 | ["", "A", "A B", "A B C"].forEach(function(bg){ 48 | ["", "A", "A B", "A B C"].forEach(function(yarns){ 49 | ["+","-"].forEach(function(dir){ 50 | let n; 51 | let n2; 52 | let y; 53 | if (dir === '-') { 54 | n = ["f0", "f1", "f2", "f3"]; 55 | n2 = ["b0", "b1", "b2", "b3"]; 56 | y = ["", "A", "A B", "A B C"]; 57 | } else { 58 | n = ["f3", "f2", "f1", "f0"]; 59 | n2 = ["b3", "b2", "b1", "b0"]; 60 | y = ["", "A", "A B", "A B C"]; 61 | } 62 | let d2 = (dir == '+' ? '-' : '+'); 63 | console.log( 64 | "\n"+ 65 | "; " + op + " " + dir + " " + yarns + " over " + bg + "\n"+ 66 | "inhook A B C\n"+ 67 | "tuck " + d2 + " " + n2[0] + " " + bg + "\n"+ 68 | "tuck " + d2 + " " + n2[1] + " " + bg + "\n"+ 69 | "tuck " + d2 + " " + n2[2] + " " + bg + "\n"+ 70 | "tuck " + d2 + " " + n2[3] + " " + bg + "\n"+ 71 | "outhook A B C\n"+ 72 | "inhook A B C\n"+ 73 | "tuck " + d2 + " " + n[0] + " " + y[0] + "\n"+ 74 | "tuck " + d2 + " " + n[1] + " " + y[1] + "\n"+ 75 | "tuck " + d2 + " " + n[2] + " " + y[2] + "\n"+ 76 | "tuck " + d2 + " " + n[3] + " " + y[3] + "\n"+ 77 | op + " " + dir + " " + n[3] + " " + n2[3] + " " + yarns + "\n"+ 78 | op + " " + dir + " " + n[2] + " " + n2[2] + " " + yarns + "\n"+ 79 | op + " " + dir + " " + n[1] + " " + n2[1] + " " + yarns + "\n"+ 80 | op + " " + dir + " " + n[0] + " " + n2[0] + " " + yarns + "\n"+ 81 | "drop f0\n"+ 82 | "drop f1\n"+ 83 | "drop f2\n"+ 84 | "drop f3\n"+ 85 | "drop b0\n"+ 86 | "drop b1\n"+ 87 | "drop b2\n"+ 88 | "drop b3\n"+ 89 | "outhook A B C" 90 | ); 91 | }); 92 | }); 93 | }); 94 | }); 95 | --------------------------------------------------------------------------------