Off by one pixel layout error (potentially due to rounding issue)
- Dominant language
- Rust
- Stars
- 3.6k
- Forks
- 226
- Avg merge
- 10h 41m
- Merged PRs (30d)
- 40
Description
## `taffy` version
0.7.5
## Platform
Rust
## What you did
```rust
use taffy::prelude::*;
use taffy::util::print_tree;
fn main() {
let mut tree: TaffyTree<()> = TaffyTree::new();
let text = tree
.new_leaf(Style {
size: Size {
width: length(1.0),
height: length(1.0),
},
..Default::default()
})
.unwrap();
let flex_container = tree
.new_with_children(
Style {
justify_content: Some(JustifyContent::End),
border: Rect {
top: length(1.0),
left: length(1.0),
right: length(1.0),
bottom: length(1.0),
},
grid_row: Line {
start: line(1),
end: span(4),
},
grid_column: Line {
start: line(2),
end: span(1),
},
..Default::default()
},
&[text],
)
.unwrap();
let grid_container = tree
.new_with_children(
Style {
size: Size {
width: percent(1.0),
height: percent(1.0),
},
display: Display::Grid,
// 12 by 12 grid
grid_template_rows: vec![repeat(GridTrackRepetition::Count(12), vec![fr(1.0)])],
grid_template_columns: vec![repeat(GridTrackRepetition::Count(12), vec![fr(1.0)])],
..Default::default()
},
&[flex_container],
)
.unwrap();
tree.compute_layout(
grid_container,
Size {
width: AvailableSpace::Definite(127.0),
height: AvailableSpace::Definite(29.0),
},
)
.unwrap();
print_tree(&tree, grid_container);
}
```
## What went wrong
The above layout print this:
```
TREE
└── GRID [x: 0 y: 0 w: 127 h: 29 content_w: 21 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967299))
└── FLEX ROW [x: 11 y: 0 w: 10 h: 10 content_w: 9 content_h: 2 border: l:1 r:1 t:1 b:1, padding: l:0 r:0 t:0 b:0] (NodeId(4294967298))
└── LEAF [x: 9 y: 1 w: 1 h: 1 content_w: 0 content_h: 0 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967297))
```
Notice the leaf node, the parent width is 10 with border of 1. The leaf node is aligned at the right edge (justify content = end) of the parent. It should be 8, instead of 9. It produces a layout like this (`-` means empty pixel):
```
-----------┌────────┐---------------------------------------------------------------------------------------------------------
-----------│--------A---------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------│--------│----------------------------------------------------------------------------------------------------------
-----------└────────┘----------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------
```
The letter A is the position of the leaf node. As you can see, it is overlapping with the border.
## Additional information
### Context
I'm building a terminal UI application and using taffy as the layout engine. Due to the nature of terminal UI, off by one pixel errors are very noticeable.
### My theory
Since it doesn't happen consistently, only on some specific viewport sizes, I suspect it is caused by rounding errors. Maybe [these lines](https://github.com/DioxusLabs/taffy/blob/HEAD/src/compute/mod.rs#L216-L217) 🤔 ?
In here, the relative location of a child node is always rounded. **It doesn't take into consideration how the parent size changes due to rounding**. In my example, the parent node is shrink by 1 (from 11 to 10) after rounding, but the child node still round up. The situation is reversed if the parent grow by 1, it will create a gap of 1 pixel between the child and the parent border.
I attempted to fix it my making the child aware of how the parent's size changes. Each time we perform rounding for a child node, we pass a scale factor: how much the parent node have grow/shrink:
```
scale = layout.size.width / unrounded_layout.size.width
```
The child location (x, y) will be scale up according to the scale factor:
```
layout.location.x = round(unrounded_layout.location.x * scale_factor.width)
layout.location.y = round(unrounded_layout.location.y * scale_factor.height)
```
It did fix my cases but broke a lot of tests.
Contributor guide
Research direction
Reproduce the 127×29 layout from the issue, then inspect src/compute/mod.rs around lines 216–217 where child positions are rounded. Compare the rounded parent and child layouts across viewport sizes, and use the existing layout tests to check any change. Done means the leaf no longer overlaps the parent border without breaking the existing test suite.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100