Skip to content

Instantly share code, notes, and snippets.

@jonyrock
Last active December 20, 2016 04:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonyrock/8d61f09c30379fd32fd425dfc863fd76 to your computer and use it in GitHub Desktop.
Save jonyrock/8d61f09c30379fd32fd425dfc863fd76 to your computer and use it in GitHub Desktop.
HexCut
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"> </script>
</head>
<body>
<script>
function onLevelChange() {
var val = $('#levelRange').val();
$('#levelLabel').text(val)
$('#triCount').text(val * val)
updateField(+val);
}
</script>
<div style="text-align: center">
A visualisation of triangle cuts inspired by
CodeForces <a href="http://codeforces.com/problemset/problem/559/A" > Gerald's Hexagon Problem </a>
</div>
<div class="svgHolder" id="svgHolder">
<svg>
<defs>
<pattern id="crosshatch" patternUnits="userSpaceOnUse"
width="30" height="30"
>
<image xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc4JyBoZWlnaHQ9JzgnPgogIDxyZWN0IHdpZHRoPSc4JyBoZWlnaHQ9JzgnIGZpbGw9JyNmZmYnLz4KICA8cGF0aCBkPSdNMCAwTDggOFpNOCAwTDAgOFonIHN0cm9rZS13aWR0aD0nMC41JyBzdHJva2U9JyNhYWEnLz4KPC9zdmc+Cg=="
x="0" y="0" width="30" height="30" > </image>
</pattern>
</defs>
</svg>
</div>
<div class="row" style="width: 300px; margin:auto">
<table style="width:100%">
<tr>
<td style="width:100px">Levels: <span id="levelLabel"> 1</span></td>
<td style="width:100px; text-align:center">Count: <span id="triCount">xx</span></td>
<td style="width:80px; text-align:right;"> Cut size: </td>
<td style="width:30px;"><span id="cutCount">--</span></td>
</tr>
</table>
<input type="range" id="levelRange"
value="4" min="1" max="30"
step="1" oninput="onLevelChange()"
style="width:100%"
>
</div>
<script>
var width = 960;
var legendWidth = 120;
var height = 400;
var lineLength = 150;
var fontSize = 30;
var lineStroke = 0.4;
var trianglesSelection = undefined;
var strokeColor = 'black';
var linesCount = -1;
var a = height;
var TR_H = a * Math.sqrt(3) / 2;
var svg = d3.select("#svgHolder svg")
.attr("width", width)
.attr("height", height)
var traiglesGroup = svg.append('g');
var trianglesElems = {}
var trianglePath = d3.path();
trianglePath.moveTo(0, 0);
trianglePath.lineTo(a / 2, TR_H);
trianglePath.lineTo(-a / 2, TR_H);
trianglePath.closePath();
function triangle(selection) {
var g = selection.append("g")
g
.append('path')
.attr("d", trianglePath.toString())
.attr("stroke", strokeColor)
.attr("stroke-alignment", 'center')
.attr("fill", "white")
.attr('transform', function(d) {
if(d.col % 2 == 1) {
return `translate(0, ${TR_H}), rotate(-180)`;
} else {
return ''
}
})
.attr("stroke-width", function(d) {
if(d.col % 2 == 1) {
return 0
}
return linesCount * lineStroke;
})
g
.append('text')
.text((d, i) => (i + 1))
//.text((d, i) => (d.gow))
.attr('font-size', () => fontSize * (1 + Math.log(linesCount)))
.attr('text-anchor', 'middle')
.attr('y', function(d) {
if(d.col % 2 == 1) {
return TR_H / 2.2;
}
return TR_H / 1.2;
})
.attr('fill', '#ddd')
g
.attr('transform', function(d, i) {
var x = -d.row * a / 2 + d.col * a / 2;
var y = d.row * TR_H;
return `translate(${x}, ${y})`;
});
// TODO: move
g
.on("mouseover", showCut)
.on("mouseout", hideCut)
}
function triangleUpdate(selection) {
selection
.select('path')
.attr("stroke-width", function(d) {
if(d.col % 2 == 1) {
return 0
}
return linesCount * lineStroke;
})
selection
.select('text')
.attr('font-size', () => fontSize * (1 + Math.log(linesCount)))
}
function getTrArr(lines) {
var res = [];
var lineCount = 1;
for(var line = 0; line < lines; line++) {
for(var i = 0; i < lineCount; i++) {
res.push({ row: line, col: i })
}
lineCount += 2;
}
lineCount = lines * 2 - 1;
var bottomLeft = res.length - lineCount;
var bottomRight = res.length - 1;
cursor = bottomLeft;
curlineCount = 1;
for(var line = 0; line < lines; line++) {
var inCursor = cursor;
lineCount = lines * 2 - 1;
for(var i = 0; i < curlineCount; i++) {
res[inCursor].gow = line;
if(i % 2 == 0) {
inCursor--;
} else {
inCursor -= lineCount - 1;
lineCount -= 2;
}
}
cursor += 2;
curlineCount += 2;
}
cursor = bottomRight;
curlineCount = 1;
for(var line = 0; line < lines; line++) {
var inCursor = cursor;
lineCount = lines * 2 - 1;
for(var i = 0; i < curlineCount; i++) {
res[inCursor].bow = line;
if(i % 2 == 0) {
inCursor++;
} else {
inCursor -= lineCount - 1;
lineCount -= 2;
}
}
cursor -= 2;
curlineCount += 2;
}
return res;
}
function updateField(lc) {
linesCount = lc;
var l = 4 * linesCount;
console.log(linesCount);
svg.select("#crosshatch")
.attr('height', l)
.attr('width', l);
svg.select("#crosshatch image")
.attr('width', l)
.attr('height', l)
var data = getTrArr(linesCount);
//traiglesGroup.selectAll('g').remove();
var selection = trianglesSelection = traiglesGroup.selectAll('g')
.data(data)
selection.enter().call(triangle)
selection.call(triangleUpdate)
selection.exit().remove();
traiglesGroup.attr(
'transform', `translate(${width / 2}, 20), scale(${1 / linesCount})`
)
}
function showCut(d) {
var filter = undefined;
if(d.row < d.gow && d.row < d.bow) {
filter = t => t.row <= d.row;
}
if(d.bow < d.row && d.bow < d.gow) {
filter = t => t.bow <= d.bow;
}
if(d.gow < d.row && d.gow < d.bow) {
filter = t => t.gow <= d.gow;
}
if(!filter) {
return;
}
var filtered = traiglesGroup
.selectAll('g')
.filter(filter)
$("#cutCount").text(filtered.size());
filtered
.selectAll('path')
.attr("fill", "url(#crosshatch)")
filtered
.selectAll('text')
.attr('fill', '#aaa')
}
function hideCut(d) {
$("#cutCount").text('--');
traiglesGroup
.selectAll('path')
.attr('fill', 'white')
traiglesGroup
.selectAll('text')
.attr('fill', '#ddd')
}
onLevelChange();
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment