Skip to content

Instantly share code, notes, and snippets.

@Draradech
Last active June 28, 2024 12:04
Show Gist options
  • Save Draradech/35d36347312ca6d0887aa7d55f366e30 to your computer and use it in GitHub Desktop.
Save Draradech/35d36347312ca6d0887aa7d55f366e30 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Jigsaw puzzle</title>
<script type="text/javascript">
function save(filename, data)
{
var blob = new Blob([data], {type: "text/csv"});
if (window.navigator.msSaveOrOpenBlob)
{
window.navigator.msSaveBlob(blob, filename);
}
else
{
var elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
var seed = 1;
function random() { var x = Math.sin(seed) * 10000; seed += 1; return x - Math.floor(x); }
function uniform(min, max) { var r = random(); return min + r * (max - min); }
function rbool() { return random() > 0.5; }
function $(id) { return document.getElementById(id); }
function updateseed() { $("_seed").value = $("seed").value; update(); }
function updatetabsize() { $("_tabsize").value = $("tabsize").value + "%"; update(); }
function updatejitter() { $("_jitter").value = $("jitter").value + "%"; update(); }
function update_seed() { var val = parseFloat($("_seed").value); if (!isNaN(val)) { $("seed").value = val; } updateseed(); }
function update_tabsize() { var val = parseFloat($("_tabsize").value); if (!isNaN(val)) { $("tabsize").value = val; } updatetabsize(); }
function update_jitter() { var val = parseFloat($("_jitter").value); if (!isNaN(val)) { $("jitter").value = val; } updatejitter(); }
var a, b, c, d, e, t, j, flip, xi, yi, xn, yn, vertical, offset, width, height, radius;
function first() { e = uniform(-j, j); next();}
function next() { var flipold = flip; flip = rbool(); a = (flip == flipold ? -e: e); b = uniform(-j, j); c = uniform(-j, j); d = uniform(-j, j); e = uniform(-j, j);}
function sl() { return vertical ? height / yn : width / xn; }
function sw() { return vertical ? width / xn : height / yn;}
function ol() { return offset + sl() * (vertical ? yi : xi); }
function ow() { return offset + sw() * (vertical ? xi : yi); }
function l(v) { var ret = ol() + sl() * v; return Math.round(ret * 100) / 100; }
function w(v) { var ret = ow() + sw() * v * (flip ? -1.0 : 1.0); return Math.round(ret * 100) / 100; }
function p0l() { return l(0.0); }
function p0w() { return w(0.0); }
function p1l() { return l(0.2); }
function p1w() { return w(a); }
function p2l() { return l(0.5 + b + d); }
function p2w() { return w(-t + c); }
function p3l() { return l(0.5 - t + b); }
function p3w() { return w(t + c); }
function p4l() { return l(0.5 - 2.0 * t + b - d); }
function p4w() { return w(3.0 * t + c); }
function p5l() { return l(0.5 + 2.0 * t + b - d); }
function p5w() { return w(3.0 * t + c); }
function p6l() { return l(0.5 + t + b); }
function p6w() { return w(t + c); }
function p7l() { return l(0.5 + b + d); }
function p7w() { return w(-t + c); }
function p8l() { return l(0.8); }
function p8w() { return w(e); }
function p9l() { return l(1.0); }
function p9w() { return w(0.0); }
function parse_input()
{
seed = parseInt($("seed").value);
t = parseFloat($("tabsize").value) / 200.0;
j = parseFloat($("jitter").value) / 100.0;
xn = parseInt($("xn").value);
yn = parseInt($("yn").value);
}
function gen_dh()
{
var str = "";
vertical = 0;
for (yi = 1; yi < yn; ++yi)
{
xi = 0;
first();
str += "M " + p0l() + "," + p0w() + " ";
for (; xi < xn; ++xi)
{
str += "C " + p1l() + " " + p1w() + " " + p2l() + " " + p2w() + " " + p3l() + " " + p3w() + " ";
str += "C " + p4l() + " " + p4w() + " " + p5l() + " " + p5w() + " " + p6l() + " " + p6w() + " ";
str += "C " + p7l() + " " + p7w() + " " + p8l() + " " + p8w() + " " + p9l() + " " + p9w() + " ";
next();
}
}
return str;
}
function gen_dv()
{
var str = "";
vertical = 1;
for (xi = 1; xi < xn; ++xi)
{
yi = 0;
first();
str += "M " + p0w() + "," + p0l() + " ";
for (; yi < yn; ++yi)
{
str += "C " + p1w() + " " + p1l() + " " + p2w() + " " + p2l() + " " + p3w() + " " + p3l() + " ";
str += "C " + p4w() + " " + p4l() + " " + p5w() + " " + p5l() + " " + p6w() + " " + p6l() + " ";
str += "C " + p7w() + " " + p7l() + " " + p8w() + " " + p8l() + " " + p9w() + " " + p9l() + " ";
next();
}
}
return str;
}
function gen_db()
{
var str = "";
str += "M " + (offset + radius) + " " + (offset) + " ";
str += "L " + (offset + width - radius) + " " + (offset) + " ";
str += "A " + (radius) + " " + (radius) + " 0 0 1 " + (offset + width) + " " + (offset + radius) + " ";
str += "L " + (offset + width) + " " + (offset + height - radius) + " ";
str += "A " + (radius) + " " + (radius) + " 0 0 1 " + (offset + width - radius) + " " + (offset + height) + " ";
str += "L " + (offset + radius) + " " + (offset + height) + " ";
str += "A " + (radius) + " " + (radius) + " 0 0 1 " + (offset) + " " + (offset + height - radius) + " ";
str += "L " + (offset) + " " + (offset + radius) + " ";
str += "A " + (radius) + " " + (radius) + " 0 0 1 " + (offset + radius) + " " + (offset) + " ";
return str;
}
function update()
{
width = parseInt($("width").value);
height = parseInt($("height").value);
radius = parseFloat($("radius").value);
var ratio = 1.0 * width / height;
if (ratio > 1.5)
{
radius = radius * 900 / width;
width = 900;
height = width / ratio;
}
else
{
radius = radius * 600 / height;
height = 600;
width = height * ratio;
}
$("puzzlecontainer").setAttribute("width", width + 11);
$("puzzlecontainer").setAttribute("height", height + 11);
offset = 5.5;
parse_input();
$("puzzlepath_h").setAttribute("d", gen_dh());
$("puzzlepath_v").setAttribute("d", gen_dv());
$("puzzlepath_b").setAttribute("d", gen_db());
}
function generate()
{
width = parseInt($("width").value);
height = parseInt($("height").value);
radius = parseFloat($("radius").value);
offset = 0.0;
parse_input();
var data = "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.0\" ";
data += "width=\"" + width + "mm\" height=\"" + height + "mm\" viewBox=\"0 0 " + width + " " + height + "\">";
data += "<path fill=\"none\" stroke=\"DarkBlue\" stroke-width=\"0.1\" d=\"";
data += gen_dh();
data += "\"></path>";
data += "<path fill=\"none\" stroke=\"DarkRed\" stroke-width=\"0.1\" d=\"";
data += gen_dv();
data += "\"></path>";
data += "<path fill=\"none\" stroke=\"Black\" stroke-width=\"0.1\" d=\"";
data += gen_db();
data += "\"></path>";
data += "</svg>";
save("jigsaw.svg", data);
}
</script>
</head>
<body onload="$('seed').value = Math.random() * 10000; updateseed();">
<table>
<tr>
<td>Seed:</td>
<td><input id="_seed" type="text" value="0" onchange="update_seed()"/></td>
<td><input id="seed" type="range" value="0" min="0" max="9999" step="1" onchange="updateseed()"/></td>
</tr>
<tr>
<td>Tab Size:</td>
<td><input id="_tabsize" type="text" value="20%" onchange="update_tabsize()"/></td>
<td><input id="tabsize" type="range" value="20" min="10" max="30" step="0.1" onchange="updatetabsize()"/></td>
</tr>
<tr>
<td>Jitter:</td>
<td><input id="_jitter" type="text" value="4%" onchange="update_jitter()"/></td>
<td><input id="jitter" type="range" value="4" min="0" max="13" step="0.1" onchange="updatejitter()"/></td>
</tr>
<tr>
<td>Corner Radius:</td>
<td><input id="radius" type="text" value="2.0" size="4" onchange="update()"/> mm</td>
<td></td>
</tr>
<tr>
<td>Tiles:</td>
<td><input id="xn" type="text" value="15" size="4" onchange="update()"/> x <input id="yn" type="text" value="10" size="4" onchange="update()"/></td>
<td></td>
</tr>
<tr>
<td>Size:</td>
<td><input id="width" type="text" value="300" size="4" onchange="update()"/> x <input id="height" type="text" value="200" size="4" onchange="update()"/> mm</td>
<td><button onclick="generate()">Download SVG</button></td>
</tr>
</table>
<svg id="puzzlecontainer"><path id="puzzlepath_h" fill="none" stroke="DarkBlue"></path><path id="puzzlepath_v" fill="none" stroke="DarkRed"></path><path id="puzzlepath_b" fill="none" stroke="Black"></path></svg>
</body>
</html>
@synercoder
Copy link

Hi,

If I was to play around with this gist/code, under what license would it fall?

P.S. Love it
P.P.S. Line 40 contains a spelling error: heigth instead of height, this would make the first render different from the second render (just a slight bit).

@Draradech
Copy link
Author

Draradech commented Apr 5, 2020

Hi,

from my point of view, you can do whatever you like with it. For my part that would make CC0 (https://creativecommons.org/publicdomain/zero/1.0/) the best fit. If you want to give credit, a link to this gist is fine, but in no way required.

Note, that the save function is pieced together based on a bunch of answers of this stackoverflow: https://stackoverflow.com/questions/19327749/javascript-blob-filename-without-link, no idea what that means for any licensing...

Regarding the typo: interesting, I would have guessed that should be a syntax error. I don't see how this could cause any problems however. gen_d() is only called from update() and generate(), both are setting height before calling gen_d().

@synercoder
Copy link

Oh yes, my bad, I was playing around with the ratio code commented out, this caused the height not to be initialized that way. That caused the first render to have the the first curve on the first row to curve a bit different. If you click save directly after (without changing settings) the resultant .svg file will have a different first curve.

But thank you for your quick response!

@michael38autumn
Copy link

Hi Manuel,
Thanks very much for sharing this. It's very useful for me as an artist (and IT consultant).
Much appreciated.
Michael

@kagazwala
Copy link

Hi Manuel,
Can your code be used and a jsx to run on Adobe Illustrator?

@Draradech
Copy link
Author

Draradech commented Aug 23, 2020

Can your code be used and a jsx to run on Adobe Illustrator?

I don't have access to Adobe Illustrator, and I don't know their scripting interface. As is, this is a static html webpage - your best bet is to use it via the rawgit link and just save the svg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment