Public
Edited
Aug 13, 2022
1 star
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
algorithms = ({
// See Desmos graph: https://www.desmos.com/calculator/tmajzuq5tm

"Minimum rows × cols": (containerAspectRatio, facetAspectRatio, n) => {
const ratio = containerAspectRatio / facetAspectRatio;
const cols = Math.min(Math.sqrt(n * ratio), n);
const rows = Math.min(n / cols, n);

const rows_ceil = Math.ceil(rows);
const cols_ceil = Math.ceil(cols);
const rows_floor = Math.floor(rows);
const cols_floor = Math.floor(cols);

if (rows_ceil * cols_floor >= n) {
return {
rows: rows_ceil,
cols: cols_floor
};
} else if (rows_floor * cols_ceil >= n) {
return {
rows: rows_floor,
cols: cols_ceil
};
} else {
return {
rows: rows_ceil,
cols: cols_ceil
};
}
},

// "Minimum rows × cols (always rows ≥ cols)": (containerAspectRatio, facetAspectRatio, n) => {
// const ratio = facetAspectRatio / containerAspectRatio
// const rows = Math.min(Math.sqrt(n * ratio), n)
// const cols = Math.min(n / rows, n)

// const rows_ceil = Math.ceil(rows)
// const cols_ceil = Math.ceil(cols)
// const rows_floor = Math.floor(rows)
// const cols_floor = Math.floor(cols)

// if (rows_ceil * cols_floor >= n) {
// return {
// rows: rows_ceil,
// cols: cols_floor
// }
// } else {
// return {
// rows: rows_ceil,
// cols: cols_ceil
// }
// }
// },

"Round col, then derive row": (containerAspectRatio, facetAspectRatio, n) => {
const ratio = facetAspectRatio / containerAspectRatio;
const rows_initial = Math.min(Math.sqrt(n * ratio), n);
const cols_initial = Math.min(n / rows_initial, n);

const cols = Math.round(cols_initial);
const rows = Math.ceil(n / cols);
return {
rows,
cols
};
},

"✅ Round col, then derive row, remove extra columns": (
containerAspectRatio,
idealAspectRatio,
n
) => {
const ratio = containerAspectRatio / idealAspectRatio;
// Prefer vertical grid for count=2.
if (count === 2 && ratio < 2) return { rows: 2, columns: 1 };
// Otherwise, optimize for closest to the ideal aspect ratio.
const initialColumns = Math.min(
Math.round(Math.sqrt(count * ratio)),
count
);
const rows = Math.ceil(count / initialColumns);
// Remove extra columns if we can fit everything in fewer.
// This will result in wider aspect ratios than ideal, which is ok.
const columns = Math.ceil(count / rows);

return {
rows,
cols: columns
};
},

"Floor col, then derive row": (containerAspectRatio, facetAspectRatio, n) => {
const ratio = facetAspectRatio / containerAspectRatio;
const rows = Math.min(Math.sqrt(n * ratio), n);
const cols = Math.min(n / rows, n);

const cols_temp = Math.floor(cols);
const rows_temp = Math.ceil(n / cols_temp);
return {
rows: rows_temp,
cols: cols_temp
};
},

"Round row, then derive col": (containerAspectRatio, facetAspectRatio, n) => {
const ratio = facetAspectRatio / containerAspectRatio;
const rows = Math.min(Math.sqrt(n * ratio), n);
const cols = Math.min(n / rows, n);

const rows_temp = Math.round(rows);
const cols_temp = Math.ceil(n / rows_temp);
return {
rows: rows_temp,
cols: cols_temp
};
}

// // Adapted from: https://math.stackexchange.com/a/2570649
// "StackOverflow #1": (containerAspectRatio, facetAspectRatio, n) => {
// const ratio = containerAspectRatio / facetAspectRatio;
// const cols = Math.sqrt(n * ratio);
// const rows = n / cols;

// // Find best option filling the whole height
// let rows1 = Math.ceil(rows);
// let cols1 = Math.ceil(n / rows1);
// while (rows1 * ratio < cols1) {
// rows1++;
// cols1 = Math.ceil(n / rows1);
// }
// const cellSize1 = 1 / rows1;

// // Find best option filling the whole width
// let cols2 = Math.ceil(cols);
// let rows2 = Math.ceil(n / cols2);
// while (cols2 < rows2 * ratio) {
// cols2++;
// rows2 = Math.ceil(n / cols2);
// }
// const cellSize2 = containerAspectRatio / cols2;

// // Find the best values
// if (cellSize1 < cellSize2) {
// return {
// rows: rows2,
// cols: cols2,
// };
// } else {
// return {
// rows: rows1,
// cols: cols1,
// };
// }
// },

// "StackOverflow #2": (containerAspectRatio, facetAspectRatio, n) => {
// const x = containerAspectRatio / facetAspectRatio;
// const y = 1;

// let sx, sy;

// const px = Math.ceil(Math.sqrt((n * x) / y));

// if (Math.floor((px * y) / x) * px < n) {
// sx = y / Math.ceil((px * y) / x);
// } else {
// sx = x / px;
// }

// var py = Math.ceil(Math.sqrt((n * y) / x));
// if (Math.floor((py * x) / y) * py < n) {
// sy = x / Math.ceil((x * py) / y);
// } else {
// sy = y / py;
// }

// const size = Math.max(sx, sy);
// const cols = Math.ceil(containerAspectRatio / (size * facetAspectRatio));
// const rows = Math.ceil(n / cols);

// return {
// cols,
// rows,
// };
// },
})
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