Dynamic programming for even rows
This commit is contained in:
@@ -11,7 +11,7 @@ export interface Props {
|
|||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ROW_HEIGHT = 250;
|
export const ROW_HEIGHT = 350;
|
||||||
export const MOBILE_ROW_HEIGHT = 150;
|
export const MOBILE_ROW_HEIGHT = 150;
|
||||||
|
|
||||||
interface Row {
|
interface Row {
|
||||||
@@ -19,38 +19,101 @@ interface Row {
|
|||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BadList {
|
||||||
|
splits: number[];
|
||||||
|
badness: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class Grid extends React.PureComponent<Props, {}> {
|
export class Grid extends React.PureComponent<Props, {}> {
|
||||||
static displayName: string = "Grid";
|
static displayName: string = "Grid";
|
||||||
|
|
||||||
private gridHeight = 0;
|
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() {
|
render() {
|
||||||
this.gridHeight = 0;
|
this.gridHeight = 0;
|
||||||
|
|
||||||
let row: Model.Image[] = [];
|
const badList = this.rows(0);
|
||||||
const rows: Row[] = [];
|
let lastBreak = 0;
|
||||||
let rowWidth = 0;
|
const rows: Row[] = badList.splits.map((split) => {
|
||||||
|
const images = this.props.images.slice(lastBreak, split);
|
||||||
|
lastBreak = split;
|
||||||
|
|
||||||
this.props.images.forEach((image) => {
|
return {
|
||||||
const newWidth = rowWidth + image.width / image.height;
|
images,
|
||||||
const height = this.props.width / newWidth;
|
width: images.reduce((acc, img) => acc + img.width / img.height, 0),
|
||||||
|
};
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const images = rows.map((row) => {
|
const images = rows.map((row) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user