Skip to content

Off by one pixel layout error (potentially due to rounding issue) #803

Open
@Goose97

Description

taffy version

0.7.5

Platform

Rust

What you did

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 🤔 ?

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions