d3-morton
Version:
D3 layout to visualize distance variables using a continuous Morton (Z-order) space-filling space.
115 lines (104 loc) • 3.31 kB
JavaScript
function morton () {
var zOrder = function () {
// http://bl.ocks.org/jaredwinick/5073432
// http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
function point2Distance(x, y) {
var B = [0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF];
var S = [1, 2, 4, 8];
x = (x | x << S[3]) & B[3];
x = (x | x << S[2]) & B[2];
x = (x | x << S[1]) & B[1];
x = (x | x << S[0]) & B[0];
y = (y | y << S[3]) & B[3];
y = (y | y << S[2]) & B[2];
y = (y | y << S[1]) & B[1];
y = (y | y << S[0]) & B[0];
return x | y << 1;
}
function distance2Point(d) {
return [deinterleave(d), deinterleave(d >> 1)];
//
// http://stackoverflow.com/questions/4909263/how-to-efficiently-de-interleave-bits-inverse-morton
function deinterleave(x) {
x = x & 0x55555555;
x = (x | x >> 1) & 0x33333333;
x = (x | x >> 2) & 0x0F0F0F0F;
x = (x | x >> 4) & 0x00FF00FF;
x = (x | x >> 8) & 0x0000FFFF;
return x;
}
}
return {
point2Distance: point2Distance,
distance2Point: distance2Point
};
}();
var mortonLayout = {},
canvasWidth = 1,
order = 4,
simplifyCurves = true;
mortonLayout.canvasWidth = function (_) {
if (!arguments.length) return canvasWidth;
canvasWidth = +_;
return mortonLayout;
};
// Note: Maximum safe order is 26, due to JS numbers upper-boundary of 53 bits
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
mortonLayout.order = function (_) {
if (!arguments.length) return order;
order = +_;
return mortonLayout;
};
mortonLayout.simplifyCurves = function (_) {
if (!arguments.length) return simplifyCurves;
simplifyCurves = _;
return mortonLayout;
};
mortonLayout.layout = function (range) {
var d = getZOrderPath(range.start, range.length, order, canvasWidth, simplifyCurves);
range.cellWidth = d.cellWidth;
range.pathVertices = d.pathVertices;
return mortonLayout;
};
mortonLayout.getValAtXY = function (x, y) {
var n = Math.pow(2, order),
xy = [x, y].map(function (coord) {
return Math.floor(coord * n / canvasWidth);
});
return zOrder.point2Distance(xy[0], xy[1]);
};
return mortonLayout;
//
function getZOrderPath(start, length, order, sideSize, simplifyCurves) {
if (simplifyCurves) {
// Adjust resolution
while (!Number.isInteger(start) || !Number.isInteger(length)) {
start *= 4;
length *= 4;
order += 1;
}
// resolution simplification
while (!(start % 4) && !(length % 4) && order > 0) {
start /= 4;
length /= 4;
order -= 1;
}
}
// prevent overflow
var maxPos = Math.pow(4, order);
start = Math.min(start, maxPos);
length = Math.min(length, maxPos - start);
// nSide is on a binary boundary 2^0, 2^1, 2^2, ...
var nSide = Math.pow(2, order),
cellWidth = sideSize / nSide;
var vertices = [];
for (var i = 0; i < length; i++) {
vertices.push(zOrder.distance2Point(start + i));
}
return {
cellWidth: cellWidth,
pathVertices: vertices
};
}
}
export { morton as default };