You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PackCharts's default shelf-width heuristic and its shelf-wrap test disagree about how padding is accounted for, so charts wrap to a new shelf earlier than the heuristic intends. At low chart counts one premature wrap is enough to turn a would-be square atlas into a narrow column.
Introduced with F2 (#18, PR #99). Not a correctness bug — the layout is still valid and non-overlapping — but it wastes atlas area, which matters as soon as the packed extent is fit to a square texture.
Observed
examples/src/MultiChartFlatten.cpp (two charts, each ≈1 × 2 after bounding-box minimization, padding = 0.1, normalize = true) reports:
Packed atlas extent: [0, 0] -> [0.27907, 1]
The two charts stack into a single column (1.2 × 4.3 before normalization) even though side-by-side would give an almost exactly square 2.3 × 2.2 atlas.
Cause
The target width is computed from padded chart areas:
but the wrap test measures the unpadded right edge of the chart being placed, against a cursor that already starts at the perimeter inset pad:
if (cursorX > pad and cursorX + width[i] > targetWidth) { /* wrap */ }
Worked through for the example above:
step
value
areaSum
2 × (1.1 × 2.1) = 4.62
targetWidth
sqrt(4.62) ≈ 2.149
chart A placed at cursorX = 0.1
0.1 + 1 = 1.1 ≤ 2.149 ✔
cursor advances
0.1 + 1 + 0.1 = 1.2
chart B
1.2 + 1 = 2.2 > 2.149 → wrap
Two 1 × 2 charts side by side actually need width pad + 1 + pad + 1 + pad = 2.3 and height pad + 2 + pad = 2.2, i.e. area 5.06 — more than the 4.62 the heuristic estimated. sqrt(Σ (w+pad)(h+pad)) under-counts padding, because a row of k charts needs k+1 horizontal gutters (the perimeter inset at both ends plus the interior ones), not k. The estimate then falls just below the width the charts need, and the row wraps by 0.05 units.
Suggested directions
Either is fine; the point is that the two computations should use one consistent notion of the padding budget.
Account for the gutters the layout actually spends. Estimate the row capacity including the perimeter inset — e.g. derive targetWidth from unpadded areas and add a pad allowance for the expected charts-per-row, or compare cursorX + width[i] + pad against targetWidth + pad so both sides include the trailing gutter.
Wrap on aspect ratio rather than a fixed width. Place a chart on the current shelf when doing so leaves the running extent closer to square than wrapping would. This removes the estimate/measurement mismatch entirely and is robust at low chart counts, where a single wrap decision dominates the result.
Notes
Worst at 2–4 charts; the relative error shrinks as chart count grows and rows fill.
Tests that pin exact extents (PaddingSeparatesChartsInSingleRow, PaddingSurroundsChartsAtPerimeter) set target_width explicitly, so they constrain layout but not the heuristic — whichever direction is taken needs a test asserting the default heuristic's atlas aspect ratio.
Summary
PackCharts's default shelf-width heuristic and its shelf-wrap test disagree about howpaddingis accounted for, so charts wrap to a new shelf earlier than the heuristic intends. At low chart counts one premature wrap is enough to turn a would-be square atlas into a narrow column.Introduced with F2 (#18, PR #99). Not a correctness bug — the layout is still valid and non-overlapping — but it wastes atlas area, which matters as soon as the packed extent is fit to a square texture.
Observed
examples/src/MultiChartFlatten.cpp(two charts, each ≈1 × 2 after bounding-box minimization,padding = 0.1,normalize = true) reports:The two charts stack into a single column (1.2 × 4.3 before normalization) even though side-by-side would give an almost exactly square 2.3 × 2.2 atlas.
Cause
The target width is computed from padded chart areas:
but the wrap test measures the unpadded right edge of the chart being placed, against a cursor that already starts at the perimeter inset
pad:Worked through for the example above:
areaSum2 × (1.1 × 2.1)= 4.62targetWidthsqrt(4.62)≈ 2.149cursorX = 0.10.1 + 1 = 1.1 ≤ 2.149✔0.1 + 1 + 0.1 = 1.21.2 + 1 = 2.2 > 2.149→ wrapTwo 1 × 2 charts side by side actually need width
pad + 1 + pad + 1 + pad= 2.3 and heightpad + 2 + pad= 2.2, i.e. area 5.06 — more than the 4.62 the heuristic estimated.sqrt(Σ (w+pad)(h+pad))under-counts padding, because a row of k charts needs k+1 horizontal gutters (the perimeter inset at both ends plus the interior ones), not k. The estimate then falls just below the width the charts need, and the row wraps by 0.05 units.Suggested directions
Either is fine; the point is that the two computations should use one consistent notion of the padding budget.
targetWidthfrom unpadded areas and add a pad allowance for the expected charts-per-row, or comparecursorX + width[i] + padagainsttargetWidth + padso both sides include the trailing gutter.Notes
PaddingSeparatesChartsInSingleRow,PaddingSurroundsChartsAtPerimeter) settarget_widthexplicitly, so they constrain layout but not the heuristic — whichever direction is taken needs a test asserting the default heuristic's atlas aspect ratio.