Skip to content

Instantly share code, notes, and snippets.

@vasturiano
Last active July 19, 2018 00:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vasturiano/db5e9e9cfe77d8c468136dc781ba0cc8 to your computer and use it in GitHub Desktop.
Save vasturiano/db5e9e9cfe77d8c468136dc781ba0cc8 to your computer and use it in GitHub Desktop.
Morton (Z-order) Curve

A representation of the Morton (Z-order) curve, using d3-morton layout. Use the slider to modify the curve order (number of iterations). On mouse hover, XY coordinates are reversely converted to curve distance. Zoom/pan the canvas using mouse-wheel/drag events.

See also Hilbert curve.

<head>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.1.3/css/bootstrap-slider.min.css">
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.2.6/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.1.3/bootstrap-slider.min.js"></script>
<script src="//unpkg.com/d3-morton"></script>
<script src="morton-demo.js"></script>
<style>
body {
text-align: center;
}
svg {
margin: 10px;
}
svg path {
fill: none;
stroke: #3A5894;
stroke-width: 0.25;
stroke-linecap: square;
}
svg path.skeleton {
stroke: #EEE;
stroke-width: 0.1;
}
#val-tooltip {
display: none;
position: absolute;
margin-top: 22px;
margin-left: -1px;
padding: 5px;
border-radius: 3px;
font: 11px sans-serif;
color: #eee;
background: rgba(0,0,140,0.9);
text-align: center;
pointer-events: none;
}
</style>
</head>
<body>
<svg id="morton-chart"></svg>
<div id="morton-controls">
<input id="morton-order"/>
</div>
<div id="val-tooltip"></div>
<script>
mortonDemo();
</script>
</body>
function mortonDemo() {
var svg = d3.select('svg#morton-chart'),
canvasWidth = Math.min(window.innerWidth, window.innerHeight - 100),
zOrder,
order = 3;
function d3Digest() {
var zOrderData = {
start: 0,
length: Math.pow(4, order)
};
zOrder.order(order).layout(zOrderData);
svg.selectAll('path')
.datum(zOrderData)
.attr('d', function(d) { return getZOrderPath(d.pathVertices); })
.attr('transform', function(d) {
return 'scale('+ d.cellWidth + ') '
+ 'translate(' + (d.pathVertices[0][0] +.5) + ',' + (d.pathVertices[0][1] +.5) + ')';
});
svg.select('path:not(.skeleton)')
.transition().duration(order * 1000).ease(d3.easePoly)
.attrTween('stroke-dasharray', tweenDash);
function getZOrderPath(vertices) {
var path = 'M0 0L0 0';
vertices.forEach(function(vert) {
path += 'L' + vert.join(',');
});
return path;
}
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) { return i(t); };
}
}
function orderChange(newOrder) {
order = newOrder;
d3Digest();
}
function init() {
zOrder = d3.zOrder()
.order(order)
.canvasWidth(canvasWidth)
.simplifyCurves(false);
svg.attr("width", canvasWidth).attr("height", canvasWidth);
var canvas = svg.append('g');
canvas.append('path').attr('class', 'skeleton');
canvas.append('path');
// Canvas zoom/pan
svg.call(d3.zoom()
.translateExtent([[0, 0], [canvasWidth, canvasWidth]])
.scaleExtent([1, Infinity])
.on("zoom", function() {
canvas.attr("transform", d3.event.transform);
})
);
// Value Tooltip
var valTooltip = d3.select('#val-tooltip');
svg.on('mouseover', function() { valTooltip.style("display", "inline"); })
.on('mouseout', function() { valTooltip.style("display", "none"); })
.on('mousemove', function () {
var coords = d3.mouse(canvas.node());
valTooltip.text(zOrder.getValAtXY(coords[0], coords[1]))
.style('left', d3.event.pageX)
.style('top', d3.event.pageY);
});
// Order slider
$('input#morton-order').slider({
step: 1,
max: 9,
min: 0,
value: order,
tooltip: 'always',
tooltip_position: 'bottom',
formatter: function(d) {
return 'Order: ' + d;
}
}).on('change', function(e) {
orderChange(e.value.newValue);
});
d3Digest();
}
init();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment