Dynamic programming for even rows

This commit is contained in:
2024-06-19 13:13:43 -07:00
parent f9c0b9ae09
commit ad07adfb5f

View File

@@ -11,7 +11,7 @@ export interface Props {
height: number;
}
export const ROW_HEIGHT = 250;
export const ROW_HEIGHT = 350;
export const MOBILE_ROW_HEIGHT = 150;
interface Row {
@@ -19,38 +19,101 @@ interface Row {
width: number;
}
interface BadList {
splits: number[];
badness: number;
}
export class Grid extends React.PureComponent<Props, {}> {
static displayName: string = "Grid";
private gridHeight = 0;
static badness = (
row: Model.Image[],
width: number,
height: number
): number => {
const rowWidth = row.reduce((w, img) => w + img.width / img.height, 0);
const rowHeight = width / rowWidth;
return (rowHeight - height) * (rowHeight - height);
};
// [width][idx] -> badness
private rowsMemo: Map<number, Map<number, BadList>> = new Map();
rows(idx: number): BadList {
const targetHeight = this._rowHeight();
const memo = this.rowsMemo.get(this.props.width) ?? new Map();
const maybeMemo = memo.get(idx);
if (maybeMemo) {
return maybeMemo;
}
if (idx === this.props.images.length) {
return {
splits: [],
badness: 0,
};
}
if (idx === this.props.images.length - 1) {
const img = this.props.images[idx];
const h = (img.height * this.props.width) / img.width;
return {
splits: [],
badness: (targetHeight - h) * (targetHeight - h),
};
}
let bestIdx = -1;
let leastBad = 1e50;
let bestSplits: number[] = [];
for (let i = idx + 1; i <= this.props.images.length; i++) {
const rowBadness = Grid.badness(
this.props.images.slice(idx, i),
this.props.width,
targetHeight
);
const rest = this.rows(i);
const badness = rest.badness + rowBadness;
if (badness < leastBad) {
leastBad = badness;
bestIdx = i;
bestSplits = [i, ...rest.splits];
}
}
const badList = {
splits: bestSplits,
badness: leastBad,
};
memo.set(idx, badList);
if (!this.rowsMemo.has(this.props.width)) {
this.rowsMemo.set(this.props.width, memo);
}
return badList;
}
render() {
this.gridHeight = 0;
let row: Model.Image[] = [];
const rows: Row[] = [];
let rowWidth = 0;
const badList = this.rows(0);
let lastBreak = 0;
const rows: Row[] = badList.splits.map((split) => {
const images = this.props.images.slice(lastBreak, split);
lastBreak = split;
this.props.images.forEach((image) => {
const newWidth = rowWidth + image.width / image.height;
const height = this.props.width / newWidth;
if (height < this._rowHeight()) {
rows.push({
images: row,
width: rowWidth,
});
row = [];
rowWidth = image.width / image.height;
} else {
rowWidth = newWidth;
}
row.push(image);
});
rows.push({
images: row,
width: rowWidth,
return {
images,
width: images.reduce((acc, img) => acc + img.width / img.height, 0),
};
});
const images = rows.map((row) => {