diff --git a/tests/YGFloatOptionalTest.cpp b/tests/YGFloatOptionalTest.cpp index c35f7b2e..656c81f7 100644 --- a/tests/YGFloatOptionalTest.cpp +++ b/tests/YGFloatOptionalTest.cpp @@ -7,10 +7,12 @@ #include -#include +#include #include #include +using namespace facebook; + constexpr auto empty = YGFloatOptional{}; constexpr auto zero = YGFloatOptional{0.0f}; constexpr auto one = YGFloatOptional{1.0f}; @@ -192,13 +194,13 @@ TEST(YGFloatOptional, addition) { ASSERT_EQ(negative + empty, empty); } -TEST(YGFloatOptionalTest, YGFloatOptionalMax) { - ASSERT_EQ(YGFloatOptionalMax(empty, empty), empty); - ASSERT_EQ(YGFloatOptionalMax(empty, positive), positive); - ASSERT_EQ(YGFloatOptionalMax(negative, empty), negative); - ASSERT_EQ(YGFloatOptionalMax(negative, YGFloatOptional{-INFINITY}), negative); +TEST(YGFloatOptionalTest, maxOrDefined) { + ASSERT_EQ(yoga::maxOrDefined(empty, empty), empty); + ASSERT_EQ(yoga::maxOrDefined(empty, positive), positive); + ASSERT_EQ(yoga::maxOrDefined(negative, empty), negative); + ASSERT_EQ(yoga::maxOrDefined(negative, YGFloatOptional{-INFINITY}), negative); ASSERT_EQ( - YGFloatOptionalMax(YGFloatOptional{1.0f}, YGFloatOptional{1.125f}), + yoga::maxOrDefined(YGFloatOptional{1.0f}, YGFloatOptional{1.125f}), YGFloatOptional{1.125f}); } diff --git a/yoga/Utils.cpp b/yoga/Utils.cpp deleted file mode 100644 index 7cc94022..00000000 --- a/yoga/Utils.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "Utils.h" -#include - -using namespace facebook; - -YGFlexDirection YGFlexDirectionCross( - const YGFlexDirection flexDirection, - const YGDirection direction) { - return YGFlexDirectionIsColumn(flexDirection) - ? YGResolveFlexDirection(YGFlexDirectionRow, direction) - : YGFlexDirectionColumn; -} - -float YGFloatMax(const float a, const float b) { - if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { - return fmaxf(a, b); - } - return yoga::isUndefined(a) ? b : a; -} - -float YGFloatMin(const float a, const float b) { - if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { - return fminf(a, b); - } - - return yoga::isUndefined(a) ? b : a; -} - -bool YGValueEqual(const YGValue& a, const YGValue& b) { - if (a.unit != b.unit) { - return false; - } - - if (a.unit == YGUnitUndefined || - (yoga::isUndefined(a.value) && yoga::isUndefined(b.value))) { - return true; - } - - return fabs(a.value - b.value) < 0.0001f; -} - -bool YGFloatsEqual(const float a, const float b) { - if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { - return fabs(a - b) < 0.0001f; - } - return yoga::isUndefined(a) && yoga::isUndefined(b); -} - -bool YGDoubleEqual(const double a, const double b) { - if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { - return fabs(a - b) < 0.0001; - } - return yoga::isUndefined(a) && yoga::isUndefined(b); -} - -float YGFloatSanitize(const float val) { - return yoga::isUndefined(val) ? 0 : val; -} - -YGFloatOptional YGFloatOptionalMax(YGFloatOptional op1, YGFloatOptional op2) { - if (op1 >= op2) { - return op1; - } - if (op2 > op1) { - return op2; - } - return op1.isUndefined() ? op2 : op1; -} - -void yoga::throwLogicalErrorWithMessage(const char* message) { -#if defined(__cpp_exceptions) - throw std::logic_error(message); -#else // !defined(__cpp_exceptions) - std::terminate(); -#endif // defined(__cpp_exceptions) -} diff --git a/yoga/Utils.h b/yoga/Utils.h deleted file mode 100644 index bd8ab309..00000000 --- a/yoga/Utils.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -// This struct is an helper model to hold the data for step 4 of flexbox algo, -// which is collecting the flex items in a line. -// -// - itemsOnLine: Number of items which can fit in a line considering the -// available Inner dimension, the flex items computed flexbasis and their -// margin. It may be different than the difference between start and end -// indicates because we skip over absolute-positioned items. -// -// - sizeConsumedOnCurrentLine: It is accumulation of the dimensions and margin -// of all the children on the current line. This will be used in order to -// either set the dimensions of the node if none already exist or to compute -// the remaining space left for the flexible children. -// -// - totalFlexGrowFactors: total flex grow factors of flex items which are to be -// laid in the current line -// -// - totalFlexShrinkFactors: total flex shrink factors of flex items which are -// to be laid in the current line -// -// - endOfLineIndex: Its the end index of the last flex item which was examined -// and it may or may not be part of the current line(as it may be absolutely -// positioned or including it may have caused to overshoot availableInnerDim) -// -// - relativeChildren: Maintain a vector of the child nodes that can shrink -// and/or grow. - -struct YGCollectFlexItemsRowValues { - uint32_t itemsOnLine; - float sizeConsumedOnCurrentLine; - float totalFlexGrowFactors; - float totalFlexShrinkScaledFactors; - uint32_t endOfLineIndex; - std::vector relativeChildren; - float remainingFreeSpace; - // The size of the mainDim for the row after considering size, padding, margin - // and border of flex items. This is used to calculate maxLineDim after going - // through all the rows to decide on the main axis size of owner. - float mainDim; - // The size of the crossDim for the row after considering size, padding, - // margin and border of flex items. Used for calculating containers crossSize. - float crossDim; -}; - -bool YGValueEqual(const YGValue& a, const YGValue& b); -inline bool YGValueEqual( - facebook::yoga::CompactValue a, - facebook::yoga::CompactValue b) { - return YGValueEqual((YGValue) a, (YGValue) b); -} - -// This custom float equality function returns true if either absolute -// difference between two floats is less than 0.0001f or both are undefined. -bool YGFloatsEqual(const float a, const float b); - -bool YGDoubleEqual(const double a, const double b); - -float YGFloatMax(const float a, const float b); - -YGFloatOptional YGFloatOptionalMax( - const YGFloatOptional op1, - const YGFloatOptional op2); - -float YGFloatMin(const float a, const float b); - -// This custom float comparison function compares the array of float with -// YGFloatsEqual, as the default float comparison operator will not work(Look -// at the comments of YGFloatsEqual function). -template -bool YGFloatArrayEqual( - const std::array& val1, - const std::array& val2) { - bool areEqual = true; - for (std::size_t i = 0; i < size && areEqual; ++i) { - areEqual = YGFloatsEqual(val1[i], val2[i]); - } - return areEqual; -} - -// This function returns 0 if YGFloatIsUndefined(val) is true and val otherwise -float YGFloatSanitize(const float val); - -YGFlexDirection YGFlexDirectionCross( - const YGFlexDirection flexDirection, - const YGDirection direction); - -inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) { - return flexDirection == YGFlexDirectionRow || - flexDirection == YGFlexDirectionRowReverse; -} - -inline YGFloatOptional YGResolveValue( - const YGValue value, - const float ownerSize) { - switch (value.unit) { - case YGUnitPoint: - return YGFloatOptional{value.value}; - case YGUnitPercent: - return YGFloatOptional{value.value * ownerSize * 0.01f}; - default: - return YGFloatOptional{}; - } -} - -inline YGFloatOptional YGResolveValue( - facebook::yoga::CompactValue value, - float ownerSize) { - return YGResolveValue((YGValue) value, ownerSize); -} - -inline bool YGFlexDirectionIsColumn(const YGFlexDirection flexDirection) { - return flexDirection == YGFlexDirectionColumn || - flexDirection == YGFlexDirectionColumnReverse; -} - -inline YGFlexDirection YGResolveFlexDirection( - const YGFlexDirection flexDirection, - const YGDirection direction) { - if (direction == YGDirectionRTL) { - if (flexDirection == YGFlexDirectionRow) { - return YGFlexDirectionRowReverse; - } else if (flexDirection == YGFlexDirectionRowReverse) { - return YGFlexDirectionRow; - } - } - - return flexDirection; -} - -inline YGFloatOptional YGResolveValueMargin( - facebook::yoga::CompactValue value, - const float ownerSize) { - return value.isAuto() ? YGFloatOptional{0} : YGResolveValue(value, ownerSize); -} diff --git a/yoga/Yoga-internal.h b/yoga/Yoga-internal.h index 3fc4f23a..34032824 100644 --- a/yoga/Yoga-internal.h +++ b/yoga/Yoga-internal.h @@ -41,8 +41,6 @@ inline bool isUndefined(double value) { return std::isnan(value); } -void throwLogicalErrorWithMessage(const char* message); - } // namespace facebook::yoga extern const std::array trailing; @@ -149,5 +147,3 @@ public: static const float kDefaultFlexGrow = 0.0f; static const float kDefaultFlexShrink = 0.0f; static const float kWebDefaultFlexShrink = 1.0f; - -extern bool YGFloatsEqual(const float a, const float b); diff --git a/yoga/Yoga.cpp b/yoga/Yoga.cpp index c150be61..1179e7a7 100644 --- a/yoga/Yoga.cpp +++ b/yoga/Yoga.cpp @@ -5,20 +5,24 @@ * LICENSE file in the root directory of this source tree. */ -#include -#include #include #include +#include +#include #include +#include #include +#include +#include +#include +#include #include #include -#include +#include #include -#include -#include "event/event.h" +#include using namespace facebook; using namespace facebook::yoga; @@ -1039,8 +1043,7 @@ static inline YGAlign YGNodeAlignItem( const YGAlign align = child->getStyle().alignSelf() == YGAlignAuto ? node->getStyle().alignItems() : child->getStyle().alignSelf(); - if (align == YGAlignBaseline && - YGFlexDirectionIsColumn(node->getStyle().flexDirection())) { + if (align == YGAlignBaseline && isColumn(node->getStyle().flexDirection())) { return YGAlignFlexStart; } return align; @@ -1095,7 +1098,7 @@ static float YGBaseline(yoga::Node* node, void* layoutContext) { } static bool YGIsBaselineLayout(const yoga::Node* node) { - if (YGFlexDirectionIsColumn(node->getStyle().flexDirection())) { + if (isColumn(node->getStyle().flexDirection())) { return false; } if (node->getStyle().alignItems() == YGAlignBaseline) { @@ -1155,15 +1158,15 @@ static YGFloatOptional YGNodeBoundAxisWithinMinAndMax( YGFloatOptional min; YGFloatOptional max; - if (YGFlexDirectionIsColumn(axis)) { - min = YGResolveValue( + if (isColumn(axis)) { + min = yoga::resolveValue( node->getStyle().minDimensions()[YGDimensionHeight], axisSize); - max = YGResolveValue( + max = yoga::resolveValue( node->getStyle().maxDimensions()[YGDimensionHeight], axisSize); - } else if (YGFlexDirectionIsRow(axis)) { - min = YGResolveValue( + } else if (isRow(axis)) { + min = yoga::resolveValue( node->getStyle().minDimensions()[YGDimensionWidth], axisSize); - max = YGResolveValue( + max = yoga::resolveValue( node->getStyle().maxDimensions()[YGDimensionWidth], axisSize); } @@ -1186,7 +1189,7 @@ static inline float YGNodeBoundAxis( const float value, const float axisSize, const float widthSize) { - return YGFloatMax( + return yoga::maxOrDefined( YGNodeBoundAxisWithinMinAndMax( node, axis, YGFloatOptional{value}, axisSize) .unwrap(), @@ -1212,7 +1215,7 @@ static void YGConstrainMaxSizeForMode( YGMeasureMode* mode, float* size) { const YGFloatOptional maxSize = - YGResolveValue( + yoga::resolveValue( node->getStyle().maxDimensions()[dim[axis]], ownerAxisSize) + YGFloatOptional(node->getMarginForAxis(axis, ownerWidth)); switch (*mode) { @@ -1247,8 +1250,8 @@ static void YGNodeComputeFlexBasisForChild( const uint32_t depth, const uint32_t generationCount) { const YGFlexDirection mainAxis = - YGResolveFlexDirection(node->getStyle().flexDirection(), direction); - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + resolveDirection(node->getStyle().flexDirection(), direction); + const bool isMainAxisRow = isRow(mainAxis); const float mainAxisSize = isMainAxisRow ? width : height; const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; @@ -1258,7 +1261,7 @@ static void YGNodeComputeFlexBasisForChild( YGMeasureMode childHeightMeasureMode; const YGFloatOptional resolvedFlexBasis = - YGResolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize); + yoga::resolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize); const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, ownerWidth); const bool isColumnStyleDimDefined = @@ -1272,15 +1275,15 @@ static void YGNodeComputeFlexBasisForChild( const YGFloatOptional paddingAndBorder = YGFloatOptional( YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth)); child->setLayoutComputedFlexBasis( - YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder)); + yoga::maxOrDefined(resolvedFlexBasis, paddingAndBorder)); } } else if (isMainAxisRow && isRowStyleDimDefined) { // The width is definite, so use that as the flex basis. const YGFloatOptional paddingAndBorder = YGFloatOptional( YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth)); - child->setLayoutComputedFlexBasis(YGFloatOptionalMax( - YGResolveValue( + child->setLayoutComputedFlexBasis(yoga::maxOrDefined( + yoga::resolveValue( child->getResolvedDimensions()[YGDimensionWidth], ownerWidth), paddingAndBorder)); } else if (!isMainAxisRow && isColumnStyleDimDefined) { @@ -1288,8 +1291,8 @@ static void YGNodeComputeFlexBasisForChild( const YGFloatOptional paddingAndBorder = YGFloatOptional(YGNodePaddingAndBorderForAxis( child, YGFlexDirectionColumn, ownerWidth)); - child->setLayoutComputedFlexBasis(YGFloatOptionalMax( - YGResolveValue( + child->setLayoutComputedFlexBasis(yoga::maxOrDefined( + yoga::resolveValue( child->getResolvedDimensions()[YGDimensionHeight], ownerHeight), paddingAndBorder)); } else { @@ -1307,7 +1310,7 @@ static void YGNodeComputeFlexBasisForChild( if (isRowStyleDimDefined) { childWidth = - YGResolveValue( + yoga::resolveValue( child->getResolvedDimensions()[YGDimensionWidth], ownerWidth) .unwrap() + marginRow; @@ -1315,7 +1318,7 @@ static void YGNodeComputeFlexBasisForChild( } if (isColumnStyleDimDefined) { childHeight = - YGResolveValue( + yoga::resolveValue( child->getResolvedDimensions()[YGDimensionHeight], ownerHeight) .unwrap() + marginColumn; @@ -1423,7 +1426,7 @@ static void YGNodeComputeFlexBasisForChild( depth, generationCount); - child->setLayoutComputedFlexBasis(YGFloatOptional(YGFloatMax( + child->setLayoutComputedFlexBasis(YGFloatOptional(yoga::maxOrDefined( child->getLayout().measuredDimensions[dim[mainAxis]], YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth)))); } @@ -1443,9 +1446,9 @@ static void YGNodeAbsoluteLayoutChild( const uint32_t depth, const uint32_t generationCount) { const YGFlexDirection mainAxis = - YGResolveFlexDirection(node->getStyle().flexDirection(), direction); - const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + resolveDirection(node->getStyle().flexDirection(), direction); + const YGFlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); float childWidth = YGUndefined; float childHeight = YGUndefined; @@ -1457,9 +1460,9 @@ static void YGNodeAbsoluteLayoutChild( child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap(); if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) { - childWidth = - YGResolveValue(child->getResolvedDimensions()[YGDimensionWidth], width) - .unwrap() + + childWidth = yoga::resolveValue( + child->getResolvedDimensions()[YGDimensionWidth], width) + .unwrap() + marginRow; } else { // If the child doesn't have a specified width, compute the width based on @@ -1478,7 +1481,7 @@ static void YGNodeAbsoluteLayoutChild( } if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) { - childHeight = YGResolveValue( + childHeight = yoga::resolveValue( child->getResolvedDimensions()[YGDimensionHeight], height) .unwrap() + marginColumn; @@ -1697,10 +1700,10 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions( // We want to make sure we don't call measure with negative size const float innerWidth = YGFloatIsUndefined(availableWidth) ? availableWidth - : YGFloatMax(0, availableWidth - paddingAndBorderAxisRow); + : yoga::maxOrDefined(0, availableWidth - paddingAndBorderAxisRow); const float innerHeight = YGFloatIsUndefined(availableHeight) ? availableHeight - : YGFloatMax(0, availableHeight - paddingAndBorderAxisColumn); + : yoga::maxOrDefined(0, availableHeight - paddingAndBorderAxisColumn); if (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly) { @@ -1873,20 +1876,20 @@ static float YGNodeCalculateAvailableInnerDim( if (!YGFloatIsUndefined(availableInnerDim)) { // We want to make sure our available height does not violate min and max // constraints - const YGFloatOptional minDimensionOptional = - YGResolveValue(node->getStyle().minDimensions()[dimension], ownerDim); + const YGFloatOptional minDimensionOptional = yoga::resolveValue( + node->getStyle().minDimensions()[dimension], ownerDim); const float minInnerDim = minDimensionOptional.isUndefined() ? 0.0f : minDimensionOptional.unwrap() - paddingAndBorder; - const YGFloatOptional maxDimensionOptional = - YGResolveValue(node->getStyle().maxDimensions()[dimension], ownerDim); + const YGFloatOptional maxDimensionOptional = yoga::resolveValue( + node->getStyle().maxDimensions()[dimension], ownerDim); const float maxInnerDim = maxDimensionOptional.isUndefined() ? FLT_MAX : maxDimensionOptional.unwrap() - paddingAndBorder; - availableInnerDim = - YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim); + availableInnerDim = yoga::maxOrDefined( + yoga::minOrDefined(availableInnerDim, maxInnerDim), minInnerDim); } return availableInnerDim; @@ -1910,7 +1913,7 @@ static float YGNodeComputeFlexBasisForChildren( YGNodeRef singleFlexChild = nullptr; const auto& children = node->getChildren(); YGMeasureMode measureModeMainDim = - YGFlexDirectionIsRow(mainAxis) ? widthMeasureMode : heightMeasureMode; + isRow(mainAxis) ? widthMeasureMode : heightMeasureMode; // If there is only one child with flexGrow + flexShrink it means we can set // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the // child to exactly match the remaining space @@ -1918,8 +1921,8 @@ static float YGNodeComputeFlexBasisForChildren( for (auto child : children) { if (child->isNodeFlexible()) { if (singleFlexChild != nullptr || - YGFloatsEqual(child->resolveFlexGrow(), 0.0f) || - YGFloatsEqual(child->resolveFlexShrink(), 0.0f)) { + yoga::inexactEquals(child->resolveFlexGrow(), 0.0f) || + yoga::inexactEquals(child->resolveFlexShrink(), 0.0f)) { // There is already a flexible child, or this flexible child doesn't // have flexGrow and flexShrink, abort singleFlexChild = nullptr; @@ -1942,12 +1945,10 @@ static float YGNodeComputeFlexBasisForChildren( if (performLayout) { // Set the initial position (relative to the owner). const YGDirection childDirection = child->resolveDirection(direction); - const float mainDim = YGFlexDirectionIsRow(mainAxis) - ? availableInnerWidth - : availableInnerHeight; - const float crossDim = YGFlexDirectionIsRow(mainAxis) - ? availableInnerHeight - : availableInnerWidth; + const float mainDim = + isRow(mainAxis) ? availableInnerWidth : availableInnerHeight; + const float crossDim = + isRow(mainAxis) ? availableInnerHeight : availableInnerWidth; child->setPosition( childDirection, mainDim, crossDim, availableInnerWidth); } @@ -1989,7 +1990,7 @@ static float YGNodeComputeFlexBasisForChildren( // computedFlexBasis properly computed(To do this use // YGNodeComputeFlexBasisForChildren function). This function calculates // YGCollectFlexItemsRowMeasurement -static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues( +static CollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues( yoga::Node* const node, const YGDirection ownerDirection, const float mainAxisownerSize, @@ -1997,11 +1998,11 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues( const float availableInnerMainDim, const uint32_t startOfLineIndex, const uint32_t lineCount) { - YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {}; + CollectFlexItemsRowValues flexAlgoRowMeasurement = {}; flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size()); float sizeConsumedOnCurrentLineIncludingMinConstraint = 0; - const YGFlexDirection mainAxis = YGResolveFlexDirection( + const YGFlexDirection mainAxis = resolveDirection( node->getStyle().flexDirection(), node->resolveDirection(ownerDirection)); const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap; const float gap = node->getGapForAxis(mainAxis, availableInnerWidth).unwrap(); @@ -2081,7 +2082,7 @@ static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues( // function the child nodes would have proper size. Prior using this function // please ensure that YGDistributeFreeSpaceFirstPass is called. static float YGDistributeFreeSpaceSecondPass( - YGCollectFlexItemsRowValues& collectedFlexItemsValues, + CollectFlexItemsRowValues& collectedFlexItemsValues, yoga::Node* const node, const YGFlexDirection mainAxis, const YGFlexDirection crossAxis, @@ -2102,7 +2103,7 @@ static float YGDistributeFreeSpaceSecondPass( float flexShrinkScaledFactor = 0; float flexGrowFactor = 0; float deltaFreeSpace = 0; - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const bool isMainAxisRow = isRow(mainAxis); const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap; for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) { @@ -2202,7 +2203,7 @@ static float YGDistributeFreeSpaceSecondPass( : YGMeasureModeAtMost; } else { childCrossSize = - YGResolveValue( + yoga::resolveValue( currentRelativeChild->getResolvedDimension(dim[crossAxis]), availableInnerCrossDim) .unwrap() + @@ -2279,7 +2280,7 @@ static float YGDistributeFreeSpaceSecondPass( // whose min and max constraints are triggered, those flex item's clamped size // is removed from the remaingfreespace. static void YGDistributeFreeSpaceFirstPass( - YGCollectFlexItemsRowValues& collectedFlexItemsValues, + CollectFlexItemsRowValues& collectedFlexItemsValues, const YGFlexDirection mainAxis, const float mainAxisownerSize, const float availableInnerMainDim, @@ -2386,7 +2387,7 @@ static void YGDistributeFreeSpaceFirstPass( // static void YGResolveFlexibleLength( yoga::Node* const node, - YGCollectFlexItemsRowValues& collectedFlexItemsValues, + CollectFlexItemsRowValues& collectedFlexItemsValues, const YGFlexDirection mainAxis, const YGFlexDirection crossAxis, const float mainAxisownerSize, @@ -2437,7 +2438,7 @@ static void YGResolveFlexibleLength( static void YGJustifyMainAxis( yoga::Node* const node, - YGCollectFlexItemsRowValues& collectedFlexItemsValues, + CollectFlexItemsRowValues& collectedFlexItemsValues, const uint32_t startOfLineIndex, const YGFlexDirection mainAxis, const YGFlexDirection crossAxis, @@ -2461,7 +2462,8 @@ static void YGJustifyMainAxis( if (measureModeMainDim == YGMeasureModeAtMost && collectedFlexItemsValues.remainingFreeSpace > 0) { if (!style.minDimensions()[dim[mainAxis]].isUndefined() && - !YGResolveValue(style.minDimensions()[dim[mainAxis]], mainAxisownerSize) + !yoga::resolveValue( + style.minDimensions()[dim[mainAxis]], mainAxisownerSize) .isUndefined()) { // This condition makes sure that if the size of main dimension(after // considering child nodes main dim, leading and trailing padding etc) @@ -2471,14 +2473,14 @@ static void YGJustifyMainAxis( // `minAvailableMainDim` denotes minimum available space in which child // can be laid out, it will exclude space consumed by padding and border. const float minAvailableMainDim = - YGResolveValue( + yoga::resolveValue( style.minDimensions()[dim[mainAxis]], mainAxisownerSize) .unwrap() - leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; const float occupiedSpaceByChildNodes = availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace; - collectedFlexItemsValues.remainingFreeSpace = - YGFloatMax(0, minAvailableMainDim - occupiedSpaceByChildNodes); + collectedFlexItemsValues.remainingFreeSpace = yoga::maxOrDefined( + 0, minAvailableMainDim - occupiedSpaceByChildNodes); } else { collectedFlexItemsValues.remainingFreeSpace = 0; } @@ -2517,7 +2519,8 @@ static void YGJustifyMainAxis( case YGJustifySpaceBetween: if (collectedFlexItemsValues.itemsOnLine > 1) { betweenMainDim += - YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) / + yoga::maxOrDefined( + collectedFlexItemsValues.remainingFreeSpace, 0) / (collectedFlexItemsValues.itemsOnLine - 1); } break; @@ -2628,14 +2631,14 @@ static void YGJustifyMainAxis( ascent; maxAscentForCurrentLine = - YGFloatMax(maxAscentForCurrentLine, ascent); + yoga::maxOrDefined(maxAscentForCurrentLine, ascent); maxDescentForCurrentLine = - YGFloatMax(maxDescentForCurrentLine, descent); + yoga::maxOrDefined(maxDescentForCurrentLine, descent); } else { // The cross dimension is the max of the elements dimension since // there can only be one element in that cross dimension in the case // when the items are not baseline aligned - collectedFlexItemsValues.crossDim = YGFloatMax( + collectedFlexItemsValues.crossDim = yoga::maxOrDefined( collectedFlexItemsValues.crossDim, YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); } @@ -2759,9 +2762,9 @@ static void YGNodelayoutImpl( node->setLayoutDirection(direction); const YGFlexDirection flexRowDirection = - YGResolveFlexDirection(YGFlexDirectionRow, direction); + resolveDirection(YGFlexDirectionRow, direction); const YGFlexDirection flexColumnDirection = - YGResolveFlexDirection(YGFlexDirectionColumn, direction); + resolveDirection(YGFlexDirectionColumn, direction); const YGEdge startEdge = direction == YGDirectionLTR ? YGEdgeLeft : YGEdgeRight; @@ -2851,9 +2854,9 @@ static void YGNodelayoutImpl( // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM const YGFlexDirection mainAxis = - YGResolveFlexDirection(node->getStyle().flexDirection(), direction); - const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + resolveDirection(node->getStyle().flexDirection(), direction); + const YGFlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap; const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; @@ -2948,7 +2951,7 @@ static void YGNodelayoutImpl( // Max main dimension of all the lines. float maxLineMainDim = 0; - YGCollectFlexItemsRowValues collectedFlexItemsValues; + CollectFlexItemsRowValues collectedFlexItemsValues; for (; endOfLineIndex < childCount; lineCount++, startOfLineIndex = endOfLineIndex) { collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues( @@ -2978,17 +2981,19 @@ static void YGNodelayoutImpl( const auto& minDimensions = node->getStyle().minDimensions(); const auto& maxDimensions = node->getStyle().maxDimensions(); const float minInnerWidth = - YGResolveValue(minDimensions[YGDimensionWidth], ownerWidth).unwrap() - + yoga::resolveValue(minDimensions[YGDimensionWidth], ownerWidth) + .unwrap() - paddingAndBorderAxisRow; const float maxInnerWidth = - YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth).unwrap() - + yoga::resolveValue(maxDimensions[YGDimensionWidth], ownerWidth) + .unwrap() - paddingAndBorderAxisRow; const float minInnerHeight = - YGResolveValue(minDimensions[YGDimensionHeight], ownerHeight) + yoga::resolveValue(minDimensions[YGDimensionHeight], ownerHeight) .unwrap() - paddingAndBorderAxisColumn; const float maxInnerHeight = - YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight) + yoga::resolveValue(maxDimensions[YGDimensionHeight], ownerHeight) .unwrap() - paddingAndBorderAxisColumn; @@ -3241,13 +3246,14 @@ static void YGNodelayoutImpl( if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto && child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { - leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2); + leadingCrossDim += + yoga::maxOrDefined(0.0f, remainingCrossDim / 2); } else if ( child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { // No-Op } else if ( child->marginLeadingValue(crossAxis).unit == YGUnitAuto) { - leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim); + leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim); } else if (alignItem == YGAlignFlexStart) { // No-Op } else if (alignItem == YGAlignCenter) { @@ -3268,7 +3274,7 @@ static void YGNodelayoutImpl( const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f; totalLineCrossDim += collectedFlexItemsValues.crossDim + appliedCrossGap; maxLineMainDim = - YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim); + yoga::maxOrDefined(maxLineMainDim, collectedFlexItemsValues.mainDim); } // STEP 8: MULTI-LINE CONTENT ALIGNMENT @@ -3331,7 +3337,7 @@ static void YGNodelayoutImpl( break; } if (YGNodeIsLayoutDimDefined(child, crossAxis)) { - lineHeight = YGFloatMax( + lineHeight = yoga::maxOrDefined( lineHeight, child->getLayout().measuredDimensions[dim[crossAxis]] + child->getMarginForAxis(crossAxis, availableInnerWidth) @@ -3351,10 +3357,10 @@ static void YGNodelayoutImpl( .unwrap() - ascent; maxAscentForCurrentLine = - YGFloatMax(maxAscentForCurrentLine, ascent); + yoga::maxOrDefined(maxAscentForCurrentLine, ascent); maxDescentForCurrentLine = - YGFloatMax(maxDescentForCurrentLine, descent); - lineHeight = YGFloatMax( + yoga::maxOrDefined(maxDescentForCurrentLine, descent); + lineHeight = yoga::maxOrDefined( lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); } } @@ -3422,11 +3428,11 @@ static void YGNodelayoutImpl( .unwrap()) : lineHeight; - if (!(YGFloatsEqual( + if (!(yoga::inexactEquals( childWidth, child->getLayout() .measuredDimensions[YGDimensionWidth]) && - YGFloatsEqual( + yoga::inexactEquals( childHeight, child->getLayout() .measuredDimensions[YGDimensionHeight]))) { @@ -3510,8 +3516,8 @@ static void YGNodelayoutImpl( measureModeMainDim == YGMeasureModeAtMost && node->getStyle().overflow() == YGOverflowScroll) { node->setLayoutMeasuredDimension( - YGFloatMax( - YGFloatMin( + yoga::maxOrDefined( + yoga::minOrDefined( availableInnerMainDim + paddingAndBorderAxisMain, YGNodeBoundAxisWithinMinAndMax( node, @@ -3541,8 +3547,8 @@ static void YGNodelayoutImpl( measureModeCrossDim == YGMeasureModeAtMost && node->getStyle().overflow() == YGOverflowScroll) { node->setLayoutMeasuredDimension( - YGFloatMax( - YGFloatMin( + yoga::maxOrDefined( + yoga::minOrDefined( availableInnerCrossDim + paddingAndBorderAxisCross, YGNodeBoundAxisWithinMinAndMax( node, @@ -3659,7 +3665,7 @@ static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize( float size, float lastComputedSize) { return sizeMode == YGMeasureModeExactly && - YGFloatsEqual(size, lastComputedSize); + yoga::inexactEquals(size, lastComputedSize); } static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits( @@ -3669,7 +3675,7 @@ static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits( float lastComputedSize) { return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined && - (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize)); + (size >= lastComputedSize || yoga::inexactEquals(size, lastComputedSize)); } static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid( @@ -3682,7 +3688,7 @@ static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid( sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) && !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) && lastSize > size && - (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); + (lastComputedSize <= size || yoga::inexactEquals(size, lastComputedSize)); } YOGA_EXPORT float YGRoundValueToPixelGrid( @@ -3712,10 +3718,10 @@ YOGA_EXPORT float YGRoundValueToPixelGrid( // - Finding the `floor`: -2.2 - fractial2 = -2.2 - 0.8 = -3 ++fractial; } - if (YGDoubleEqual(fractial, 0)) { + if (yoga::inexactEquals(fractial, 0)) { // First we check if the value is already rounded scaledValue = scaledValue - fractial; - } else if (YGDoubleEqual(fractial, 1.0)) { + } else if (yoga::inexactEquals(fractial, 1.0)) { scaledValue = scaledValue - fractial + 1.0; } else if (forceCeil) { // Next we check if we need to use forced rounding @@ -3726,7 +3732,7 @@ YOGA_EXPORT float YGRoundValueToPixelGrid( // Finally we just round the value scaledValue = scaledValue - fractial + (!YGDoubleIsUndefined(fractial) && - (fractial > 0.5 || YGDoubleEqual(fractial, 0.5)) + (fractial > 0.5 || yoga::inexactEquals(fractial, 0.5)) ? 1.0 : 0.0); } @@ -3774,9 +3780,9 @@ YOGA_EXPORT bool YGNodeCanUseCachedMeasurement( : lastHeight; const bool hasSameWidthSpec = lastWidthMode == widthMode && - YGFloatsEqual(effectiveLastWidth, effectiveWidth); + yoga::inexactEquals(effectiveLastWidth, effectiveWidth); const bool hasSameHeightSpec = lastHeightMode == heightMode && - YGFloatsEqual(effectiveLastHeight, effectiveHeight); + yoga::inexactEquals(effectiveLastHeight, effectiveHeight); const bool widthIsCompatible = hasSameWidthSpec || @@ -3908,17 +3914,19 @@ bool YGLayoutNodeInternal( } } } else if (performLayout) { - if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) && - YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) && + if (yoga::inexactEquals( + layout->cachedLayout.availableWidth, availableWidth) && + yoga::inexactEquals( + layout->cachedLayout.availableHeight, availableHeight) && layout->cachedLayout.widthMeasureMode == widthMeasureMode && layout->cachedLayout.heightMeasureMode == heightMeasureMode) { cachedResults = &layout->cachedLayout; } } else { for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { - if (YGFloatsEqual( + if (yoga::inexactEquals( layout->cachedMeasurements[i].availableWidth, availableWidth) && - YGFloatsEqual( + yoga::inexactEquals( layout->cachedMeasurements[i].availableHeight, availableHeight) && layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode && layout->cachedMeasurements[i].heightMeasureMode == @@ -4145,11 +4153,11 @@ static void YGRoundToPixelGrid( // whole number, we don't have any fraction To verify if the result is close // to whole number we want to check both floor and ceil numbers const bool hasFractionalWidth = - !YGDoubleEqual(fmod(nodeWidth * pointScaleFactor, 1.0), 0) && - !YGDoubleEqual(fmod(nodeWidth * pointScaleFactor, 1.0), 1.0); + !yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 0) && + !yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 1.0); const bool hasFractionalHeight = - !YGDoubleEqual(fmod(nodeHeight * pointScaleFactor, 1.0), 0) && - !YGDoubleEqual(fmod(nodeHeight * pointScaleFactor, 1.0), 1.0); + !yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 0) && + !yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 1.0); node->setLayoutDimension( YGRoundValueToPixelGrid( @@ -4199,15 +4207,15 @@ YOGA_EXPORT void YGNodeCalculateLayoutWithContext( const auto& maxDimensions = node->getStyle().maxDimensions(); if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, ownerWidth)) { width = - (YGResolveValue( + (yoga::resolveValue( node->getResolvedDimension(dim[YGFlexDirectionRow]), ownerWidth) + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth)) .unwrap(); widthMeasureMode = YGMeasureModeExactly; - } else if (!YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth) + } else if (!yoga::resolveValue(maxDimensions[YGDimensionWidth], ownerWidth) .isUndefined()) { - width = - YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth).unwrap(); + width = yoga::resolveValue(maxDimensions[YGDimensionWidth], ownerWidth) + .unwrap(); widthMeasureMode = YGMeasureModeAtMost; } else { width = ownerWidth; @@ -4218,16 +4226,16 @@ YOGA_EXPORT void YGNodeCalculateLayoutWithContext( float height = YGUndefined; YGMeasureMode heightMeasureMode = YGMeasureModeUndefined; if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, ownerHeight)) { - height = (YGResolveValue( + height = (yoga::resolveValue( node->getResolvedDimension(dim[YGFlexDirectionColumn]), ownerHeight) + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth)) .unwrap(); heightMeasureMode = YGMeasureModeExactly; - } else if (!YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight) + } else if (!yoga::resolveValue(maxDimensions[YGDimensionHeight], ownerHeight) .isUndefined()) { - height = - YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight).unwrap(); + height = yoga::resolveValue(maxDimensions[YGDimensionHeight], ownerHeight) + .unwrap(); heightMeasureMode = YGMeasureModeAtMost; } else { height = ownerHeight; @@ -4288,6 +4296,14 @@ YOGA_EXPORT void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { } } +static void fatalWithMessage(const char* message) { +#if defined(__cpp_exceptions) + throw std::logic_error(message); +#else + std::terminate(); +#endif +} + void YGAssert(const bool condition, const char* message) { if (!condition) { yoga::log( @@ -4296,7 +4312,7 @@ void YGAssert(const bool condition, const char* message) { nullptr, "%s\n", message); - throwLogicalErrorWithMessage(message); + fatalWithMessage(message); } } @@ -4311,7 +4327,7 @@ void YGAssertWithNode( nullptr, "%s\n", message); - throwLogicalErrorWithMessage(message); + fatalWithMessage(message); } } @@ -4326,7 +4342,7 @@ void YGAssertWithConfig( nullptr, "%s\n", message); - throwLogicalErrorWithMessage(message); + fatalWithMessage(message); } } diff --git a/yoga/algorithm/CollectFlexItemsRowValues.h b/yoga/algorithm/CollectFlexItemsRowValues.h new file mode 100644 index 00000000..58a3916a --- /dev/null +++ b/yoga/algorithm/CollectFlexItemsRowValues.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +// This struct is an helper model to hold the data for step 4 of flexbox algo, +// which is collecting the flex items in a line. +// +// - itemsOnLine: Number of items which can fit in a line considering the +// available Inner dimension, the flex items computed flexbasis and their +// margin. It may be different than the difference between start and end +// indicates because we skip over absolute-positioned items. +// +// - sizeConsumedOnCurrentLine: It is accumulation of the dimensions and margin +// of all the children on the current line. This will be used in order to +// either set the dimensions of the node if none already exist or to compute +// the remaining space left for the flexible children. +// +// - totalFlexGrowFactors: total flex grow factors of flex items which are to be +// laid in the current line +// +// - totalFlexShrinkFactors: total flex shrink factors of flex items which are +// to be laid in the current line +// +// - endOfLineIndex: Its the end index of the last flex item which was examined +// and it may or may not be part of the current line(as it may be absolutely +// positioned or including it may have caused to overshoot availableInnerDim) +// +// - relativeChildren: Maintain a vector of the child nodes that can shrink +// and/or grow. + +struct CollectFlexItemsRowValues { + uint32_t itemsOnLine; + float sizeConsumedOnCurrentLine; + float totalFlexGrowFactors; + float totalFlexShrinkScaledFactors; + uint32_t endOfLineIndex; + std::vector relativeChildren; + float remainingFreeSpace; + // The size of the mainDim for the row after considering size, padding, margin + // and border of flex items. This is used to calculate maxLineDim after going + // through all the rows to decide on the main axis size of owner. + float mainDim; + // The size of the crossDim for the row after considering size, padding, + // margin and border of flex items. Used for calculating containers crossSize. + float crossDim; +}; + +} // namespace facebook::yoga diff --git a/yoga/algorithm/FlexDirection.h b/yoga/algorithm/FlexDirection.h new file mode 100644 index 00000000..ed17ce2b --- /dev/null +++ b/yoga/algorithm/FlexDirection.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::yoga { + +inline bool isRow(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionRow || + flexDirection == YGFlexDirectionRowReverse; +} + +inline bool isColumn(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionColumn || + flexDirection == YGFlexDirectionColumnReverse; +} + +inline YGFlexDirection resolveDirection( + const YGFlexDirection flexDirection, + const YGDirection direction) { + if (direction == YGDirectionRTL) { + if (flexDirection == YGFlexDirectionRow) { + return YGFlexDirectionRowReverse; + } else if (flexDirection == YGFlexDirectionRowReverse) { + return YGFlexDirectionRow; + } + } + + return flexDirection; +} + +inline YGFlexDirection resolveCrossDirection( + const YGFlexDirection flexDirection, + const YGDirection direction) { + return isColumn(flexDirection) + ? resolveDirection(YGFlexDirectionRow, direction) + : YGFlexDirectionColumn; +} + +} // namespace facebook::yoga diff --git a/yoga/algorithm/ResolveValue.h b/yoga/algorithm/ResolveValue.h new file mode 100644 index 00000000..033a2420 --- /dev/null +++ b/yoga/algorithm/ResolveValue.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +inline YGFloatOptional resolveValue( + const YGValue value, + const float ownerSize) { + switch (value.unit) { + case YGUnitPoint: + return YGFloatOptional{value.value}; + case YGUnitPercent: + return YGFloatOptional{value.value * ownerSize * 0.01f}; + default: + return YGFloatOptional{}; + } +} + +inline YGFloatOptional resolveValue(CompactValue value, float ownerSize) { + return resolveValue((YGValue) value, ownerSize); +} + +} // namespace facebook::yoga diff --git a/yoga/debug/NodeToString.cpp b/yoga/debug/NodeToString.cpp index b3d6986d..0a033cdb 100644 --- a/yoga/debug/NodeToString.cpp +++ b/yoga/debug/NodeToString.cpp @@ -12,8 +12,8 @@ #include #include +#include #include -#include namespace facebook::yoga { @@ -24,8 +24,9 @@ static void indent(std::string& base, uint32_t level) { } static bool areFourValuesEqual(const Style::Edges& four) { - return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) && - YGValueEqual(four[0], four[3]); + return yoga::inexactEquals(four[0], four[1]) && + yoga::inexactEquals(four[0], four[2]) && + yoga::inexactEquals(four[0], four[3]); } static void appendFormattedString(std::string& str, const char* fmt, ...) { @@ -80,7 +81,7 @@ static void appendNumberIfNotZero( const YGValue number) { if (number.unit == YGUnitAuto) { base.append(str + ": auto; "); - } else if (!YGFloatsEqual(number.value, 0)) { + } else if (!yoga::inexactEquals(number.value, 0)) { appendNumberIfNotUndefined(base, str, number); } } diff --git a/yoga/node/LayoutResults.cpp b/yoga/node/LayoutResults.cpp index d6611aa1..6446b8cb 100644 --- a/yoga/node/LayoutResults.cpp +++ b/yoga/node/LayoutResults.cpp @@ -6,16 +6,16 @@ */ #include -#include +#include namespace facebook::yoga { bool LayoutResults::operator==(LayoutResults layout) const { - bool isEqual = YGFloatArrayEqual(position, layout.position) && - YGFloatArrayEqual(dimensions, layout.dimensions) && - YGFloatArrayEqual(margin, layout.margin) && - YGFloatArrayEqual(border, layout.border) && - YGFloatArrayEqual(padding, layout.padding) && + bool isEqual = yoga::inexactEquals(position, layout.position) && + yoga::inexactEquals(dimensions, layout.dimensions) && + yoga::inexactEquals(margin, layout.margin) && + yoga::inexactEquals(border, layout.border) && + yoga::inexactEquals(padding, layout.padding) && direction() == layout.direction() && hadOverflow() == layout.hadOverflow() && lastOwnerDirection == layout.lastOwnerDirection && diff --git a/yoga/node/Node.cpp b/yoga/node/Node.cpp index f0b355cf..c30ee78e 100644 --- a/yoga/node/Node.cpp +++ b/yoga/node/Node.cpp @@ -5,10 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -#include #include #include -#include + +#include +#include +#include +#include namespace facebook::yoga { @@ -110,7 +113,7 @@ CompactValue Node::computeColumnGap( YGFloatOptional Node::getLeadingPosition( const YGFlexDirection axis, const float axisSize) const { - auto leadingPosition = YGFlexDirectionIsRow(axis) + auto leadingPosition = isRow(axis) ? computeEdgeValueForRow( style_.position(), YGEdgeStart, @@ -118,13 +121,13 @@ YGFloatOptional Node::getLeadingPosition( CompactValue::ofZero()) : computeEdgeValueForColumn( style_.position(), leading[axis], CompactValue::ofZero()); - return YGResolveValue(leadingPosition, axisSize); + return yoga::resolveValue(leadingPosition, axisSize); } YGFloatOptional Node::getTrailingPosition( const YGFlexDirection axis, const float axisSize) const { - auto trailingPosition = YGFlexDirectionIsRow(axis) + auto trailingPosition = isRow(axis) ? computeEdgeValueForRow( style_.position(), YGEdgeEnd, @@ -132,11 +135,11 @@ YGFloatOptional Node::getTrailingPosition( CompactValue::ofZero()) : computeEdgeValueForColumn( style_.position(), trailing[axis], CompactValue::ofZero()); - return YGResolveValue(trailingPosition, axisSize); + return yoga::resolveValue(trailingPosition, axisSize); } bool Node::isLeadingPositionDefined(const YGFlexDirection axis) const { - auto leadingPosition = YGFlexDirectionIsRow(axis) + auto leadingPosition = isRow(axis) ? computeEdgeValueForRow( style_.position(), YGEdgeStart, @@ -148,7 +151,7 @@ bool Node::isLeadingPositionDefined(const YGFlexDirection axis) const { } bool Node::isTrailingPosDefined(const YGFlexDirection axis) const { - auto trailingPosition = YGFlexDirectionIsRow(axis) + auto trailingPosition = isRow(axis) ? computeEdgeValueForRow( style_.position(), YGEdgeEnd, @@ -162,23 +165,26 @@ bool Node::isTrailingPosDefined(const YGFlexDirection axis) const { YGFloatOptional Node::getLeadingMargin( const YGFlexDirection axis, const float widthSize) const { - auto leadingMargin = YGFlexDirectionIsRow(axis) + auto leadingMargin = isRow(axis) ? computeEdgeValueForRow( style_.margin(), YGEdgeStart, leading[axis], CompactValue::ofZero()) : computeEdgeValueForColumn( style_.margin(), leading[axis], CompactValue::ofZero()); - return YGResolveValueMargin(leadingMargin, widthSize); + return leadingMargin.isAuto() ? YGFloatOptional{0} + : yoga::resolveValue(leadingMargin, widthSize); } YGFloatOptional Node::getTrailingMargin( const YGFlexDirection axis, const float widthSize) const { - auto trailingMargin = YGFlexDirectionIsRow(axis) + auto trailingMargin = isRow(axis) ? computeEdgeValueForRow( style_.margin(), YGEdgeEnd, trailing[axis], CompactValue::ofZero()) : computeEdgeValueForColumn( style_.margin(), trailing[axis], CompactValue::ofZero()); - return YGResolveValueMargin(trailingMargin, widthSize); + return trailingMargin.isAuto() + ? YGFloatOptional{0} + : yoga::resolveValue(trailingMargin, widthSize); } YGFloatOptional Node::getMarginForAxis( @@ -190,10 +196,10 @@ YGFloatOptional Node::getMarginForAxis( YGFloatOptional Node::getGapForAxis( const YGFlexDirection axis, const float widthSize) const { - auto gap = YGFlexDirectionIsRow(axis) + auto gap = isRow(axis) ? computeColumnGap(style_.gap(), CompactValue::ofZero()) : computeRowGap(style_.gap(), CompactValue::ofZero()); - return YGResolveValue(gap, widthSize); + return yoga::resolveValue(gap, widthSize); } YGSize Node::measure( @@ -370,9 +376,9 @@ void Node::setPosition( const YGDirection directionRespectingRoot = owner_ != nullptr ? direction : YGDirectionLTR; const YGFlexDirection mainAxis = - YGResolveFlexDirection(style_.flexDirection(), directionRespectingRoot); + yoga::resolveDirection(style_.flexDirection(), directionRespectingRoot); const YGFlexDirection crossAxis = - YGFlexDirectionCross(mainAxis, directionRespectingRoot); + yoga::resolveCrossDirection(mainAxis, directionRespectingRoot); // Here we should check for `YGPositionTypeStatic` and in this case zero inset // properties (left, right, top, bottom, begin, end). @@ -399,8 +405,7 @@ void Node::setPosition( } YGValue Node::marginLeadingValue(const YGFlexDirection axis) const { - if (YGFlexDirectionIsRow(axis) && - !style_.margin()[YGEdgeStart].isUndefined()) { + if (isRow(axis) && !style_.margin()[YGEdgeStart].isUndefined()) { return style_.margin()[YGEdgeStart]; } else { return style_.margin()[leading[axis]]; @@ -408,7 +413,7 @@ YGValue Node::marginLeadingValue(const YGFlexDirection axis) const { } YGValue Node::marginTrailingValue(const YGFlexDirection axis) const { - if (YGFlexDirectionIsRow(axis) && !style_.margin()[YGEdgeEnd].isUndefined()) { + if (isRow(axis) && !style_.margin()[YGEdgeEnd].isUndefined()) { return style_.margin()[YGEdgeEnd]; } else { return style_.margin()[trailing[axis]]; @@ -431,7 +436,8 @@ void Node::resolveDimension() { const Style& style = getStyle(); for (auto dim : {YGDimensionWidth, YGDimensionHeight}) { if (!style.maxDimensions()[dim].isUndefined() && - YGValueEqual(style.maxDimensions()[dim], style.minDimensions()[dim])) { + yoga::inexactEquals( + style.maxDimensions()[dim], style.minDimensions()[dim])) { resolvedDimensions_[dim] = style.maxDimensions()[dim]; } else { resolvedDimensions_[dim] = style.dimensions()[dim]; @@ -511,7 +517,7 @@ bool Node::isNodeFlexible() { } float Node::getLeadingBorder(const YGFlexDirection axis) const { - YGValue leadingBorder = YGFlexDirectionIsRow(axis) + YGValue leadingBorder = isRow(axis) ? computeEdgeValueForRow( style_.border(), YGEdgeStart, leading[axis], CompactValue::ofZero()) : computeEdgeValueForColumn( @@ -520,7 +526,7 @@ float Node::getLeadingBorder(const YGFlexDirection axis) const { } float Node::getTrailingBorder(const YGFlexDirection axis) const { - YGValue trailingBorder = YGFlexDirectionIsRow(axis) + YGValue trailingBorder = isRow(axis) ? computeEdgeValueForRow( style_.border(), YGEdgeEnd, trailing[axis], CompactValue::ofZero()) : computeEdgeValueForColumn( @@ -531,7 +537,7 @@ float Node::getTrailingBorder(const YGFlexDirection axis) const { YGFloatOptional Node::getLeadingPadding( const YGFlexDirection axis, const float widthSize) const { - auto leadingPadding = YGFlexDirectionIsRow(axis) + auto leadingPadding = isRow(axis) ? computeEdgeValueForRow( style_.padding(), YGEdgeStart, @@ -539,20 +545,20 @@ YGFloatOptional Node::getLeadingPadding( CompactValue::ofZero()) : computeEdgeValueForColumn( style_.padding(), leading[axis], CompactValue::ofZero()); - return YGFloatOptionalMax( - YGResolveValue(leadingPadding, widthSize), YGFloatOptional(0.0f)); + return yoga::maxOrDefined( + yoga::resolveValue(leadingPadding, widthSize), YGFloatOptional(0.0f)); } YGFloatOptional Node::getTrailingPadding( const YGFlexDirection axis, const float widthSize) const { - auto trailingPadding = YGFlexDirectionIsRow(axis) + auto trailingPadding = isRow(axis) ? computeEdgeValueForRow( style_.padding(), YGEdgeEnd, trailing[axis], CompactValue::ofZero()) : computeEdgeValueForColumn( style_.padding(), trailing[axis], CompactValue::ofZero()); - return YGFloatOptionalMax( - YGResolveValue(trailingPadding, widthSize), YGFloatOptional(0.0f)); + return yoga::maxOrDefined( + yoga::resolveValue(trailingPadding, widthSize), YGFloatOptional(0.0f)); } YGFloatOptional Node::getLeadingPaddingAndBorder( diff --git a/yoga/numeric/Comparison.h b/yoga/numeric/Comparison.h new file mode 100644 index 00000000..4e9b3515 --- /dev/null +++ b/yoga/numeric/Comparison.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace facebook::yoga { + +inline float maxOrDefined(const float a, const float b) { + if (!std::isnan(a) && !std::isnan(b)) { + return fmaxf(a, b); + } + return std::isnan(a) ? b : a; +} + +inline float minOrDefined(const float a, const float b) { + if (!std::isnan(a) && !std::isnan(b)) { + return fminf(a, b); + } + + return std::isnan(a) ? b : a; +} + +inline YGFloatOptional maxOrDefined(YGFloatOptional op1, YGFloatOptional op2) { + if (op1 >= op2) { + return op1; + } + if (op2 > op1) { + return op2; + } + return op1.isUndefined() ? op2 : op1; +} + +// Custom equality functions using a hardcoded epsilon of 0.0001f, or returning +// true if both floats are NaN. +inline bool inexactEquals(const float a, const float b) { + if (!std::isnan(a) && !std::isnan(b)) { + return fabs(a - b) < 0.0001f; + } + return std::isnan(a) && std::isnan(b); +} + +inline bool inexactEquals(const double a, const double b) { + if (!std::isnan(a) && !std::isnan(b)) { + return fabs(a - b) < 0.0001; + } + return std::isnan(a) && std::isnan(b); +} + +inline bool inexactEquals(const YGValue& a, const YGValue& b) { + if (a.unit != b.unit) { + return false; + } + + if (a.unit == YGUnitUndefined || + (std::isnan(a.value) && std::isnan(b.value))) { + return true; + } + + return fabs(a.value - b.value) < 0.0001f; +} + +inline bool inexactEquals(CompactValue a, CompactValue b) { + return inexactEquals((YGValue) a, (YGValue) b); +} + +template +bool inexactEquals( + const std::array& val1, + const std::array& val2) { + bool areEqual = true; + for (std::size_t i = 0; i < Size && areEqual; ++i) { + areEqual = inexactEquals(val1[i], val2[i]); + } + return areEqual; +} + +} // namespace facebook::yoga diff --git a/yoga/style/Style.cpp b/yoga/style/Style.cpp index 806f2428..fc268347 100644 --- a/yoga/style/Style.cpp +++ b/yoga/style/Style.cpp @@ -6,7 +6,7 @@ */ #include -#include +#include namespace facebook::yoga { @@ -21,7 +21,7 @@ bool operator==(const Style& lhs, const Style& rhs) { lhs.positionType() == rhs.positionType() && lhs.flexWrap() == rhs.flexWrap() && lhs.overflow() == rhs.overflow() && lhs.display() == rhs.display() && - YGValueEqual(lhs.flexBasis(), rhs.flexBasis()) && + yoga::inexactEquals(lhs.flexBasis(), rhs.flexBasis()) && lhs.margin() == rhs.margin() && lhs.position() == rhs.position() && lhs.padding() == rhs.padding() && lhs.border() == rhs.border() && lhs.gap() == rhs.gap() && lhs.dimensions() == rhs.dimensions() &&