StringArtGenerator/index.html

713 wiersze
68 KiB
HTML

2019-03-01 02:35:52 +00:00
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
2019-03-05 02:24:14 +00:00
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="style.css">
2019-03-05 02:24:14 +00:00
2019-03-05 13:28:36 +00:00
<title>String Art Generator</title>
</head>
2019-03-01 02:35:52 +00:00
<body>
2019-03-05 02:24:14 +00:00
<div class="container">
<div class="jumbotron" style="padding-bottom: 20px !important;">
2019-03-05 02:59:14 +00:00
<div style="float: left; position: absolute; top: 20px;">
<a href="https://github.com/halfmonty/StringArtGenerator">Find on Github</a>
</div>
2019-03-05 02:24:14 +00:00
<h1>String Art Generator</h1>
<div style="float: right; margin-top: -40px;">
Already have steps generated?
<button class="btn btn-primary" onclick="onHasSteps();">Click Here</button>
</div>
<h2 id="status">Please wait: loading...</h2><br/>
<span>Wait for generate to complete loading.<br/>
For best results use close up high contrast pictures.<br/>
If the page crashes (which it may) just refresh or close and re-open the tab/window.<br/><br/>
</span>
<br/>
<div><h5>Variables to play with:</h5></div>
<div class="row">
<div class="col">
<div class="form-group">
<label for="numberOfPins">Number of Pins</label>
<input type="text" class="form-control" id="numberOfPins">
</div>
</div>
<div class="col">
<div class="form-group">
<label for="numberOfLines">Number of Lines</label>
<input type="text" class="form-control" id="numberOfLines">
</div>
</div>
<div class="col">
<div class="form-group">
<label for="lineWeight">Line Weight</label>
<input type="text" class="form-control" id="lineWeight">
</div>
</div>
</div>
<label class="btn btn-primary btn-file">
Click Here and select an Image to Start <input type="file" id="fileInput" style="display: none;">
</label>
</div>
2019-03-05 02:24:14 +00:00
<div class="row">
<div class="col">
<div id="step1" class="inputoutput center hidden">
<img class="centerImage" id="imageSrc" alt="No Image" />
</div>
<div id="step2" class="inputoutput center hidden">
<div class="caption">Cropped and Grayscaled:</div>
<canvas class="centerCanvasMedium" id="canvasOutput" ></canvas>
</div>
<div id="step3" class="inputoutput center hidden">
<div class="caption">String Art Output:</div>
<div id="drawStatus"></div>
<canvas class="centerCanvasLarge" id="canvasOutput2" ></canvas>
</div>
<div id="showPins" class="inputoutput center hidden">
<textarea id="pinsOutput" rows="10" cols="100"></textarea>
<div class="centerBorderless">
<span>These examples will overwrite your generated output. Please copy your output first if you need to before clicking an example button.</span><br/><br/>
<button style="float: left;" onclick="example1();">Example 1</button>
<button style="float: left;" onclick="example2();">Example 2</button>
<button style="float: left;" onclick="example3();">Example 3</button>
</div><br/><br/>
<span>Copy these numbers to save for later so you don't have to generate them again.</span><br/>
<span>If you copied the numbers from a previous run, paste them above.</span><br/>
<span>These numbers are used to help walk you through creating this string art</span><br/><br/>
<div style="height: 30px;">
<button style="float: left;" onclick="startCreating();">Start Creating</button>
<button style="float: right;" onclick="startDrawing();">Just Draw</button>
</div>
</div>
<div id="incrementalDrawing" class="inputoutput center hidden">
<div class="caption">String Art Output:</div>
<span id="incrementalCurrentStep"></span>
<div id="drawStatus"></div>
<canvas class="centerCanvasLarge" id="canvasOutput3" ></canvas>
<div style="height: 30px;">
<button style="float: left;" onclick="lastStep();">Last Step</button>
<button style="float: right;" onclick="nextStep();">Next Step</button>
</div>
</div>
</div>
</div>
2019-03-05 02:24:14 +00:00
</div>
2019-03-05 02:24:14 +00:00
2019-03-01 02:35:52 +00:00
2019-03-01 02:35:52 +00:00
<script type="text/javascript">
let IMG_SIZE = 500;
let MAX_LINES = 4000;// 4000;
2019-03-01 02:35:52 +00:00
let N_PINS = 36*8;
let MIN_LOOP = 20;
let MIN_DISTANCE = 20;
let LINE_WEIGHT = 20;
2019-03-01 02:35:52 +00:00
let FILENAME = "";
let SCALE = 20;
let HOOP_DIAMETER = 0.625;
let img;
// set up element variables
let imgElement = document.getElementById("imageSrc")
let inputElement = document.getElementById("fileInput");
inputElement.addEventListener("change", (e) => {
imgElement.src = URL.createObjectURL(e.target.files[0]);
}, false);
var ctx=document.getElementById("canvasOutput").getContext("2d");
var ctx2=document.getElementById("canvasOutput2").getContext("2d");
var ctx3=document.getElementById("canvasOutput3").getContext("2d");
2019-03-01 02:35:52 +00:00
let status = document.getElementById("status");
let drawStatus = document.getElementById("drawStatus");
let showPins = document.getElementById("showPins");
let pinsOutput = document.getElementById("pinsOutput");
let incrementalDrawing = document.getElementById("incrementalDrawing");
let incrementalCurrentStep = document.getElementById("incrementalCurrentStep");
2019-03-04 04:28:51 +00:00
let numberOfPins = document.getElementById("numberOfPins");
let numberOfLines = document.getElementById("numberOfLines");
let lineWeight = document.getElementById("lineWeight");
2019-03-01 02:35:52 +00:00
let length;
var R = {};
//pre initilization
let pin_coords;
let center;
let radius;
let line_cache_y;
let line_cache_x;
let line_cache_length;
let line_cache_weight;
//line variables
let error;
let img_result;
let result;
let line_mask;
let line_sequence;
let pin;
let thread_length;
let last_pins;
let listenForKeys = false;
//*******************************
// Line Generation
//*******************************
2019-03-01 02:35:52 +00:00
imgElement.onload = function() {
listenForKeys = false;
showStep(1);
showPins.classList.add('hidden');
incrementalDrawing.classList.add('hidden');
// Take uploaded picture, square up and put on canvas
base_image = new Image();
base_image.src = imgElement.src;
ctx.canvas.width = IMG_SIZE;
ctx.canvas.height = IMG_SIZE;
ctx2.canvas.weight = IMG_SIZE * 2;
ctx2.canvas.height = IMG_SIZE * 2;
ctx.clearRect(0,0, IMG_SIZE, IMG_SIZE);
2019-03-05 02:24:14 +00:00
var selectedWidth = base_image.width;
var selectedHeight = base_image.height;
var xOffset = 0;
var yOffset = 0;
// square crop center of picture
if(base_image.height > base_image.width){
selectedWidth = base_image.width;
selectedHeight = base_image.width;
yOffset = Math.floor((base_image.height - base_image.width) / 2);
}else if(base_image.width > base_image.height) {
selectedWidth = base_image.height;
selectedHeight = base_image.height;
xOffset = Math.floor((base_image.width - base_image.height) / 2)
}
ctx.drawImage(base_image, xOffset, yOffset, selectedWidth, selectedHeight, 0, 0, IMG_SIZE, IMG_SIZE);
length = IMG_SIZE;
// make grayscale by averaging the RGB channels.
// extract out the R channel because that's all we need and push graysacle image onto canvas
var imgPixels = ctx.getImageData(0, 0, IMG_SIZE, IMG_SIZE);
R = img_result = nj.ones([IMG_SIZE, IMG_SIZE ]).multiply(0xff);
var rdata = [];
for(var y = 0; y < imgPixels.height; y++){
for(var x = 0; x < imgPixels.width; x++){
var i = (y * 4) * imgPixels.width + x * 4;
var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
imgPixels.data[i] = avg;
imgPixels.data[i + 1] = avg;
imgPixels.data[i + 2] = avg;
rdata.push(avg);
}
}
R.selection.data = rdata;
2019-03-05 02:24:14 +00:00
ctx.putImageData(imgPixels, 0, 0, 0, 0, IMG_SIZE, IMG_SIZE);
2019-03-01 02:35:52 +00:00
//circle crop canvas
ctx.globalCompositeOperation='destination-in';
ctx.beginPath();
ctx.arc(IMG_SIZE/2,IMG_SIZE/2, IMG_SIZE/2, 0, Math.PI*2);
2019-03-01 02:35:52 +00:00
ctx.closePath();
ctx.fill();
// start processing
NonBlockingCalculatePins();
}
2019-03-01 02:35:52 +00:00
function NonBlockingCalculatePins(){
// set up necessary variables
console.log("Calculating pins...");
status.textContent = "Calculating pins...";
pin_coords = [];
center = length / 2;
radius = length / 2 - 1/2
let i = 0;
(function codeBlock(){
if(i < N_PINS){
angle = 2 * Math.PI * i / N_PINS;
pin_coords.push([Math.floor(center + radius * Math.cos(angle)),
Math.floor(center + radius * Math.sin(angle))]);
i++;
setTimeout(codeBlock, 0);
} else {
console.log('Done Calculating pins');
status.textContent = "Done Calculating pins";
showStep(2);
2019-03-01 02:35:52 +00:00
NonBlockingPrecalculateLines();
}
})();
}
function NonBlockingPrecalculateLines(){
// set up necessary variables
console.log("Precalculating all lines...");
status.textContent = "Precalculating all lines...";
line_cache_y = Array.apply(null, {length: (N_PINS * N_PINS)});
line_cache_x = Array.apply(null, {length: (N_PINS * N_PINS)});
line_cache_length = Array.apply(null, {length: (N_PINS * N_PINS)}).map(Function.call, function(){return 0;});
line_cache_weight = Array.apply(null, {length: (N_PINS * N_PINS)}).map(Function.call, function(){return 1;});
let a = 0;
(function codeBlock(){
if(a < N_PINS){
for (b = a + MIN_DISTANCE; b < N_PINS; b++) {
x0 = pin_coords[a][0];
y0 = pin_coords[a][1];
x1 = pin_coords[b][0];
y1 = pin_coords[b][1];
d = Math.floor(Number(Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)*(y1 - y0))));
xs = linspace(x0, x1, d);
ys = linspace(y0, y1, d);
line_cache_y[b*N_PINS + a] = ys;
line_cache_y[a*N_PINS + b] = ys;
line_cache_x[b*N_PINS + a] = xs;
line_cache_x[a*N_PINS + b] = xs;
line_cache_length[b*N_PINS + a] = d;
line_cache_length[a*N_PINS + b] = d;
}
a++;
setTimeout(codeBlock, 0);
} else {
console.log('Done Precalculating Lines');
status.textContent = "Done Precalculating Lines";
NonBlockingLineCalculator();
showStep(3);
2019-03-01 02:35:52 +00:00
}
})();
}
function NonBlockingLineCalculator(){
// set up necessary variables
console.log("Drawing Lines...");
status.textContent = "Drawing Lines...";
error = nj.ones([IMG_SIZE, IMG_SIZE]).multiply(0xff).subtract(nj.uint8(R.selection.data).reshape(IMG_SIZE, IMG_SIZE));
img_result = nj.ones([IMG_SIZE, IMG_SIZE ]).multiply(0xff);
result = nj.ones([IMG_SIZE * SCALE, IMG_SIZE * SCALE]).multiply(0xff);
result = new cv.matFromArray(IMG_SIZE * SCALE, IMG_SIZE * SCALE, cv.CV_8UC1, result.selection.data);
line_mask = nj.zeros([IMG_SIZE, IMG_SIZE], 'float64');
2019-03-01 02:35:52 +00:00
line_sequence = [];
pin = 0;
line_sequence.push(pin);
thread_length = 0;
last_pins = [];
let l = 0;
(function codeBlock(){
if(l < MAX_LINES){
if(l%10 == 0){
draw();
}
max_err = -1;
best_pin = -1;
for(offset=MIN_DISTANCE; offset < N_PINS - MIN_DISTANCE; offset++){
test_pin = (pin + offset) % N_PINS;
if(last_pins.includes(test_pin)){
continue;
}else {
xs = line_cache_x[test_pin * N_PINS + pin];
ys = line_cache_y[test_pin * N_PINS + pin];
line_err = getLineErr(error, ys, xs) * line_cache_weight[test_pin * N_PINS + pin];
if( line_err > max_err){
max_err = line_err;
best_pin = test_pin;
}
}
}
line_sequence.push(best_pin);
xs = line_cache_x[best_pin * N_PINS + pin];
ys = line_cache_y[best_pin * N_PINS + pin];
weight = LINE_WEIGHT * line_cache_weight[best_pin * N_PINS + pin];
line_mask = nj.zeros([IMG_SIZE, IMG_SIZE], 'float64');
2019-03-01 02:35:52 +00:00
line_mask = setLine(line_mask, ys, xs, weight);
error = subtractArrays(error, line_mask);
p = new cv.Point(pin_coords[pin][0] * SCALE, pin_coords[pin][1] * SCALE);
p2 = new cv.Point(pin_coords[best_pin][0] * SCALE, pin_coords[best_pin][1] * SCALE);
cv.line(result, p, p2, new cv.Scalar(0, 0, 0), 2, cv.LINE_AA, 0);
2019-03-01 02:35:52 +00:00
x0 = pin_coords[pin][0];
y0 = pin_coords[pin][1];
x1 = pin_coords[best_pin][0];
y1 = pin_coords[best_pin][1];
dist = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
thread_length += HOOP_DIAMETER / length * dist;
last_pins.push(best_pin);
if(last_pins.length > 20){
last_pins.shift();
}
pin = best_pin;
//update status
drawStatus.textContent = l + " Lines drawn | " + Math.round((l / MAX_LINES) * 100) + "% complete";
2019-03-01 02:35:52 +00:00
l++;
setTimeout(codeBlock, 0);
} else {
console.log('Done Drawing Lines');
Finalize();
}
})();
}
function draw(){
let dsize = new cv.Size(IMG_SIZE * 2, IMG_SIZE * 2);
2019-03-01 02:35:52 +00:00
let dst = new cv.Mat();
cv.resize(result, dst, dsize, 0, 0, cv.INTER_AREA);
cv.imshow('canvasOutput2', dst);
dst.delete();
}
function Finalize() {
let dsize = new cv.Size(IMG_SIZE * 2, IMG_SIZE * 2);
2019-03-01 02:35:52 +00:00
let dst = new cv.Mat();
cv.resize(result, dst, dsize, 0, 0, cv.INTER_AREA);
console.log("complete");
drawStatus.textContent = MAX_LINES + " Lines drawn | 100% complete";
2019-03-01 02:35:52 +00:00
cv.imshow('canvasOutput2', dst);
console.log(line_sequence);
status.textContent = "Complete";
pinsOutput.value = line_sequence;
showPins.classList.remove('hidden');
dst.delete(); result.delete();
2019-03-05 02:24:14 +00:00
window.scrollTo({ top: 5000, left: 0, behavior: 'smooth' });
2019-03-01 02:35:52 +00:00
}
function getLineErr(arr, coords1, coords2){
let result = new Uint8Array(coords1.length);
for(i=0;i<coords1.length;i++){
result[i] = arr.get(coords1[i], coords2[i]);
}
return getSum(result);
}
function setLine(arr, coords1, coords2, line){
for(i=0;i<coords1.length;i++){
arr.set(coords1[i], coords2[i], line);
}
return arr;
}
function compareMul(arr1, arr2){
let result = new Uint8Array(arr1.length);
for(i=0;i<arr1.length;i++){
result[i] = (arr1[i] < arr2[i]) * 254 + 1 ;
}
return result;
}
function compareAbsdiff(arr1, arr2){
let rsult = new Uint8Array(arr1.length);
for(i=0;i<arr1.length;i++){
rsult[i] = (arr1[i] * arr2[i]);
}
return rsult;
}
function subtractArrays(arr1, arr2) {
for(i=0; i<arr1.selection.data.length;i++){
arr1.selection.data[i] = arr1.selection.data[i] - arr2.selection.data[i]
if(arr1.selection.data[i] < 0){
arr1.selection.data[i] = 0;
}else if (arr1.selection.data[i] > 255){
arr1.selection.data[i] = 255;
}
}
return arr1;
}
function subtractArraysSimple(arr1, arr2) {
for(i=0; i<arr1.length;i++){
arr1[i] = arr1[i] - arr2[i];
}
return arr1;
}
function getSum(arr) {
let v = 0;
for(i=0;i<arr.length;i++){
v = v + arr[i];
}
return v;
}
function makeArr(startValue, stopValue, cardinality) {
var arr = [];
var currValue = startValue;
var step = (stopValue - startValue) / (cardinality - 1);
for (var i = 0; i < cardinality; i++) {
arr.push(Math.round(currValue + (step * i)));
}
return arr;
}
function AddRGB(arr1, arr2, arr3){
for(i=0;i<arr1.data.length;i++){
var avg = (arr1.data[i] + arr2.data[i] + arr3.data[i]);
arr1.data[i] = avg;
}
return arr1;
}
function linspace(a,b,n) {
if(typeof n === "undefined") n = Math.max(Math.round(b-a)+1,1);
if(n<2) { return n===1?[a]:[]; }
var i,ret = Array(n);
n--;
for(i=n;i>=0;i--) { ret[i] = Math.floor((i*b+(n-i)*a)/n); }
return ret;
}
function showStep(id){
let step1 = document.getElementById("step1");
let step2 = document.getElementById("step2");
let step3 = document.getElementById("step3");
switch (id){
case 1:
step1.classList.remove('hidden');
step2.classList.add('hidden');
step3.classList.add('hidden');
break;
case 2:
step1.classList.add('hidden');
step2.classList.remove('hidden');
step3.classList.add('hidden');
break;
case 3:
step1.classList.add('hidden');
step2.classList.add('hidden');
step3.classList.remove('hidden');
break;
default:
break;
}
}
//********************************
// Creation Assistant
//********************************
var pointIndex = 0;
var lastStepImage;
function startCreating(){
window.speechSynthesis.getVoices();
incrementalDrawing.classList.remove('hidden');
base_image2 = new Image();
ctx3.canvas.width = IMG_SIZE * 2;
ctx3.canvas.height = IMG_SIZE * 2;
ctx3.clearRect(0,0, IMG_SIZE * 2, IMG_SIZE * 2);
ctx3.drawImage(base_image2, 0, 0, IMG_SIZE * 2, IMG_SIZE * 2);
line_sequence = pinsOutput.value.split(",").map(V => { return parseInt(V)});
2019-03-05 02:24:14 +00:00
window.scrollTo({ top: 5000, left: 0, behavior: 'smooth' });
incrementalCurrentStep.textContent = "";
pointIndex = 0;
if(pin_coords == null){
CalculatePins();
}
nextStep();
listenForKeys = true;
}
function startDrawing(){
incrementalDrawing.classList.remove('hidden');
listenForKeys = false;
base_image2 = new Image();
ctx3.canvas.width = IMG_SIZE * 2;
ctx3.canvas.height = IMG_SIZE * 2;
ctx3.clearRect(0,0, IMG_SIZE * 2, IMG_SIZE * 2);
ctx3.drawImage(base_image2, 0, 0, IMG_SIZE * 2, IMG_SIZE * 2);
line_sequence = pinsOutput.value.split(",").map(V => { return parseInt(V)});
2019-03-05 02:24:14 +00:00
window.scrollTo({ top: 5000, left: 0, behavior: 'smooth' });
incrementalCurrentStep.textContent = "";
pointIndex = 0;
if(pin_coords == null){
CalculatePins();
}
let j = 0;
(function codeBlock(){
if(j < MAX_LINES - 1){
//incrementalCurrentStep.textContent = "Current Line: " + (pointIndex + 1) + " | Pin " + line_sequence[pointIndex] + " to " + line_sequence[pointIndex + 1];
pointIndex++;
ctx3.beginPath();
ctx3.moveTo(pin_coords[line_sequence[pointIndex - 1]][0] * 2, pin_coords[line_sequence[pointIndex - 1]][1] * 2);
ctx3.lineTo(pin_coords[line_sequence[pointIndex]][0] * 2, pin_coords[line_sequence[pointIndex]][1] * 2);
ctx3.strokeStyle = "black";
ctx3.lineWidth = 0.3;
ctx3.stroke();
j++;
setTimeout(codeBlock, 0);
} else {
}
})();
}
function nextStep(){
if(pointIndex > MAX_LINES - 1){ return;}
incrementalCurrentStep.textContent = "Current Line: " + (pointIndex + 1) + " | Pin " + line_sequence[pointIndex] + " to " + line_sequence[pointIndex + 1];
if(pointIndex > 0){
//ctx3.clearRect(0,0, IMG_SIZE * 2, IMG_SIZE * 2);
ctx3.putImageData(lastStepImage, 0, 0);
ctx3.beginPath();
ctx3.moveTo(pin_coords[line_sequence[pointIndex - 1]][0] * 2, pin_coords[line_sequence[pointIndex - 1]][1] * 2);
ctx3.lineTo(pin_coords[line_sequence[pointIndex]][0] * 2, pin_coords[line_sequence[pointIndex]][1] * 2);
ctx3.strokeStyle = "black";
ctx3.lineWidth = 0.3;
ctx3.stroke();
}
lastStepImage = ctx3.getImageData(0, 0, IMG_SIZE * 2, IMG_SIZE * 2);
pointIndex++;
ctx3.beginPath();
ctx3.moveTo(pin_coords[line_sequence[pointIndex - 1]][0] * 2, pin_coords[line_sequence[pointIndex - 1]][1] * 2);
ctx3.lineTo(pin_coords[line_sequence[pointIndex]][0] * 2, pin_coords[line_sequence[pointIndex]][1] * 2);
ctx3.strokeStyle = "#FF0000";
ctx3.lineWidth = 1;
ctx3.stroke();
//window.speechSynthesis.speak(new SpeechSynthesisUtterance(line_sequence[pointIndex + 1]));
}
function lastStep(){
if(pointIndex < 2){ return;}
pointIndex--;
pointIndex--;
ctx3.clearRect(0,0, IMG_SIZE * 2, IMG_SIZE * 2);
incrementalCurrentStep.textContent = "Current Line: " + (pointIndex + 1) + " | Pin " + line_sequence[pointIndex] + " to " + line_sequence[pointIndex + 1];
for(i=0; i < pointIndex; i++){
ctx3.beginPath();
ctx3.moveTo(pin_coords[line_sequence[i]][0] * 2, pin_coords[line_sequence[i]][1] * 2);
ctx3.lineTo(pin_coords[line_sequence[i + 1]][0] * 2, pin_coords[line_sequence[i + 1]][1] * 2);
ctx3.strokeStyle = "black";
ctx3.lineWidth = 0.3;
ctx3.stroke();
}
lastStepImage = ctx3.getImageData(0, 0, IMG_SIZE * 2, IMG_SIZE * 2);
pointIndex++;
ctx3.beginPath();
ctx3.moveTo(pin_coords[line_sequence[pointIndex - 1]][0] * 2, pin_coords[line_sequence[pointIndex - 1]][1] * 2);
ctx3.lineTo(pin_coords[line_sequence[pointIndex]][0] * 2, pin_coords[line_sequence[pointIndex]][1] * 2);
ctx3.strokeStyle = "#FF0000";
ctx3.lineWidth = 1;
ctx3.stroke();
}
function CalculatePins(){
console.log("Calculating pins...");
pin_coords = [];
center = IMG_SIZE / 2;
radius = IMG_SIZE / 2 - 1/2
for(i=0; i < N_PINS; i++){
angle = 2 * Math.PI * i / N_PINS;
pin_coords.push([Math.floor(center + radius * Math.cos(angle)),
Math.floor(center + radius * Math.sin(angle))]);
}
}
function onHasSteps(){
step1.classList.add('hidden');
step2.classList.add('hidden');
step3.classList.add('hidden');
showPins.classList.remove('hidden');
2019-03-05 02:24:14 +00:00
window.scrollTo({ top: 5000, left: 0, behavior: 'smooth' });
}
document.body.onkeydown = function(e){
if(!listenForKeys){ return; }
if(e.keyCode == 32){ // space bar
nextStep();
}else if(e.keyCode == 39){ //right key
nextStep();
}else if(e.keyCode == 37){ //left key
lastStep();
}
}
function example1(){
pinsOutput.value = "0,127,22,147,276,144,21,126,20,142,9,139,270,140,271,141,8,138,274,137,7,136,273,143,275,146,23,151,25,152,281,155,26,157,27,160,28,162,30,165,32,168,31,164,29,161,33,169,34,172,63,175,69,179,71,174,35,167,61,177,68,170,32,163,30,159,28,154,280,153,26,150,281,149,277,146,12,114,13,116,14,118,17,122,18,123,20,125,22,145,11,140,276,148,278,141,7,139,277,105,275,104,274,142,279,149,24,131,3,130,16,120,10,143,272,135,8,136,5,133,25,156,282,157,284,159,31,171,59,161,27,152,285,151,280,148,10,121,19,135,6,137,276,142,11,113,13,124,287,170,31,166,67,169,61,179,65,162,33,175,70,181,72,178,66,163,29,145,283,150,278,152,23,128,1,129,16,119,9,135,26,146,282,155,286,168,57,163,60,166,55,159,287,130,24,147,30,171,34,177,64,180,63,164,33,173,67,162,58,167,285,123,12,112,11,149,283,154,32,172,1,126,14,127,2,132,23,144,272,142,13,146,10,138,269,139,286,140,6,115,15,119,18,124,20,122,12,137,273,135,17,133,4,131,5,156,281,151,277,106,278,143,282,149,274,121,284,166,65,160,71,169,35,173,59,174,70,183,72,170,34,176,66,164,28,144,11,111,13,125,19,134,260,133,266,132,18,118,8,137,285,169,73,175,4,174,32,167,64,154,281,125,286,156,63,182,71,171,60,170,0,163,67,178,58,157,283,158,32,155,64,187,70,159,53,164,56,168,74,180,72,166,34,161,30,167,15,131,26,132,8,108,279,140,9,109,1,133,268,138,285,153,10,134,18,120,11,123,21,128,286,171,287,169,55,170,29,152,282,144,3,151,275,102,271,99,270,98,189,68,184,45,183,66,161,73,165,284,148,22,126,15,118,16,115,5,138,273,144,9,131,267,140,14,128,2,149,62,168,284,160,59,176,72,159,73,158,286,127,24,135,261,134,0,123,19,139,275,130,6,114,188,112,2,162,64,184,92,178,35,179,62,205,67,167,63,199,60,202,61,185,50,160,7,129,17,117,8,147,278,105,276,103,181,65,156,284,152,279,144,5,176,69,182,91,177,94,208,60,186,21,127,256,128,285,150,13,112,10,125,287,141,273,140,17,144,32,175,31,121,16,166,68,172,286,131,20,184,71,156,14,136,6,177,72,161,1,125,23,129,263,135,7,108,10,107,185,63,163,287,142,271,100,184,109,186,58,200,59,146,280,150,61,168,36,172,74,160,64,176,17,119,28,118,270,97,177,5,137,269,132,264,116,187,22,111,2,168,285,161,9,110,280,155,73,171,0,140,8,129,25,134,259,136,18,112,184,22,130,257,132,4,145,276,152,33,174,37,138,12,115,188,54,161,62,198,74,158,56,187,95,269,119,10,128,265,133,3,153,278,107,8,142,33,124,15,109,4,125,11,139,268,134,274,146,5,175,90,165,283,128,16,117,29,115,25,191,53,166,73,190,54,171,66,185,111,188,96,181,36,173,287,157,64,207,65,202,93,203,61,147,271,131,16,145,0,162,68,149,14,132,260,135,287,172,70,160,1,150,277,141,9,133,13,180,104,5,116,17,121,20,107,210,33,131,276,154,74,202,62,173,91,179,12,124,22,110,213,69,191,104,278,139,262,127,20,185,119,30,114,19,176,99,190,24,113,183,21,117,273,148,63,205,31,206,59,178,1,141,15,169,284,120,187,112,26,192,53,186,71,153,282,129,3,166,286,167,62,191,59,182,49,159,63,177,69,154,4,126,285,171,95,212,71,148,279,163,12,113,186,65,158,8,134,264,142,7,127,15,120,24,126,34,174,72,168,75,159,71,150,25,110,27,194,55,185,92,172,2,164,0,112,22,252,23,121,184,57,165,36,170,100,189,25,144,280,125,21,131,11,155,267,157,66,180,58,164,9,121,214,119,188,51,168,101,206,67,175,2,103,6,152,74,156,268,132,15,160,287,122,269,96,270,136,274,129,217,72,149,32,205,139,4,165,56,197,62,186,118,283,166,64,152,69,218,73,157,16,136,206,138,263,38,262,113,26,111,180,116,29,171,97,187,24,253,125,32,207,94,209,60,145,208,31,123,268,141,70,195,48,188,63,181,2,121,185,40,137,283,122,183,101,178,68,165,72,147,1,107,276,153,265,92,169,286,173,73,163,8,126,19,105,7,117,26,255,129,258,136,4,106,186,42,181,115,189,23,111,212,64,179,67,211,61,159,283,133,20,251,21,116,177,18,162,282,167,95,189,71,220,131,212,34,182,14,122,213,125,207,68,143,6,102,4,170,74,153,286,107,24,108,184,46,191,56,203,83,205,94,172,57,158,0,129,35,260,36,259,133,270,130,266,157,71,175,109,187,59,162,74,151,70,140,205,137,13,110,3,185,115,269,97,166,29,256,33,209,63,200,79,198,52,187,126,272,146,208,103,279,122,25,192,106,5,113,178,70,210,32,256,109,16,249,15,114,177,56,160,90,176,117,27,205,59,180,48,183,115,265,141,206,58,156,73,174,0,110,181,18,102,189,125,14,167,232,168,34,21
}
function example2(){
pinsOutput.value = "0,138,19,152,40,155,42,154,41,159,45,157,54,170,55,172,56,173,65,168,47,156,44,153,38,151,35,150,15,147,13,146,12,132,11,144,32,145,27,143,25,142,31,148,39,149,16,135,17,151,36,150,14,146,33,147,12,133,10,130,9,131,11,145,38,154,40,156,271,113,272,127,267,124,273,158,54,169,55,164,69,175,70,176,67,174,60,171,56,178,76,250,77,251,78,252,79,257,138,277,140,26,159,53,162,55,179,66,167,42,158,274,129,11,146,31,144,38,143,24,157,272,125,265,121,262,118,266,126,278,128,268,153,43,169,67,244,69,174,55,173,75,255,82,259,137,256,77,178,54,171,48,161,276,115,270,114,244,135,15,148,12,145,13,133,273,126,8,123,287,136,254,81,253,80,258,139,1,137,260,121,269,154,37,149,2,151,3,152,41,160,56,167,61,181,76,172,75,256,83,188,86,189,82,257,135,13,131,271,157,55,177,63,176,73,247,74,237,104,203,101,194,98,193,93,191,56,180,65,240,109,242,68,243,112,269,116,245,78,241,74,254,73,165,284,166,44,160,5,159,277,141,23,137,18,153,40,147,11,136,255,76,249,74,173,67,167,70,163,28,138,30,165,55,158,25,134,287,122,250,135,242,111,207,108,241,79,239,72,246,71,252,137,20,152,265,124,276,139,29,162,279,131,24,140,275,161,273,116,261,120,283,164,18,163,55,168,46,151,43,170,70,169,75,238,106,204,97,193,90,191,88,192,57,158,4,155,268,117,267,153,21,140,27,133,275,115,245,135,271,160,47,173,64,238,74,248,120,268,123,9,129,12,144,6,162,274,159,54,176,52,175,62,183,79,264,151,34,145,36,149,14,161,60,231,72,253,134,245,80,254,78,265,148,13,128,277,164,70,247,136,275,155,54,172,48,259,138,261,50,262,77,174,66,170,74,236,135,285,204,100,193,56,156,22,166,280,118,281,164,72,240,110,208,2,206,109,263,123,10,131,239,135,258,81,187,87,192,99,202,96,201,95,190,94,194,67,166,55,171,46,150,39,142,26,216,29,214,30,135,286,122,259,80,240,130,280,133,9,170,57,228,59,229,58,194,107,243,76,257,85,190,91,186,64,175,71,209,104,196,56,162,16,152,4,126,255,74,250,78,242,66,200,92,198,90,262,158,23,213,25,132,272,156,269,151,19,134,243,69,169,8,172,74,168,283,203,281,163,5,207,1,208,103,238,135,241,109,205,68,195,97,194,102,237,76,260,49,258,86,268,115,273,141,26,157,5,154,55,180,67,161,62,236,100,235,75,170,47,251,41,147,28,215,30,133,29,136,253,128,11,143,252,70,178,10,146,43,149,32,150,263,79,246,117,249,82,266,121,20,155,24,215,27,139,270,159,4,206,287,208,105,193,99,205,0,209,113,198,104,210,72,245,81,267,155,37,140,22,136,244,82,253,130,11,127,278,163,71,251,139,260,47,250,141,276,135,235,103,192,106,195,56,177,53,150,2,205,105,239,71,248,75,166,20,117,277,132,17,163,29,219,31,222,28,164,284,120,21,165,279,202,63,184,88,178,65,241,133,225,115,256,47,149,266,161,6,143,33,148,16,134,238,73,211,3,207,112,270,154,21,127,254,44,152,256,84,191,89,196,110,204,67,171,8,124,22,120,274,114,271,133,252,41,158,263,50,265,128,237,135,233,59,166,285,208,4,153,42,169,284,209,2,136,266,146,35,141,259,78,264,49,257,112,198,84,272,134,14,160,60,197,93,180,10,179,79,236,107,6,207,286,210,108,213,0,123,31,215,128,19,130,25,217,33,127,212,2,148,48,158,69,250,41,162,72,234,66,163,67,245,108,6,103,240,134,228,119,282,91,201,277,129,270,157,260,85,273,154,3,125,21,122,9,177,69,196,96,189,80,247,46,257,133,15,102,234,79,243,132,281,167,7,162,18,131,222,120,30,218,116,28,223,130,214,25,144,15,98,13,149,261,48,174,70,165,282,204,287,215,111,19,116,200,274,142,43,154,258,151,262,92,189,108,239,67,201,272,199,66,243,83,175,54,167,285,136,12,95,198,268,158,60,232,124,209,3,88,261,125,253,79,251,46,259,49,255,73,171,59,179,54,191,57,226,134,248,145,45,262,124,23,166,62,178,90,199,89,260,48,250,36,137,32,221,29,126,14,148,45,258,155,66,177,76,254,151,39,144,255,138,286,205,278,103,196,105,5,162,70,208,6,100,189,87,201,71,203,276,129,259,133,231,57,194,108,191,91,199,266,86,183,64,204,280,209,283,211,5,101,239,110,20,127,8,168,282,92,11,179,69,157,21,184,100,16,132,220,27,114,197,83,261,46,245,35,218,4,207,125,249,47,221,5,205,71,232,135,230,58,192,107,237,63,164,73,233,132,9,120,270,152,54,173,286,134,242,81,244,77,263,159,15,114,19,211,1,214,5,153,266,50,264,147,34,216,126,235,101,201,88,177,87,194,55,190,111,6,206,65,185,81,268,197,102,25,160,68,236,129,41,249,121,267,47,220,3
}
function example3(){
pinsOutput.value = "0,150,284,147,281,146,279,145,278,144,276,143,280,148,286,152,2,151,285,149,287,153,3,154,4,156,6,155,1,150,283,145,277,141,278,142,274,90,244,68,242,57,238,56,237,58,240,59,241,72,243,66,239,67,245,86,246,81,236,55,234,54,215,71,244,79,242,74,240,57,237,80,243,65,241,58,239,56,235,75,245,70,247,87,246,78,254,71,217,72,240,88,244,83,242,77,261,80,262,76,243,85,241,57,236,56,234,81,238,58,235,73,256,78,260,79,253,70,246,66,248,84,245,91,244,76,223,55,237,82,242,81,264,77,254,69,213,54,211,68,214,72,216,73,245,90,246,65,244,80,239,57,235,82,238,55,220,71,241,56,231,58,236,86,237,83,263,78,243,67,250,70,218,72,246,69,242,85,238,79,258,74,244,75,222,55,240,56,233,82,245,78,225,77,253,68,247,65,249,71,213,67,251,69,211,53,206,54,218,74,246,89,239,78,262,81,243,90,242,87,236,88,248,80,257,72,219,56,230,55,217,68,246,71,240,77,263,76,241,73,214,54,209,67,247,173,86,239,84,243,70,249,175,252,66,244,62,206,65,251,176,89,238,78,223,74,259,79,228,80,236,75,243,68,252,178,254,67,212,55,221,77,265,82,247,78,261,74,248,172,85,171,83,246,173,87,238,80,256,177,250,68,215,70,241,55,224,79,243,57,234,165,232,82,266,77,257,84,173,251,179,253,78,265,86,174,88,237,81,255,176,256,70,211,56,209,89,243,62,241,83,236,167,80,240,79,252,76,259,71,248,65,238,86,234,72,213,90,214,53,204,55,231,164,233,87,172,81,235,89,247,75,260,73,255,78,227,71,238,169,51,171,84,175,88,268,87,206,63,245,79,226,55,239,168,235,85,244,92,247,174,250,64,213,91,243,77,258,69,216,67,218,57,221,76,264,79,166,231,163,229,56,242,59,238,84,170,245,77,248,177,88,208,89,174,83,243,87,176,253,181,255,76,266,79,241,66,249,81,224,56,220,77,251,68,212,91,238,167,240,89,211,71,214,57,208,94,213,73,262,83,172,80,263,81,245,69,210,88,205,54,225,160,74,216,90,239,170,85,199,53,236,84,227,161,77,268,136,271,137,270,138,273,140,275,144,280,146,277,142,279,148,283,151,1,152,284,145,281,149,0,147,285,150,287,154,219,20,220,54,232,168,237,171,245,66,253,180,256,178,259,187,260,179,250,65,254,182,252,173,51,170,80,166,233,55,216,70,240,66,247,88,172,52,194,84,177,89,175,246,76,267,86,204,83,239,77,259,70,244,82,169,85,234,170,236,78,165,230,81,200,58,203,62,248,68,219,22,220,26,221,66,250,80,190,87,178,51,174,254,185,258,179,255,77,244,69,247,81,196,83,176,88,235,84,192,79,171,242,55,219,70,236,54,237,166,239,82,184,261,76,269,86,210,90,215,57,217,18,218,75,265,195,264,87,177,253,65,252,179,83,211,85,231,168,51,176,90,207,89,173,88,241,78,258,76,260,186,257,177,86,201,57,244,81,240,68,220,53,209,94,183,56,222,29,221,157,223,36,224,161,74,262,75,159,225,55,235,79,251,64,211,88,192,120,191,81,268,198,82,236,52,169,86,179,248,78,219,67,244,89,178,261,72,212,85,202,53,238,83,168,230,162,226,80,270,139,275,145,280,147,282,143,273,144,283,146,286,151,284,149,279,85,184,79,218,17,217,70,214,84,195,263,73,244,78,257,67,249,180,82,197,269,75,240,172,89,189,108,188,258,176,254,79,164,235,90,204,78,193,52,167,233,58,218,69,239,173,85,189,81,223,37,224,75,249,68,256,65,208,84,182,93,180,250,184,101,185,78,229,55,232,87,174,244,86,248,171,88,209,81,241,54,173,79,271,138,272,76,256,186,103,222,25,221,156,71,235,86,206,94,216,75,259,68,245,80,163,234,171,249,79,255,70,212,81,202,90,196,124,195,122,194,266,73,217,19,220,27,221,53,233,79,247,172,86,178,56,228,164,76,265,183,82,206,90,217,54,207,85,236,67,252,78,214,63,249,73,158,220,58,215,151,282,145,287,148,2,103,184,260,74,241,90,177,91,234,166,240,82,186,255,65,245,76,268,133,55,199,271,77,237,84,228,162,78,206,93,204,88,191,121,194,82,243,69,223,32,221,24,222,155,218,53,208,62,246,174,85,193,123,196,262,77,230,167,84,203,94,181,55,197,90,236,165,83,209,71,158,222,79,213,66,251,63,217,21,220,104,185,100,282,148,278,146,276,140,51,179,261,182,253,76,257,65,205,80,208,87,200,86,180,254,81,211,58,242,54,231,161,236,79,239,76,248,83,170,88,178,94,214,64,216,13,201,85,227,43,228,166,78,267,198,81,195,262,70,242,65,210,84,244,177,56,216,76,273,137,269,79,227,164,77,170,87,188,106,187,89,191,112,190,83,235,169,245,57,213,149,5,153,4,88,207,80,220,63,248,180,94,196,268,85,209,79,256,179,262,71,239,54,238,76,270,78,215,74,219,57,224,38,1
}
2019-03-01 02:35:52 +00:00
function onOpenCvReady() {
// even when this is called, sometimes it's still not ready, adding slight time buffer
setTimeout(function(){
document.getElementById('status').innerHTML = 'Generator is ready.';
2019-03-01 02:35:52 +00:00
}, 1000);
2019-03-04 04:28:51 +00:00
numberOfPins.value = N_PINS;
numberOfPins.addEventListener("keyup", function(event) {
N_PINS = parseInt(event.target.value);
});
numberOfLines.value = MAX_LINES;
numberOfLines.addEventListener("keyup", function(event) {
MAX_LINES = parseInt(event.target.value);
});
lineWeight.value = LINE_WEIGHT;
lineWeight.addEventListener("keyup", function(event) {
LINE_WEIGHT = parseInt(event.target.value);
});
2019-03-01 02:35:52 +00:00
}
</script>
<script async src="opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
2019-03-05 02:24:14 +00:00
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/nicolaspanel/numjs@0.15.1/dist/numjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
2019-03-01 02:35:52 +00:00
</body>
2019-03-05 13:28:36 +00:00
</html>