Skip to content

Commit

Permalink
Fix linear_gradient float precision bug.
Browse files Browse the repository at this point in the history
This was reported by:
#998

Indeed, the `t` interpolation factor, which is itself interpolated might become
slightly larger than 1.0. This is due to the float precision.
This was supposedly handled, but there was an off-by-one error in the check.

Along the way, fix a bug found by a fuzzer.

Bug: #998
Fixed: #998
  • Loading branch information
ArthurSonzogni committed Feb 10, 2025
1 parent 15587da commit d75108e
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 6 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ current (development)
See #932
- Feature: Add `SliderOption::on_change`. This allows to set a callback when the
slider value changes. See #938.
- Bugfix: Handle `Dropdown` with no entries.
- Bugfix: Fix crash in `LinearGradient` due to float precision and an off-by-one
mistake. See #998.

### Dom
- Feature: Add `hscroll_indicator`. It display an horizontal indicator
Expand Down
4 changes: 0 additions & 4 deletions cmake/ftxui_set_options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ function(ftxui_set_options library)
target_compile_options(${library} PRIVATE "-Wpedantic")
target_compile_options(${library} PRIVATE "-Wshadow")
target_compile_options(${library} PRIVATE "-Wunused")

if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${library} PRIVATE "-Wuseless-cast")
endif()
endif()
endif()

Expand Down
1 change: 1 addition & 0 deletions cmake/ftxui_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_executable(ftxui-tests
src/ftxui/component/component_test.cpp
src/ftxui/component/component_test.cpp
src/ftxui/component/container_test.cpp
src/ftxui/component/dropdown_test.cpp
src/ftxui/component/hoverable_test.cpp
src/ftxui/component/input_test.cpp
src/ftxui/component/menu_test.cpp
Expand Down
6 changes: 5 additions & 1 deletion src/ftxui/component/dropdown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ Component Dropdown(DropdownOption option) {
Element Render() override {
radiobox.selected =
util::clamp(radiobox.selected(), 0, int(radiobox.entries.size()) - 1);
title_ = radiobox.entries[selected_()];
selected_ = util::clamp(selected_(), 0, int(radiobox.entries.size()) - 1);

if (selected_() >= 0) {
title_ = radiobox.entries[selected_()];
}

return transform(*open_, checkbox_->Render(), radiobox_->Render());
}
Expand Down
34 changes: 34 additions & 0 deletions src/ftxui/component/dropdown_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2025 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Button, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
#include "gtest/gtest.h" // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_FALSE, Test, EXPECT_TRUE, TEST

namespace ftxui {

TEST(DropdownTest, Empty) {
std::vector<std::string> list = {};
int index = 0;
auto dropdown = Dropdown(list, &index);

dropdown->OnEvent(Event::Return);

auto screen = Screen(8, 8);
auto document = dropdown->Render();
Render(screen, document);

EXPECT_EQ(screen.ToString(),
"╭──────╮\r\n"
"│↓ │\r\n"
"├──────┤\r\n"
"│ │\r\n"
"│ │\r\n"
"│ │\r\n"
"│ │\r\n"
"╰──────╯");
}

} // namespace ftxui
6 changes: 5 additions & 1 deletion src/ftxui/dom/linear_gradient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ Color Interpolate(const LinearGradientNormalized& gradient, float t) {
// Find the right color in the gradient's stops.
size_t i = 1;
while (true) {
if (i > gradient.positions.size()) {
// Note that `t` might be slightly greater than 1.0 due to floating point
// precision. This is why we need to handle the case where `t` is greater
// than the last stop's position.
// See https://github.com/ArthurSonzogni/FTXUI/issues/998
if (i >= gradient.positions.size()) {
const float half = 0.5F;
return Color::Interpolate(half, gradient.colors.back(),
gradient.colors.back());
Expand Down

0 comments on commit d75108e

Please sign in to comment.