1/*global describe read_png */ 2 3var TRC_R = 3; 4var CLR_W = 8; 5 6let bShowDt = false; 7 8let mouse_mode = 0; 9 10let jsr = 0x5EED; 11// jsr = ~~(Math.random()*4294967295) 12 13 14function rand(){ 15 jsr^=(jsr<<17); 16 jsr^=(jsr>>>13); 17 jsr^=(jsr<<5); 18 jsr>>>=0; 19 return jsr/4294967295; 20} 21 22 23function load_png(path){ 24 let xhr = new XMLHttpRequest(); 25 // xhr.responseType = 'arraybuffer'; 26 xhr.overrideMimeType('text/plain; charset=x-user-defined'); 27 xhr.open("GET", path, false); 28 29 xhr.send(); 30 let res = xhr.response; 31 // console.log(res); 32 let bytes = Uint8Array.from(res, c => c.charCodeAt(0)); 33 34 let [data,w,h] = read_png(bytes); 35 36 let D = []; 37 for (let i = 0; i < data.length; i+= 4){ 38 let r = data[i]; 39 let g = data[i+1]; 40 let b = data[i+2]; 41 let c = (r << 16) | (g << 8) | b; 42 D.push(c); 43 } 44 45 return [D,w,h]; 46} 47 48 49 50 51 52function dist_transform(b,m,n) { 53 // Meijster distance 54 // adapted from https://github.com/parmanoir/Meijster-distance 55 function EDT_f(x, i, g_i) { 56 return (x - i) * (x - i) + g_i * g_i; 57 } 58 function EDT_Sep(i, u, g_i, g_u) { 59 return Math.floor((u * u - i * i + g_u * g_u - g_i * g_i) / (2 * (u - i))); 60 } 61 // First phase 62 let infinity = m + n; 63 let g = new Array(m * n).fill(0); 64 for (let x = 0; x < m; x++) { 65 if (b[x + 0 * m]){ 66 g[x + 0 * m] = 0; 67 }else{ 68 g[x + 0 * m] = infinity; 69 } 70 // Scan 1 71 for (let y = 1; y < n; y++) { 72 if (b[x + y * m]){ 73 g[x + y * m] = 0; 74 }else{ 75 g[x + y * m] = 1 + g[x + (y - 1) * m]; 76 } 77 } 78 // Scan 2 79 for (let y = n - 2; y >= 0; y--) { 80 if (g[x + (y + 1) * m] < g[x + y * m]){ 81 g[x + y * m] = 1 + g[x + (y + 1) * m]; 82 } 83 } 84 } 85 86 // Second phase 87 let dt = new Array(m * n).fill(0); 88 let s = new Array(m).fill(0); 89 let t = new Array(m).fill(0); 90 let q = 0; 91 let w; 92 for (let y = 0; y < n; y++) { 93 q = 0; 94 s[0] = 0; 95 t[0] = 0; 96 97 // Scan 3 98 for (let u = 1; u < m; u++) { 99 while (q >= 0 && EDT_f(t[q], s[q], g[s[q] + y * m]) > EDT_f(t[q], u, g[u + y * m])){ 100 q--; 101 } 102 if (q < 0) { 103 q = 0; 104 s[0] = u; 105 } else { 106 w = 1 + EDT_Sep(s[q], u, g[s[q] + y * m], g[u + y * m]); 107 if (w < m) { 108 q++; 109 s[q] = u; 110 t[q] = w; 111 } 112 } 113 } 114 // Scan 4 115 for (let u = m - 1; u >= 0; u--) { 116 let d = EDT_f(u, s[q], g[s[q] + y * m]); 117 118 d = Math.floor(Math.sqrt(d)); 119 dt[u + y * m] = d; 120 if (u == t[q]) q--; 121 } 122 } 123 return dt; 124} 125 126let W = 128; 127let H = 128; 128let B = new Array(W*H).fill(0); 129let RAD = 3; 130 131let Bhist = [B.slice()]; 132 133let cnv = document.createElement("canvas"); 134cnv.width = W; 135cnv.height = H; 136let ctx = cnv.getContext('2d',{ willReadFrequently: true }); 137ctx.fillRect(0,0,W,H) 138document.body.appendChild(cnv); 139 140display(B); 141 142 143let dtdiv = document.createElement("div"); 144document.body.appendChild(dtdiv); 145 146 147function display(im){ 148 // for (let i = 0; i < H; i++){ 149 // for (let j = 0; j < W; j++){ 150 // ctx.fillStyle='#'+im[i*W+j].toString(16).padStart(6,'0') 151 // ctx.fillRect(j*SCL,i*SCL,SCL,SCL); 152 // } 153 // } 154 155 let imd = ctx.getImageData(0,0,W,H); 156 for (let i = 0; i < im.length; i ++){ 157 imd.data[i*4 ] = (im[i]>>16)&0xff; 158 imd.data[i*4+1] = (im[i]>>8)&0xff; 159 imd.data[i*4+2] = im[i]&0xff; 160 imd.data[i*4+3] = 255; 161 } 162 ctx.putImageData(imd,0,0); 163} 164 165function get_color_set(B){ 166 return Array.from(new Set(B)).filter(x=>x); 167} 168 169let mouseX; 170let mouseY; 171let mouseIsDown = false; 172 173let curr_color = 0xff3f3f; 174 175 176 177 178 179 180 181let openset = new Set(); 182let came_from; 183let gscore; 184let fscore; 185 186let astar_old_size = 0; 187 188function astar(im, x0, y0){ 189 190 let neighbors = [0,0,0,0]; 191 let n_neighbors = 0; 192 193 function heur_dist(a){ 194 if (im.data[a] == -2){ 195 return 0; 196 }else if (im.data[a] == -1){ 197 return 1; 198 }else{ 199 return im.data[a]; 200 } 201 } 202 function reconstruct_path(w, current){ 203 let path = []; 204 path.push([current%w, ~~(current/w)]); 205 while ( came_from[current] != -1){ 206 current = came_from[current]; 207 path.push([current%w, ~~(current/w)]); 208 } 209 return path; 210 } 211 212 function get_neighbors(im, n){
213 let c = n % im.w; 214 let r = ~~(n / im.w); 215 n_neighbors = 0; 216 if (c > 0 && im.data[n-1]<255) neighbors[n_neighbors++] = n-1; 217 if (c < im.w-1 && im.data[n+1]<255) neighbors[n_neighbors++] = n+1; 218 if (r > 0 && im.data[n-im.w]<255) neighbors[n_neighbors++] = n-im.w; 219 if (r < im.h-1 && im.data[n+im.w]<255) neighbors[n_neighbors++] = n+im.w; 220 } 221 222 let w = im.w; 223 let h = im.h; 224 let w_h = w*h; 225 226 if (astar_old_size < w_h){ 227 came_from = new Int32Array(w_h); 228 gscore = new Float32Array(w_h); 229 fscore = new Float32Array(w_h); 230 astar_old_size = w_h; 231 } 232 233 let start = y0*w+x0; 234 235 openset.clear(); 236 237 came_from[start] = -1; 238 openset.add(start); 239 240 for (let i = 0; i < w_h; i++) gscore[i] = Infinity; 241 for (let i = 0; i < w_h; i++) fscore[i] = Infinity; 242 for (let i = 0; i < w_h; i++) came_from[i] = -1; 243 244 gscore[start] = 0; 245 fscore[start] = heur_dist(start); 246 247 248 while (openset.size){ 249 // console.log(n_openset,im.data.length); 250 251 let amin = -1; 252 let fmin = Infinity; 253 for (let q of openset){ 254 let f = fscore[q]; 255 if ( f < fmin){ 256 fmin = f; 257 amin = q; 258 } 259 } 260 261 let current = amin; 262 263 if (im.data[current] == -2){ 264 return reconstruct_path(w,current); 265 } 266 267 openset.delete(amin); 268 269 get_neighbors(im,current); 270 271 for (let i = 0; i < n_neighbors; i++){ 272 let neighbor = neighbors[i]; 273 let penalty = Math.max(im.data[neighbor],0); 274 275 let tentative_gscore = gscore[current] + 1 + penalty; 276 if (tentative_gscore < gscore[neighbor]){ 277 came_from[neighbor] = current; 278 gscore[neighbor] = tentative_gscore; 279 fscore[neighbor] = gscore[neighbor] + heur_dist(neighbor); 280 openset.add(neighbor); 281 } 282 } 283 } 284 return null; 285} 286 287 288 289function bucketfill(im0,x0,y0,val){ 290 let val0 = im0[y0*W+x0]; 291 if (val0 == val){ 292 return; 293 } 294 let Q = []; 295 Q.push([x0,y0]); 296 while (Q.length){ 297 let [x,y] = Q.pop(); 298 if (x > 0 && im0[y*W+(x-1)]==val0){ 299 im0[y*W+(x-1)] = val; 300 Q.push([x-1,y]); 301 } 302 if (x < W-1 && im0[y*W+(x+1)] == val0){ 303 im0[y*W+(x+1)] = val; 304 Q.push([x+1,y]); 305 } 306 if (y > 0 && im0[(y-1)*W+x] == val0){ 307 im0[(y-1)*W+x] = val; 308 Q.push([x,y-1]); 309 } 310 if (y < H-1 && im0[(y+1)*W+x]== val0){ 311 im0[(y+1)*W+x] = val; 312 Q.push([x,y+1]); 313 } 314 } 315} 316 317function floodblob(im0,x0,y0,val){ 318 let Q = []; 319 let im1 = new Array(im0.length).fill(0); 320 Q.push([x0,y0]); 321 im1[y0*W+x0] = -1; 322 while (Q.length){ 323 let [x,y] = Q.pop(); 324 if (x > 0 && im0[y*W+(x-1)]==val && im1[y*W+(x-1)] == 0){ 325 im1[y*W+(x-1)] = -1; 326 Q.push([x-1,y]); 327 } 328 if (x < W-1 && im0[y*W+(x+1)] == val && im1[y*W+(x+1)] ==0){ 329 im1[y*W+(x+1)] = -1; 330 Q.push([x+1,y]); 331 } 332 if (y > 0 && im0[(y-1)*W+x] == val && im1[(y-1)*W+x]==0){ 333 im1[(y-1)*W+x] = -1; 334 Q.push([x,y-1]); 335 } 336 if (y < H-1 && im0[(y+1)*W+x]== val && im1[(y+1)*W+x]==0){ 337 im1[(y+1)*W+x] = -1; 338 Q.push([x,y+1]); 339 } 340 } 341 let needwork = false; 342 for (let i = 0; i < H; i++){ 343 for (let j = 0; j < W; j++){ 344 if (im1[i*W+j] == 0){ 345 if (im0[i*W+j] == val){ 346 im1[i*W+j] = -2; 347 needwork = true; 348 // }else if (im0[i*W+j] != 0 && !is_nc(im0[i*W+j])){ 349 }else if (im0[i*W+j] != 0){ 350 im1[i*W+j] = 255; 351 } 352 } 353 } 354 } 355 if (!needwork) return null; 356 let dt0 = dist_transform(im1.map(x=>x==255),W,H); 357 358 for (let i = 0; i < W*H; i++){ 359 if (dt0[i] < CLR_W+TRC_R && im1[i] >= 0){ 360 im1[i] = 255; 361 } 362 // if (im0[i] && is_nc(im0[i])){ 363 // im1[i] = 255; 364 // } 365 } 366 367 // let dt1 = dist_transform(B0.map(x=>x&&(x!=val)),W,H); 368 369 let dt = dist_transform(im1.map(x=>x==-2),W,H); 370 for (let i = 0; i < W*H; i++){ 371 if (im1[i] == 0){ 372 im1[i] = Math.min(dt[i]*0.5,254); 373 } 374 } 375 376 let rc0 = rand(); 377 378 if (rc0 < 0.33){ 379 for (let i = 0; i < W*H; i++){ 380 if (im1[i] >= 0 && im1[i] < 255){ 381 // im1[i] = im1[i]*(255-dt0[i])/255; 382 im1[i] = im1[i]+Math.max(1,250-dt0[i]*5)*0.5; 383 im1[i] = Math.min(im1[i],200); 384 } 385 } 386 }else if (rc0 < 0.67){ 387 let dt00 = dist_transform(Bhist[0].map(x=>x&&(x!=val)&&!is_nc(x)),W,H); 388 for (let i = 0; i < W*H; i++){ 389 if (im1[i] >= 0 && im1[i] < 255){ 390 // im1[i] = im1[i]*(255-dt0[i])/255; 391 im1[i] = im1[i]+Math.max(1,250-dt00[i]*10)*0.3; 392 im1[i] = Math.min(im1[i],200); 393 } 394 } 395 } 396 397 let rc = rand(); 398 let amt = 100+rand()*100; 399 400 for (let i = 0; i < H; i++){ 401 for (let j = 0; j < W; j++){ 402 if (im1[i*W+j] > 0 && im1[i*W+j] < 255){ 403 404 if (rc < 0.2){ 405 im1[i*W+j] = im1[i*W+j] + (j/W-0.5)*amt; 406 }else if (rc < 0.4){ 407 im1[i*W+j] = im1[i*W+j] + ((W-j)/W-0.5)*amt; 408 }else if (rc < 0.6){ 409 im1[i*W+j] = im1[i*W+j] + (i/H-0.5)*amt; 410 }else if (rc < 0.8){ 411 im1[i*W+j] = im1[i*W+j] + ((H-i)/H-0.5)*amt; 412 } 413
414 im1[i*W+j] = Math.max(1,Math.min(im1[i*W+j],254)); 415 } 416 } 417 } 418 419 420 if (bShowDt){ 421 let cnv = document.createElement("canvas"); 422 cnv.width = W; 423 cnv.height = H; 424 let ctx = cnv.getContext('2d'); 425 let imd = ctx.getImageData(0,0,W,H); 426 for (let i = 0; i < im1.length; i ++){ 427 if (im1[i] == -1){ 428 imd.data[i*4 ] = 255; 429 imd.data[i*4+1] = 0; 430 imd.data[i*4+2] = 0; 431 }else if (im1[i] == -2){ 432 imd.data[i*4 ] = 0; 433 imd.data[i*4+1] = 255; 434 imd.data[i*4+2] = 0; 435 }else{ 436 imd.data[i*4 ] = im1[i]; 437 imd.data[i*4+1] = im1[i]; 438 imd.data[i*4+2] = im1[i]; 439 } 440 imd.data[i*4+3] = 255; 441 } 442 ctx.putImageData(imd,0,0); 443 dtdiv.appendChild(cnv); 444 } 445 446 return im1; 447} 448 449let stamp; 450 451function update_trc_r(){ 452 let TRC_W = TRC_R*2+1; 453 stamp = new Array(TRC_W*TRC_W).fill(0); 454 for (let i = 0; i < TRC_W; i++){ 455 for (let j = 0; j < TRC_W; j++){ 456 let d = Math.hypot(i-TRC_R, j-TRC_R); 457 if (d < TRC_R+0.5){ 458 stamp[i*TRC_W+j] = 1; 459 } 460 } 461 } 462} 463 464 465function shuffle(array) { 466 for (let i = array.length - 1; i > 0; i--) { 467 const j = Math.floor(rand() * (i + 1)); 468 [array[i], array[j]] = [array[j], array[i]]; 469 } 470} 471 472 473function is_nc(val){ 474 if (val == 0) return false; 475 let r = (val >> 16)&0xff; 476 let g = (val >> 8)&0xff; 477 let b = (val)&0xff; 478 return r == g && g == b; 479} 480 481function do_iter(B,xhint,yhint){ 482 let CS = []; 483 let seen = new Array(B.length).fill(0); 484 let i = yhint*W+xhint; 485 for (let r = 0; r < 10000; r++){ 486 if (r){ 487 i = ~~(rand()*B.length); 488 } 489 // i = 130*W+198; 490 if (seen[i]) continue; 491 let val = B[i]; 492 if (val && !is_nc(val)){ 493 494 let x = i%W; 495 let y = ~~(i/W); 496 let C = floodblob(B,x,y,val); 497 498 if (C){ 499 for (let j = 0; j < C.length; j++){ 500 if (C[j] == -1){ 501 seen[j] = 1; 502 } 503 } 504 CS.push([C,x,y]); 505 break; 506 } 507 } 508 } 509 if (!CS.length){ 510 console.log("all good!") 511 return -1; 512 } 513 shuffle(CS); 514 let [C,x,y] = CS[0]; 515 let val = B[y*W+x]; 516 let css = '#'+val.toString(16).padStart(6,'0'); 517 console.log(`%c solving for ${css} at ${x},${y}`,'background: '+css+'; color: white'); 518 519 let path = astar({data:C,w:W,h:H},x,y); 520 if (!path){ 521 console.log("no solution!"); 522 return -2; 523 } 524 let B0 = Bhist[Bhist.length-1]; 525 let TRC_W = TRC_R*2+1; 526 for (let j = 0; j < path.length; j++){ 527 let [x,y] = path[j]; 528 if (B0[y*W+x] == val){ 529 continue; 530 } 531 for (let ki = 0; ki < TRC_W; ki++){ 532 for (let kj = 0; kj < TRC_W; kj ++){ 533 if (stamp[ki*TRC_W+kj]){ 534 B[(y+ki-TRC_R)*W+(x+kj-TRC_R)] = val; 535 } 536 } 537 } 538 } 539 Bhist.push(B.slice()); 540 display(B); 541 return 0; 542} 543 544 545// function solve(){ 546// let Bx = B.slice(); 547// let ret; 548// do{ 549// ret = do_iter(Bx); 550// }while (ret == 0); 551 552// if (ret == -2){ 553// solve(); 554// } 555// for (let i = 0; i < B.length; i++) B[i] = Bx[i]; 556 557// } 558 559 560var failure_reaction = function(){ 561 let sel = document.getElementById("sel-fail"); 562 if (sel.value == 'retry'){ 563 return true; 564 }else if (sel.value == 'stop'){ 565 throw "failed, stopping as specified."; 566 }else if (sel.value == 'ask'){ 567 return confirm("failed. try again?"); 568 } 569 throw "up"; 570} 571 572function solve(){ 573 dtdiv.innerHTML=''; 574 575 console.log("seed: 0x"+jsr.toString(16),"time: "+new Date()); 576 Bhist = Bhist.slice(0,1); 577 let Bx = B.slice(); 578 function iter(){ 579 // console.log("...") 580 let ret = do_iter(Bx); 581 if (!ret){ 582 return setTimeout(iter,1); 583 } 584 585 if (ret == -2){ 586 if (failure_reaction()){ 587 return setTimeout(solve,1); 588 } 589 } 590 for (let i = 0; i < B.length; i++) B[i] = Bx[i]; 591 } 592 iter(); 593} 594 595 596 597 598 599 600 601cnv.addEventListener('mousedown',function(event){ 602 let box = cnv.getBoundingClientRect(); 603 mouseX = event.clientX-box.left; 604 mouseY = event.clientY-box.top; 605 mouseIsDown = true; 606 if (mouse_mode == 0){ 607 do_iter(B,mouseX,mouseY) 608 }else if (mouse_mode == 1){ 609 mouse_draw(); 610 }else if (mouse_mode == 2){ 611 curr_color = B[(~~mouseY)*W+(~~mouseX)]; 612 613 update_curr_color(); 614 }else if (mouse_mode == 3){ 615 616 bucketfill(B,(~~mouseX),(~~mouseY),curr_color); 617 display(B); 618 Bhist.push(B.slice()); 619 } 620}) 621cnv.addEventListener('mousemove',function(event){ 622 let box = cnv.getBoundingClientRect(); 623 mouseX = event.clientX-box.left; 624 mouseY = event.clientY-box.top; 625 if (mouse_mode == 1){ 626 if (mouseIsDown){ 627 mouse_draw(); 628 } 629 } 630}) 631cnv.addEventListener('mouseup',function(event){ 632 mouseIsDown = false;
633 if (mouse_mode == 1){ 634 display(B); 635 Bhist.push(B.slice()); 636 } 637}) 638function mouse_draw(){ 639 let x = ~~(mouseX); 640 let y = ~~(mouseY); 641 for (let i = Math.max(y-RAD,0); i < Math.min(y+RAD+1,H-1); i++){ 642 for (let j = Math.max(x-RAD,0); j < Math.min(x+RAD+1,W-1); j++){ 643 B[i*W+j] = curr_color; 644 } 645 } 646 ctx.fillStyle = '#'+curr_color.toString(16).padStart(6,'0') 647 ctx.fillRect(x-RAD,y-RAD,(RAD*2+1),(RAD*2+1)); 648} 649 650 651let examples = { 652 'hello.D11C.echo':{seed:0xc288bec7/*0xc0e13f7a*/,conf:{TRC_R:3,CLR_W:8},url:'/autoroute/glitch-assets/hello.D11C.echo.traces_exterior.png'}, 653 'hello.ftdi.45':{seed:0x88b213fd/*0xaa69c87d*/,conf:{TRC_R:3,CLR_W:8},url:'/autoroute/glitch-assets/hello.ftdi.45.traces_exterior.png'}, 654 'hello.ISP.44':{seed:0xd9f6d431/*0xa38dd442*/,conf:{TRC_R:3,CLR_W:8},url:'/autoroute/glitch-assets/hello.ISP.44.traces_exterior.png'}, 655 'hello.ILI9341.SW.D21E':{seed:0xed12d2c6,conf:{TRC_R:3,CLR_W:8},url:'/autoroute/glitch-assets/hello.ILI9341.SW.D21E.traces_exterior.png'}, 656} 657 658function load_example(k){ 659 console.log(k); 660 curr_example = k; 661 [B,W,H] = load_png(examples[k].url); 662 cnv.width = W; 663 cnv.height = H; 664 Bhist = [B.slice()]; 665 jsr = examples[k].seed; 666 for (let q in examples[k].conf){ 667 window[q] = examples[k].conf[q]; 668 } 669 update_trc_r(); 670 display(B); 671 make_gui(); 672} 673 674let curr_example = 'hello.D11C.echo'; 675 676load_example(curr_example); 677 678function update_curr_color(){ 679 let sp = document.getElementById('span-curr-color'); 680 let s = '#'+curr_color.toString(16).padStart(6,'0'); 681 sp.innerHTML = s; 682 sp.style.background=s; 683} 684 685function upload_user_png(){ 686 var fileToLoad = document.getElementById("file_upl").files[0]; 687 var fileReader = new FileReader(); 688 fileReader.onload = function(fileLoadedEvent){ 689 let ab = fileLoadedEvent.target.result; 690 691 let bytes = new Uint8Array(ab); 692 console.log(bytes); 693 let [data,w,h] = read_png(bytes); 694 let D = []; 695 for (let i = 0; i < data.length; i+= 4){ 696 let r = data[i]; 697 let g = data[i+1]; 698 let b = data[i+2]; 699 let c = (r << 16) | (g << 8) | b; 700 D.push(c); 701 } 702 B = D; 703 W = w; 704 H = h; 705 cnv.width = W; 706 cnv.height = H; 707 display(B); 708 Bhist = [B.slice()]; 709 }; 710 fileReader.readAsArrayBuffer(fileToLoad); 711} 712 713let img_msg = `\ 714blobs of the same color will be connected (e.g. red->red, blue->blue), except: 715black (#000) is the empty space. 716white and grays are NC (avoided by other tracks, but not connected to any). 717make sure there is no anti-aliasing! 718png only! 719`; 720 721 722function make_gui(){ 723 let div= document.getElementById('gui'); 724 if (!div) div = document.createElement('div'); 725 div.id = 'gui'; 726 div.style="font-family:monospace; position:absolute;width:320px;height:720px;top:0px;right:0px;border:1px dotted black;background:whitesmoke;padding:10px;white-space:pre" 727 let htm = `load example: <select id="sel-eg" onchange="load_example(this.value)"> 728 `; 729 for (let k in examples){ 730 htm += `<option value="${k}" ${curr_example==k?'selected':''}>${k}</option>`; 731 } 732 htm += `</select><button onclick="load_example(document.getElementById('sel-eg').value)">â³</button> 733or upload png: 734<input type="file" id="file_upl" onchange="upload_user_png()"></input>\ 735<button onclick="alert(img_msg)">?</button> 736seed: <input id="inp-seed" value="${jsr}"></input>\ 737<button onclick="jsr=Number(document.getElementById('inp-seed').value)">set</button>\ 738<button onclick="jsr=(~~(Math.random()*4294967295))>>>0;document.getElementById('inp-seed').value=jsr">gen</button> 739track radius (px, " width-1/2): <input type="number" value="${TRC_R}" min="1" max="99" oninput="TRC_R=Number(this.value)"/> 740clearance (px): <input type="number" value="${CLR_W}" min="1" max="99" oninput="CLR_W=Number(this.value)"/> 741upon failure: <select id="sel-fail"> 742 <option>retry</option> 743 <option>stop</option> 744 <option>ask</option> 745</select> 746<input type="checkbox" ${bShowDt?'checked':''} onchange="bShowDt=this.checked"/>dbg: show dist transforms 747<button onclick="solve()" style="font-size:32px">solve</button><--click me 748<button onclick="if (Bhist.length>1){Bhist.pop();B=Bhist[Bhist.length-1].slice();display(B)}">undo</button>\ 749<button onclick="B.fill(0);Bhist=[B.slice()];display(B)">clear</button> 750mouse function: <select onchange="mouse_mode=this.selectedIndex"> 751 <option>route</option> 752 <option>paint</option> 753 <option>colorpicker</option> 754 <option>bucketfill</option> 755</select> 756current color: <span id="span-curr-color"></span> 757quick colors: 758<div style="white-space:normal"> 759` 760 for (let i = 0; i < 64; i++){ 761 762 let r = (((i >> 4)&3) << 6) + 63; 763 let g = (((i >> 2)&3) << 6) + 63; 764 let b = ((i & 3) << 6 ) + 63;
765 let c = (r << 16) | (g << 8) | b; 766 let s = '#'+c.toString(16).padStart(6,'0'); 767 htm += `<button onclick="curr_color=${c};update_curr_color()" style="background:${s};font-family:monospace;margin:0px;">${s}</button>` 768 } 769 htm += `<button onclick="curr_color=0;update_curr_color()" style="background:black;font-family:monospace;margin:0px;color:white">#000000</button>` 770htm += ` 771</div> 772open console to see progress details. 773 774<a href="https://glitch.com/edit/#!/autoroute">source code</a> 775 ` 776 div.innerHTML = htm; 777 document.body.appendChild(div); 778 update_curr_color(); 779} 780 781 782make_gui();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.