From f2f3ed8654a453cf2fa7e3e5a1482aa84cd2eb50 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Tue, 29 Aug 2023 19:05:16 -0700 Subject: [PATCH] C++ Cleanup 5/N: Reorganize Utils (#1357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1357 X-link: https://github.com/facebook/react-native/pull/39199 ## This diff This splits `Utils.h` and `Utils.cpp`, and tweaks naming and namespaces. ## This stack The organization of the C++ internals of Yoga are in need of attention. 1. Some of the C++ internals are namespaced, but others not. 2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these) 2. Most of the files are in a flat hierarchy, except for event tracing in its own folder 3. Some files and functions begin with YG, others don’t 4. Some functions are uppercase, others are not 5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about 6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h) 7. There is no clear indication from file structure or type naming what is private vs not 8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers This stack does some much needed spring cleaning: 1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy 3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended 4. Utils files are split 5. Most C++ internals drop the YG prefix 6. Most C++ internal function names are all lower camel case 7. We start to split up Yoga.cpp 8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings 9. It is not possible to use private APIs without static casting handles to internal classes This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well. These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer. Reviewed By: rshest Differential Revision: D48767465 fbshipit-source-id: 5be8ed5d5062072e8544c13b0833ad15c684ca23 --- tests/YGFloatOptionalTest.cpp | 16 +- yoga/Utils.cpp | 83 ------- yoga/Utils.h | 146 ------------ yoga/Yoga-internal.h | 4 - yoga/Yoga.cpp | 252 +++++++++++---------- yoga/algorithm/CollectFlexItemsRowValues.h | 58 +++++ yoga/algorithm/FlexDirection.h | 46 ++++ yoga/algorithm/ResolveValue.h | 32 +++ yoga/debug/NodeToString.cpp | 9 +- yoga/node/LayoutResults.cpp | 12 +- yoga/node/Node.cpp | 62 ++--- yoga/numeric/Comparison.h | 87 +++++++ yoga/style/Style.cpp | 4 +- 13 files changed, 413 insertions(+), 398 deletions(-) delete mode 100644 yoga/Utils.cpp delete mode 100644 yoga/Utils.h create mode 100644 yoga/algorithm/CollectFlexItemsRowValues.h create mode 100644 yoga/algorithm/FlexDirection.h create mode 100644 yoga/algorithm/ResolveValue.h create mode 100644 yoga/numeric/Comparison.h diff --git a/tests/YGFloatOptionalTest.cpp b/tests/YGFloatOptionalTest.cpp index c35f7b2e08..656c81f786 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 7cc94022b6..0000000000 --- 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 bd8ab30922..0000000000 --- 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 3fc4f23ac3..3403282446 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 @@ class Values { 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 c150be61da..1179e7a760 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 0000000000..58a3916ac3 --- /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 0000000000..ed17ce2bc9 --- /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 0000000000..033a2420a1 --- /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 b3d6986dbe..0a033cdba5 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 d6611aa159..6446b8cbc9 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 f0b355cf35..c30ee78e7e 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 0000000000..4e9b35151f --- /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 806f24289b..fc26834702 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() &&