-
-
Save sergebat/7c3e1e19907452639a14afab0f022a78 to your computer and use it in GitHub Desktop.
SVG Jigsaw Puzzle Generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> | |
<script src="https://d3js.org/d3-path.v0.1.min.js"></script> | |
<script src="https://d3js.org/d3-shape.v0.5.min.js"></script> | |
<style> | |
body { | |
font: 13px sans-serif; | |
} | |
rect, | |
circle, | |
path { | |
fill: none; | |
stroke: steelblue; | |
stroke-width: 1.5px; | |
} | |
circle { | |
fill: #fff; | |
fill-opacity: .2; | |
} | |
</style> | |
<body> | |
<form> | |
<label for="rowCount">Rows:</label> | |
<input type="number" id="rowCount" min="1" max="10" value="10"/> | |
<label for="columnCount">Columns:</label> | |
<input type="number" id="columnCount" min="1" max="10" value="10"/> | |
</form> | |
<div id="container"> | |
</div> | |
</body> | |
<script> | |
var width = 450, height = 450; | |
// Returns 6 points representing the shape of one edge of a puzzle piece. | |
// Point coordinates are expressed as percentage distances across the width | |
// and height of the piece. | |
var edgeDistributions = function() { | |
var randomBetween = function(min, max) { | |
return Math.random() * (max - min) + min; | |
}; | |
var baselineOffsets = { | |
xMin: 51, | |
xMax: 62, | |
yMin: -15, | |
yMax: 5 | |
}; | |
var upperOffsets = { | |
xMin: 20, | |
xMax: 30, | |
yMin: 20, | |
yMax: 44 | |
}; | |
var point1 = [0, 0]; | |
var point2 = [ | |
randomBetween(baselineOffsets.xMin, baselineOffsets.xMax), | |
randomBetween(baselineOffsets.yMin, baselineOffsets.yMax) | |
]; | |
var point3 = [ | |
randomBetween(upperOffsets.xMin, upperOffsets.xMax), | |
randomBetween(upperOffsets.yMin, upperOffsets.yMax) | |
]; | |
var point4 = [ | |
randomBetween(100-upperOffsets.xMax, 100-upperOffsets.xMin), | |
randomBetween(upperOffsets.yMin, upperOffsets.yMax) | |
]; | |
var point5 = [ | |
randomBetween(100-baselineOffsets.xMax, 100-baselineOffsets.xMin), | |
randomBetween(baselineOffsets.yMin, baselineOffsets.yMax) | |
]; | |
var point6 = [100, 0]; | |
var sign = Math.random() < 0.5 ? -1 : 1; | |
return [point1, point2, point3, point4, point5, point6].map(function(p) { | |
return [p[0] / 100, p[1] * sign / 100]; | |
}); | |
}; | |
// Builds an m + 1 x n matrix of edge shapes. The first and last rows | |
// are straight edges. | |
var buildDistributions = function(m, n) { | |
var lineGroups = []; | |
var lines = []; | |
var points, i, j; | |
for (j = 0; j < n; j++) { | |
lines.push([[0, 0], [1,0]]); | |
} | |
lineGroups.push(lines); | |
for (i = 1; i < m; i++) { | |
lines = []; | |
for (j = 0; j < n; j++) { | |
lines.push(edgeDistributions()); | |
} | |
lineGroups.push(lines); | |
} | |
lines = []; | |
for (j = 0; j < n; j++) { | |
lines.push([[0, 0], [1,0]]); | |
} | |
lineGroups.push(lines); | |
return lineGroups; | |
}; | |
var transposePoint = function(point) { | |
return [point[1], point[0]]; | |
}; | |
var offsetPoint = function(point, columnIndex, rowIndex, columnWidth, rowHeight) { | |
var offsetColumnPosition = function(percent, columnWidth, columnIndex) { | |
var columnOffset = columnWidth * columnIndex; | |
return percent * columnWidth + columnOffset; | |
}; | |
var offsetRowPosition = function(percent, rowHeight, rowIndex) { | |
var rowOffset = rowHeight * rowIndex; | |
return percent * rowHeight + rowOffset; | |
}; | |
var x = offsetColumnPosition(point[0], columnWidth, columnIndex); | |
var y = offsetRowPosition(point[1], rowHeight, rowIndex); | |
return [x, y]; | |
}; | |
var offsetPoints = function(lineGroups, offsetter) { | |
for (var i=0; i<lineGroups.length; i++) { | |
var lines = lineGroups[i]; | |
for (var j=0; j<lines.length; j++) { | |
lines[j] = lines[j].map(function(point) { | |
return offsetter(point, j, i); | |
}); | |
} | |
} | |
}; | |
var buildPieces = function(rowCount, columnCount) { | |
var rowHeight = height / rowCount; | |
var columnWidth = width / columnCount; | |
var pieces = []; | |
var rows = buildDistributions(rowCount, columnCount); | |
offsetPoints(rows, function(point, j, i) { | |
return offsetPoint(point, j, i, columnWidth, rowHeight); | |
}); | |
var columns = buildDistributions(columnCount, rowCount); | |
offsetPoints(columns, function(point, j, i) { | |
return offsetPoint(transposePoint(point), i, j, columnWidth, rowHeight); | |
}); | |
for (var rowIndex = 1; rowIndex <= rowCount; rowIndex++) { | |
for (var columnIndex = 0; columnIndex < columnCount; columnIndex++) { | |
var edges = []; | |
edges.push(rows[rowIndex - 1][columnIndex]); | |
edges.push(columns[columnIndex + 1][rowIndex - 1]); | |
edges.push(rows[rowIndex][columnIndex].slice().reverse()); | |
edges.push(columns[columnIndex][rowIndex - 1].slice().reverse()); | |
pieces.push(edges); | |
} | |
} | |
return pieces; | |
}; | |
var d3CurvedLine = d3_shape.line().curve(d3_shape.curveBasis); | |
var piecePathData = function(piece) { | |
return piece.map(function(edge) { | |
return d3CurvedLine(edge); | |
}).join(" "); | |
}; | |
var buildPiecePaths = function(pieces) { | |
return pieces.map(function(piece) { | |
return svg.path(piecePathData(piece)); | |
}); | |
}; | |
// SVG helpers | |
var svg = { | |
openTag: '<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="' + width + '" height="' + height + '">', | |
closeTag: '</svg>', | |
path: function(pathData) { | |
return '<path vector-effect="non-scaling-stroke" d="' + pathData + '"/>'; | |
} | |
}; | |
$(function() { | |
var generate = function() { | |
var rowCount = parseInt($("#rowCount").val(), 10); | |
var columnCount = parseInt($("#columnCount").val(), 10); | |
var pieces = buildPieces(rowCount, columnCount); | |
var piecePaths = buildPiecePaths(pieces).join(""); | |
var svgNode = [ | |
svg.openTag, | |
piecePaths, | |
svg.closeTag | |
].join(""); | |
$("#container").empty().append($(svgNode)); | |
} | |
$('input').change(generate); | |
generate(); | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment