I tried to implement the following using AI but gave up after ~100 iterations…
Here is the algorithm:
- the page has 2 columns, all blocks are breakable and come from a database, I can create an array of blocks and want to render them at width: 100%.
- I want to render the blocks one by another using this logic:
- if the current block C fits completely on current page at full height, use this one.
- otherwise: search in the next k (for example k=4) blocks and find the block with the highest height that fits completely on the current page. Use this one instead. If no one from this k blocks fit, then we use C. If one from the k blocks fitted: keep in mind that C is the prefered in the next iteration.
- repeat this after all blocks are rendered.
This would minimize white space on the page quite good because those blocks have different heights. The constant k is limited for performance reasons but also because the elements are sorted by time, so a newer element should not appear at a very late position.
Is there already a package that implements this?
AI gives me some code like this:
#set page(
paper: "a4",
margin: 1.5cm,
columns: 2,
)
#set par(
spacing: 0pt,
)
#let page-height = 26.7cm
#let k = 4
#let heights = (
4.2cm, 8.7cm, 3.4cm, 6.1cm,
9.3cm, 5.2cm, 7.8cm, 3.8cm,
6.9cm, 9.8cm, 4.7cm, 7.1cm,
5.6cm, 8.1cm, 3.2cm, 6.5cm,
7.4cm, 4.9cm, 8.3cm, 3.7cm,
6.4cm, 9.1cm, 5.5cm, 7.7cm,
4.1cm, 8.9cm, 3.5cm, 6.8cm,
9.6cm, 5.8cm, 7.2cm, 4.6cm,
8.4cm, 3.9cm, 6.3cm, 9.2cm,
5.1cm, 7.9cm, 4.4cm, 8.6cm,
3.6cm, 6.7cm, 9.7cm, 5.4cm,
7.3cm, 4.8cm, 8.2cm, 3.3cm,
)
#let blocks = heights.enumerate().map(((i, height)) => {
rect(
width: 100%,
height: height - 1pt,
fill: white,
stroke: 0.5pt + black,
inset: 5pt,
)[
#align(center + horizon)[
#text(size: 14pt)[#i]
]
]
})
#let remaining = blocks
#let remaining-heights = heights
#let used = 0cm
#while remaining.len() > 0 {
let first-height = remaining-heights.at(0)
let selected = 0
if used + first-height > page-height {
let best-height = 0cm
let found = false
for i in range(0, calc.min(k, remaining.len())) {
let h = remaining-heights.at(i)
if used + h <= page-height {
if not found or h > best-height {
selected = i
best-height = h
found = true
}
}
}
if not found {
selected = 0
}
}
remaining.at(selected)
used += remaining-heights.at(selected)
remaining = (
remaining.slice(0, selected)
+ remaining.slice(selected + 1)
)
remaining-heights = (
remaining-heights.slice(0, selected)
+ remaining-heights.slice(selected + 1)
)
}
Does only work for the first page and does not use measure. For my real blocks I do not know the heights.