Skip to content

Commit df79d5c

Browse files
committed
Tune rounding precision to handle values in 1e4..0 range with 0.01 unscaled error
1 parent ddf9573 commit df79d5c

File tree

3 files changed

+76
-11
lines changed

3 files changed

+76
-11
lines changed

Sources/yoga/Yoga.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -788,11 +788,17 @@ bool YGLayoutNodeInternal(const YGNodeRef node,
788788
const char *reason,
789789
const YGConfigRef config);
790790

791-
bool YGFloatsEqual(const float a, const float b) {
791+
bool YGFloatsEqualWithPrecision(const float a, const float b, const float precision) {
792+
assert(precision > 0);
793+
792794
if (YGFloatIsUndefined(a)) {
793795
return YGFloatIsUndefined(b);
794796
}
795-
return fabs(a - b) < 0.0001f;
797+
return fabs(a - b) < precision;
798+
}
799+
800+
bool YGFloatsEqual(const float a, const float b) {
801+
return YGFloatsEqualWithPrecision(a, b, 0.0001f);
796802
}
797803

798804
static void YGNodePrintInternal(const YGNodeRef node,
@@ -3142,12 +3148,13 @@ float YGRoundValueToPixelGrid(const float value,
31423148
const float pointScaleFactor,
31433149
const bool forceCeil,
31443150
const bool forceFloor) {
3151+
const float roundingError = fmax(0.0001, 0.01 * pointScaleFactor);
31453152
float scaledValue = value * pointScaleFactor;
31463153
float fractial = fmodf(scaledValue, 1.0);
3147-
if (YGFloatsEqual(fractial, 0)) {
3154+
if (YGFloatsEqualWithPrecision(fractial, 0.0, roundingError)) {
31483155
// First we check if the value is already rounded
31493156
scaledValue = scaledValue - fractial;
3150-
} else if (YGFloatsEqual(fractial, 1.0)) {
3157+
} else if (YGFloatsEqualWithPrecision(fractial, 1.0, roundingError)) {
31513158
scaledValue = scaledValue - fractial + 1.0;
31523159
} else if (forceCeil) {
31533160
// Next we check if we need to use forced rounding
@@ -3157,7 +3164,7 @@ float YGRoundValueToPixelGrid(const float value,
31573164
} else {
31583165
// Finally we just round the value
31593166
scaledValue = scaledValue - fractial +
3160-
(fractial > 0.5f || YGFloatsEqual(fractial, 0.5f) ? 1.0f : 0.0f);
3167+
(fractial > 0.5f || YGFloatsEqualWithPrecision(fractial, 0.5f, roundingError) ? 1.0f : 0.0f);
31613168
}
31623169
return scaledValue / pointScaleFactor;
31633170
}

core-tests/YGPixelGridRounding.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <gtest/gtest.h>
2+
#include <yoga/Yoga.h>
3+
4+
// This tests
5+
TEST(YogaTest, pixel_grid_rounding_table) {
6+
const float kPointScale = 3;
7+
8+
const YGConfigRef config = YGConfigNew();
9+
YGConfigSetPointScaleFactor(config, kPointScale);
10+
11+
const float kSeparatorHeight = 1 / kPointScale;
12+
const float kCellContentHeight = 100.5;
13+
const int kCellsCount = 100;
14+
15+
const YGNodeRef root = YGNodeNewWithConfig(config);
16+
17+
int subnodesCount = 0;
18+
19+
for (int i = 0; i < kCellsCount; i++) {
20+
const YGNodeRef separator = YGNodeNewWithConfig(config);
21+
YGNodeStyleSetHeight(separator, kSeparatorHeight);
22+
YGNodeInsertChild(root, separator, subnodesCount++);
23+
24+
const YGNodeRef cell = YGNodeNewWithConfig(config);
25+
YGNodeSetNodeType(cell, YGNodeTypeText);
26+
YGNodeStyleSetHeight(cell, kCellContentHeight);
27+
YGNodeInsertChild(root, cell, subnodesCount++);
28+
}
29+
30+
const YGNodeRef separator = YGNodeNewWithConfig(config);
31+
YGNodeStyleSetHeight(separator, kSeparatorHeight);
32+
YGNodeInsertChild(root, separator, subnodesCount++);
33+
34+
YGNodeCalculateLayout(root, 375, YGUndefined, YGDirectionLTR);
35+
36+
EXPECT_LE(kCellsCount * (kSeparatorHeight + kCellContentHeight) + kSeparatorHeight, YGNodeLayoutGetHeight(root));
37+
EXPECT_FLOAT_EQ(375, YGNodeLayoutGetWidth(root));
38+
39+
for (int i = 0; i < YGNodeGetChildCount(root); i++) {
40+
const YGNodeRef child = YGNodeGetChild(root, i);
41+
const float childHeight = YGNodeLayoutGetHeight(child);
42+
43+
if (YGNodeGetNodeType(child) == YGNodeTypeText) {
44+
EXPECT_GT(childHeight, kCellContentHeight);
45+
} else {
46+
EXPECT_GT(childHeight, 0);
47+
}
48+
}
49+
50+
YGNodeFreeRecursive(root);
51+
52+
YGConfigFree(config);
53+
}

core-tests/YGRoundingFunctionTest.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@ TEST(YogaTest, rounding_value) {
1919
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, false, true));
2020

2121
// Test that numbers with fraction are rounded correctly accounting for ceil/floor flags
22-
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, false));
23-
ASSERT_FLOAT_EQ(6.5, YGRoundValueToPixelGrid(6.01, 2.0, true, false));
24-
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, true));
25-
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, false, false));
26-
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, true, false));
27-
ASSERT_FLOAT_EQ(5.5, YGRoundValueToPixelGrid(5.99, 2.0, false, true));
22+
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.1, 2.0, false, false));
23+
ASSERT_FLOAT_EQ(6.5, YGRoundValueToPixelGrid(6.1, 2.0, true, false));
24+
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.1, 2.0, false, true));
25+
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.9, 2.0, false, false));
26+
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.9, 2.0, true, false));
27+
ASSERT_FLOAT_EQ(5.5, YGRoundValueToPixelGrid(5.9, 2.0, false, true));
28+
29+
// Are we able to treat value as rounded for reasonably large number?
30+
ASSERT_FLOAT_EQ(527.6666666, YGRoundValueToPixelGrid(527.666, 3.0, false, true));
31+
ASSERT_FLOAT_EQ(527.6666666, YGRoundValueToPixelGrid(527.666, 3.0, true, false));
32+
ASSERT_FLOAT_EQ(527.6666666, YGRoundValueToPixelGrid(527.666, 3.0, true, true));
2833
}

0 commit comments

Comments
 (0)