Skip to content

Commit

Permalink
Merge pull request #63 from zintus/pixel-grid-rounding
Browse files Browse the repository at this point in the history
Rounding behaviour
  • Loading branch information
lucdion authored Feb 28, 2018
2 parents 3677ce1 + 03bf7f8 commit d866b67
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 15 deletions.
8 changes: 4 additions & 4 deletions Sources/YogaKit/YGLayout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@ - (void)set##objc_capitalized_name:(YGValue)objc_lowercased_name

YGValue YGPointValue(CGFloat value)
{
return (YGValue) { .value = (YGUnit) value, .unit = (YGUnit) YGUnitPoint };
return (YGValue) { .value = (float) value, .unit = (YGUnit) YGUnitPoint };
}

YGValue YGPercentValue(CGFloat value)
{
return (YGValue) { .value = (YGUnit) value, .unit = YGUnitPercent };
return (YGValue) { .value = (float) value, .unit = YGUnitPercent };
}

static YGConfigRef globalConfig;
Expand Down Expand Up @@ -337,8 +337,8 @@ static YGSize YGMeasureView(
}];

return (YGSize) {
.width = (YGUnit) YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = (YGUnit) YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
.width = (float) YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = (float) YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
};
}

Expand Down
17 changes: 12 additions & 5 deletions Sources/yoga/Yoga.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -788,11 +788,17 @@ bool YGLayoutNodeInternal(const YGNodeRef node,
const char *reason,
const YGConfigRef config);

bool YGFloatsEqual(const float a, const float b) {
bool YGFloatsEqualWithPrecision(const float a, const float b, const float precision) {
assert(precision > 0);

if (YGFloatIsUndefined(a)) {
return YGFloatIsUndefined(b);
}
return fabs(a - b) < 0.0001f;
return fabs(a - b) < precision;
}

bool YGFloatsEqual(const float a, const float b) {
return YGFloatsEqualWithPrecision(a, b, 0.0001f);
}

static void YGNodePrintInternal(const YGNodeRef node,
Expand Down Expand Up @@ -3142,12 +3148,13 @@ float YGRoundValueToPixelGrid(const float value,
const float pointScaleFactor,
const bool forceCeil,
const bool forceFloor) {
const float roundingError = fmax(0.0001, 0.01 * pointScaleFactor);
float scaledValue = value * pointScaleFactor;
float fractial = fmodf(scaledValue, 1.0);
if (YGFloatsEqual(fractial, 0)) {
if (YGFloatsEqualWithPrecision(fractial, 0.0, roundingError)) {
// First we check if the value is already rounded
scaledValue = scaledValue - fractial;
} else if (YGFloatsEqual(fractial, 1.0)) {
} else if (YGFloatsEqualWithPrecision(fractial, 1.0, roundingError)) {
scaledValue = scaledValue - fractial + 1.0;
} else if (forceCeil) {
// Next we check if we need to use forced rounding
Expand All @@ -3157,7 +3164,7 @@ float YGRoundValueToPixelGrid(const float value,
} else {
// Finally we just round the value
scaledValue = scaledValue - fractial +
(fractial > 0.5f || YGFloatsEqual(fractial, 0.5f) ? 1.0f : 0.0f);
(fractial > 0.5f || YGFloatsEqualWithPrecision(fractial, 0.5f, roundingError) ? 1.0f : 0.0f);
}
return scaledValue / pointScaleFactor;
}
Expand Down
57 changes: 57 additions & 0 deletions core-tests/YGPixelGridRounding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <gtest/gtest.h>
#include <yoga/Yoga.h>

// This test scrutinize next behaviours:
// - pixel grid snapping in 1e4..0 coordinate range
// - ability to layout nodes with smallest possible dimensions (one pixel separators)
// - providing text node layout with bounds strictly larger than sized

TEST(YogaTest, pixel_grid_rounding_table) {
const float kPointScale = 3;

const YGConfigRef config = YGConfigNew();
YGConfigSetPointScaleFactor(config, kPointScale);

const float kSeparatorHeight = 1 / kPointScale;
const float kCellContentHeight = 44.5;
const int kCellsCount = 100;

const YGNodeRef root = YGNodeNewWithConfig(config);

int subnodesCount = 0;

for (int i = 0; i < kCellsCount; i++) {
const YGNodeRef separator = YGNodeNewWithConfig(config);
YGNodeStyleSetHeight(separator, kSeparatorHeight);
YGNodeInsertChild(root, separator, subnodesCount++);

const YGNodeRef cell = YGNodeNewWithConfig(config);
YGNodeSetNodeType(cell, YGNodeTypeText);
YGNodeStyleSetHeight(cell, kCellContentHeight);
YGNodeInsertChild(root, cell, subnodesCount++);
}

const YGNodeRef separator = YGNodeNewWithConfig(config);
YGNodeStyleSetHeight(separator, kSeparatorHeight);
YGNodeInsertChild(root, separator, subnodesCount++);

YGNodeCalculateLayout(root, 375, YGUndefined, YGDirectionLTR);

EXPECT_LE(kCellsCount * (kSeparatorHeight + kCellContentHeight) + kSeparatorHeight, YGNodeLayoutGetHeight(root));
EXPECT_FLOAT_EQ(375, YGNodeLayoutGetWidth(root));

for (int i = 0; i < YGNodeGetChildCount(root); i++) {
const YGNodeRef child = YGNodeGetChild(root, i);
const float childHeight = YGNodeLayoutGetHeight(child);

if (YGNodeGetNodeType(child) == YGNodeTypeText) {
EXPECT_GT(childHeight, kCellContentHeight);
} else {
EXPECT_GT(childHeight, 0);
}
}

YGNodeFreeRecursive(root);

YGConfigFree(config);
}
17 changes: 11 additions & 6 deletions core-tests/YGRoundingFunctionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ TEST(YogaTest, rounding_value) {
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, false, true));

// Test that numbers with fraction are rounded correctly accounting for ceil/floor flags
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, false));
ASSERT_FLOAT_EQ(6.5, YGRoundValueToPixelGrid(6.01, 2.0, true, false));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, true));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, false, false));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, true, false));
ASSERT_FLOAT_EQ(5.5, YGRoundValueToPixelGrid(5.99, 2.0, false, true));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.1, 2.0, false, false));
ASSERT_FLOAT_EQ(6.5, YGRoundValueToPixelGrid(6.1, 2.0, true, false));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.1, 2.0, false, true));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.9, 2.0, false, false));
ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.9, 2.0, true, false));
ASSERT_FLOAT_EQ(5.5, YGRoundValueToPixelGrid(5.9, 2.0, false, true));

// Are we able to treat value as rounded for reasonably large number?
ASSERT_FLOAT_EQ(527.6666666, YGRoundValueToPixelGrid(527.666, 3.0, false, true));
ASSERT_FLOAT_EQ(527.6666666, YGRoundValueToPixelGrid(527.666, 3.0, true, false));
ASSERT_FLOAT_EQ(527.6666666, YGRoundValueToPixelGrid(527.666, 3.0, true, true));
}

0 comments on commit d866b67

Please sign in to comment.