Published
Edited
Oct 22, 2020
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// generates a rows x columns matrix, using values from a Perlin noise field.
matrix = d3.range(0, rows).map(i => {
return d3.range(0, columns).map(j => noise(i / resolution, j / resolution));
});
Insert cell
cellTypesMatrix = d3.range(0, rows - 1).map(i => {
return d3.range(0, columns - 1).map(j => { return {row: i, col: j, type: getCellType(i, j) }});
});
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
threshold = 0.15;
Insert cell
// returns 1 if the value is above the threshold, 0 otherwise.
function getIsoValue(value) {
return +(value > threshold);
}
Insert cell
Insert cell
/**
* Returns the type of a cell in the matrix (in decimal), based on what values sit on its corners.
* @param{i} row index
* @param{j} column index
*/
function getCellType(row, col) {
const a = getIsoValue(matrix[row][col]);
const b = getIsoValue(matrix[row][col+1]);
const c = getIsoValue(matrix[row+1][col+1]);
const d = getIsoValue(matrix[row+1][col]);
return 8*a + 4*b + 2*c + d;
}
Insert cell
Insert cell
Insert cell
/**
* Renders a segment of the marching squares, from (x1, y1) to (x2, y2).
* @param{types} list of allowed type numbers (in decimal)
* @param{x1} starting x position
* @param{x2} starting y position
* @param{y1} ending x position
* @param{y2} ending y position
*/
function renderLineSegment(types, x1, x2, y1, y2) {
if (!(types instanceof Array)) {
throw new TypeError('Parameter "types" of must be an array');
}
const segments = canvas.selectAll("g.segment");
segments.filter(d => types.indexOf(d.type) > -1).append("line")
.attr("x1", x1 * spacing)
.attr("x2", x2 * spacing)
.attr("y1", y1 * spacing)
.attr("y2", y2 * spacing);
}
Insert cell
Insert cell
/**
* Renders the segments from the Marching Squares algorithm for each 2x2 neighborhood in the data, based on the
* type of each cell.
*/
function renderSegments() {
// diagonale top left
renderLineSegment([5, 7, 8], 0, 0.5, 0.5, 0);
// diagonale top right
renderLineSegment([4, 10, 11], 0.5, 1, 0, 0.5);
// diagonale bottom left
renderLineSegment([1, 10, 14], 0, 0.5, 0.5, 1);
// diagonale bottom right
renderLineSegment([2, 5, 13], 0.5, 1, 1, 0.5);
// vertical bar
renderLineSegment([6, 9], 0.5, 0.5, 0, 1);
// horizontal bar
renderLineSegment([3, 12], 0, 1, 0.5, 0.5);
}
Insert cell
// color of the line segments
segmentColor = "red";
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// specifies the gradient in the Perlin field. The lower, the steeper the local gradient.
resolution = 32
Insert cell
// number of levels that the Perlin noise can reach. The smaller, the "rougher" the appearance.
perlinOctaves = 16;
Insert cell
// number of columns of the matrix
columns = 250;
Insert cell
// number of rows of the matrix
rows = 125;
Insert cell
// if true, the type of each 2x2 neighborhood is rendered (mostly for debugging).
showGrid = false
Insert cell
// if true, renders the dots that represent the Perlin noise at a cell in the matrix.
showDots = true
Insert cell
// if true, renders the marching square lines.
showLines = true
Insert cell
// vertical size of the plot
height = 500;
Insert cell
// size of each dot in px
dotRadius = 3;
Insert cell
// size of the line segments
segmentWidth = 3;
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more