From d174ab8a7af1ca75dd6538b96a0a1f6529612917 Mon Sep 17 00:00:00 2001 From: Pritesh Nandgaonkar Date: Thu, 1 Mar 2018 04:00:35 -0800 Subject: [PATCH] Change NaN with large number Reviewed By: emilsjolander Differential Revision: D6969537 fbshipit-source-id: bdc09eaf703e0d313ca65c25a4fb44c99203d9bf --- .../java/com/facebook/yoga/YogaConstants.java | 21 +- .../main/java/com/facebook/yoga/YogaNode.java | 10 +- .../jni/first-party/yogajni/jni/YGJNI.cpp | 16 +- ReactCommon/yoga/yoga/Utils.cpp | 24 ++- ReactCommon/yoga/yoga/Utils.h | 34 ++- ReactCommon/yoga/yoga/YGLayout.cpp | 9 +- ReactCommon/yoga/yoga/YGNode.cpp | 41 ++-- ReactCommon/yoga/yoga/YGStyle.cpp | 10 +- ReactCommon/yoga/yoga/Yoga-internal.h | 15 +- ReactCommon/yoga/yoga/Yoga.cpp | 194 +++++++++++------- ReactCommon/yoga/yoga/Yoga.h | 15 +- 11 files changed, 262 insertions(+), 127 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaConstants.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaConstants.java index cb9bc8ca5a5a12..f235cfdeef6a7c 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaConstants.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaConstants.java @@ -9,12 +9,29 @@ public class YogaConstants { - public static final float UNDEFINED = Float.NaN; + /** + * Large positive number signifies that the property(float) is undefined. Earlier we used to have + * YGundefined as NAN, but the downside of this is that we can't use -ffast-math compiler flag as + * it assumes all floating-point calculation involve and result into finite numbers. For more + * information regarding -ffast-math compiler flag in clang, have a look at + * https://clang.llvm.org/docs/UsersManual.html#cmdoption-ffast-math + */ + public static final float UNDEFINED = (float) (10E20); public static boolean shouldUseFastMath = false; public static boolean isUndefined(float value) { - return Float.compare(value, UNDEFINED) == 0; + // Value of a float in the case of it being not defined is 10.1E20. Earlier it used to be NAN, + // the benefit of which + // was that if NAN is involved in any mathematical expression the result was NAN. But since we + // want to have `-ffast-math` + // flag being used by compiler which assumes that the floating point values are not NAN and Inf, + // we represent YGUndefined as 10.1E20. + // But now if YGUndefined is involved in any mathematical operations this value(10.1E20) would + // change. + // So the following check makes sure that if the value is outside a range (-10E8, 10E8) then it + // is undefined. + return (Float.compare(value, (float) 10E8) >= 0 || Float.compare(value, (float) -10E8) <= 0); } public static boolean isUndefined(YogaValue value) { diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java index c1c659335b5df4..df5fae91712879 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java @@ -652,11 +652,11 @@ public final long measure(float width, int widthMode, float height, int heightMo } return mMeasureFunction.measure( - this, - width, - YogaMeasureMode.fromInt(widthMode), - height, - YogaMeasureMode.fromInt(heightMode)); + this, + width, + YogaMeasureMode.fromInt(widthMode), + height, + YogaMeasureMode.fromInt(heightMode)); } private native void jni_YGNodeSetHasBaselineFunc(long nativePointer, boolean hasMeasureFunc); diff --git a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp index eb9d8120dc3dc4..46d73b44b4d7ff 100644 --- a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp +++ b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp @@ -337,13 +337,15 @@ struct JYogaValue : public JavaClass { } }; -#define YG_NODE_JNI_STYLE_PROP(javatype, type, name) \ - javatype jni_YGNodeStyleGet##name(alias_ref, jlong nativePointer) { \ - return (javatype) YGNodeStyleGet##name(_jlong2YGNodeRef(nativePointer)); \ - } \ - \ - void jni_YGNodeStyleSet##name(alias_ref, jlong nativePointer, javatype value) { \ - YGNodeStyleSet##name(_jlong2YGNodeRef(nativePointer), static_cast(value)); \ +#define YG_NODE_JNI_STYLE_PROP(javatype, type, name) \ + javatype jni_YGNodeStyleGet##name(alias_ref, jlong nativePointer) { \ + return (javatype)YGNodeStyleGet##name(_jlong2YGNodeRef(nativePointer)); \ + } \ + \ + void jni_YGNodeStyleSet##name( \ + alias_ref, jlong nativePointer, javatype value) { \ + YGNodeStyleSet##name( \ + _jlong2YGNodeRef(nativePointer), static_cast(value)); \ } #define YG_NODE_JNI_STYLE_UNIT_PROP(name) \ diff --git a/ReactCommon/yoga/yoga/Utils.cpp b/ReactCommon/yoga/yoga/Utils.cpp index 110c7f0d7022cb..412845723b22b1 100644 --- a/ReactCommon/yoga/yoga/Utils.cpp +++ b/ReactCommon/yoga/yoga/Utils.cpp @@ -15,15 +15,37 @@ YGFlexDirection YGFlexDirectionCross( : YGFlexDirectionColumn; } +float YGFloatMax(const float a, const float b) { + if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) { + return fmaxf(a, b); + } + return YGFloatIsUndefined(a) ? b : a; +} + +float YGFloatMin(const float a, const float b) { + if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) { + return fminf(a, b); + } + + return YGFloatIsUndefined(a) ? b : a; +} + bool YGValueEqual(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))) { + (YGFloatIsUndefined(a.value) && YGFloatIsUndefined(b.value))) { return true; } return fabs(a.value - b.value) < 0.0001f; } + +bool YGFloatsEqual(const float a, const float b) { + if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) { + return fabs(a - b) < 0.0001f; + } + return YGFloatIsUndefined(a) && YGFloatIsUndefined(b); +} diff --git a/ReactCommon/yoga/yoga/Utils.h b/ReactCommon/yoga/yoga/Utils.h index db52acd672c0a2..6ebeb86fc6b2b5 100644 --- a/ReactCommon/yoga/yoga/Utils.h +++ b/ReactCommon/yoga/yoga/Utils.h @@ -54,6 +54,38 @@ struct YGCollectFlexItemsRowValues { bool YGValueEqual(const YGValue a, const 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); + +// We need custom max function, since we want that, if one argument is +// YGUndefined then the max funtion should return the other argument as the max +// value. We wouldn't have needed a custom max function if YGUndefined was NAN +// as fmax has the same behaviour, but with NAN we cannot use `-ffast-math` +// compiler flag. +float YGFloatMax(const float a, const float b); + +// We need custom min function, since we want that, if one argument is +// YGUndefined then the min funtion should return the other argument as the min +// value. We wouldn't have needed a custom min function if YGUndefined was NAN +// as fmin has the same behaviour, but with NAN we cannot use `-ffast-math` +// compiler flag. +float YGFloatMin(const float a, const float b); + +// This custom float comparision function compares the array of float with +// YGFloatsEqual, as the default float comparision 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; +} + YGFlexDirection YGFlexDirectionCross( const YGFlexDirection flexDirection, const YGDirection direction); @@ -71,7 +103,7 @@ inline float YGResolveValue(const YGValue value, const float parentSize) { case YGUnitPoint: return value.value; case YGUnitPercent: - return value.value * parentSize / 100.0f; + return value.value * parentSize * 0.01; } return YGUndefined; } diff --git a/ReactCommon/yoga/yoga/YGLayout.cpp b/ReactCommon/yoga/yoga/YGLayout.cpp index 66348fc29c055c..208a6fe3f5c274 100644 --- a/ReactCommon/yoga/yoga/YGLayout.cpp +++ b/ReactCommon/yoga/yoga/YGLayout.cpp @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ #include "YGLayout.h" +#include "Utils.h" const std::array kYGDefaultDimensionValues = { {YGUndefined, YGUndefined}}; @@ -31,9 +32,11 @@ YGLayout::YGLayout() doesLegacyStretchFlagAffectsLayout(false) {} bool YGLayout::operator==(YGLayout layout) const { - bool isEqual = position == layout.position && - dimensions == layout.dimensions && margin == layout.margin && - border == layout.border && padding == layout.padding && + bool isEqual = YGFloatArrayEqual(position, layout.position) && + YGFloatArrayEqual(dimensions, layout.dimensions) && + YGFloatArrayEqual(margin, layout.margin) && + YGFloatArrayEqual(border, layout.border) && + YGFloatArrayEqual(padding, layout.padding) && direction == layout.direction && hadOverflow == layout.hadOverflow && lastParentDirection == layout.lastParentDirection && nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && diff --git a/ReactCommon/yoga/yoga/YGNode.cpp b/ReactCommon/yoga/yoga/YGNode.cpp index 89be5bfe3bb5e0..ae7fce26e8ce3a 100644 --- a/ReactCommon/yoga/yoga/YGNode.cpp +++ b/ReactCommon/yoga/yoga/YGNode.cpp @@ -624,26 +624,28 @@ bool YGNode::isNodeFlexible() { float YGNode::getLeadingBorder(const YGFlexDirection axis) { if (YGFlexDirectionIsRow(axis) && style_.border[YGEdgeStart].unit != YGUnitUndefined && + !YGFloatIsUndefined(style_.border[YGEdgeStart].value) && style_.border[YGEdgeStart].value >= 0.0f) { return style_.border[YGEdgeStart].value; } - return fmaxf( - YGComputedEdgeValue(style_.border, leading[axis], &YGValueZero)->value, - 0.0f); + float computedEdgeValue = + YGComputedEdgeValue(style_.border, leading[axis], &YGValueZero)->value; + return YGFloatMax(computedEdgeValue, 0.0f); } float YGNode::getTrailingBorder(const YGFlexDirection flexDirection) { if (YGFlexDirectionIsRow(flexDirection) && style_.border[YGEdgeEnd].unit != YGUnitUndefined && + !YGFloatIsUndefined(style_.border[YGEdgeEnd].value) && style_.border[YGEdgeEnd].value >= 0.0f) { return style_.border[YGEdgeEnd].value; } - return fmaxf( + float computedEdgeValue = YGComputedEdgeValue(style_.border, trailing[flexDirection], &YGValueZero) - ->value, - 0.0f); + ->value; + return YGFloatMax(computedEdgeValue, 0.0f); } float YGNode::getLeadingPadding( @@ -651,14 +653,16 @@ float YGNode::getLeadingPadding( const float widthSize) { if (YGFlexDirectionIsRow(axis) && style_.padding[YGEdgeStart].unit != YGUnitUndefined && - YGResolveValue(style_.padding[YGEdgeStart], widthSize) >= 0.0f) { + !YGFloatIsUndefined( + YGResolveValue(style_.padding[YGEdgeStart], widthSize)) && + YGResolveValue(style_.padding[YGEdgeStart], widthSize) > 0.0f) { return YGResolveValue(style_.padding[YGEdgeStart], widthSize); } - return fmaxf( - YGResolveValue( - *YGComputedEdgeValue(style_.padding, leading[axis], &YGValueZero), - widthSize), - 0.0f); + + float resolvedValue = YGResolveValue( + *YGComputedEdgeValue(style_.padding, leading[axis], &YGValueZero), + widthSize); + return YGFloatMax(resolvedValue, 0.0f); } float YGNode::getTrailingPadding( @@ -666,14 +670,17 @@ float YGNode::getTrailingPadding( const float widthSize) { if (YGFlexDirectionIsRow(axis) && style_.padding[YGEdgeEnd].unit != YGUnitUndefined && + !YGFloatIsUndefined( + YGResolveValue(style_.padding[YGEdgeEnd], widthSize)) && YGResolveValue(style_.padding[YGEdgeEnd], widthSize) >= 0.0f) { return YGResolveValue(style_.padding[YGEdgeEnd], widthSize); } - return fmaxf( - YGResolveValue( - *YGComputedEdgeValue(style_.padding, trailing[axis], &YGValueZero), - widthSize), - 0.0f); + + float resolvedValue = YGResolveValue( + *YGComputedEdgeValue(style_.padding, trailing[axis], &YGValueZero), + widthSize); + + return YGFloatMax(resolvedValue, 0.0f); } float YGNode::getLeadingPaddingAndBorder( diff --git a/ReactCommon/yoga/yoga/YGStyle.cpp b/ReactCommon/yoga/yoga/YGStyle.cpp index a73cb3ce6dd6ad..56b5a1e69e014b 100644 --- a/ReactCommon/yoga/yoga/YGStyle.cpp +++ b/ReactCommon/yoga/yoga/YGStyle.cpp @@ -70,21 +70,23 @@ bool YGStyle::operator==(const YGStyle& style) { YGValueArrayEqual(minDimensions, style.minDimensions) && YGValueArrayEqual(maxDimensions, style.maxDimensions); - if (!(std::isnan(flex) && std::isnan(style.flex))) { + if (!(YGFloatIsUndefined(flex) && YGFloatIsUndefined(style.flex))) { areNonFloatValuesEqual = areNonFloatValuesEqual && flex == style.flex; } - if (!(std::isnan(flexGrow) && std::isnan(style.flexGrow))) { + if (!(YGFloatIsUndefined(flexGrow) && YGFloatIsUndefined(style.flexGrow))) { areNonFloatValuesEqual = areNonFloatValuesEqual && flexGrow == style.flexGrow; } - if (!(std::isnan(flexShrink) && std::isnan(style.flexShrink))) { + if (!(YGFloatIsUndefined(flexShrink) && + YGFloatIsUndefined(style.flexShrink))) { areNonFloatValuesEqual = areNonFloatValuesEqual && flexShrink == style.flexShrink; } - if (!(std::isnan(aspectRatio) && std::isnan(style.aspectRatio))) { + if (!(YGFloatIsUndefined(aspectRatio) && + YGFloatIsUndefined(style.aspectRatio))) { areNonFloatValuesEqual = areNonFloatValuesEqual && aspectRatio == style.aspectRatio; } diff --git a/ReactCommon/yoga/yoga/Yoga-internal.h b/ReactCommon/yoga/yoga/Yoga-internal.h index 21b14b239fc870..56078d6c3f06bc 100644 --- a/ReactCommon/yoga/yoga/Yoga-internal.h +++ b/ReactCommon/yoga/yoga/Yoga-internal.h @@ -62,19 +62,20 @@ struct YGCachedMeasurement { bool isEqual = widthMeasureMode == measurement.widthMeasureMode && heightMeasureMode == measurement.heightMeasureMode; - if (!std::isnan(availableWidth) || - !std::isnan(measurement.availableWidth)) { + if (!YGFloatIsUndefined(availableWidth) || + !YGFloatIsUndefined(measurement.availableWidth)) { isEqual = isEqual && availableWidth == measurement.availableWidth; } - if (!std::isnan(availableHeight) || - !std::isnan(measurement.availableHeight)) { + if (!YGFloatIsUndefined(availableHeight) || + !YGFloatIsUndefined(measurement.availableHeight)) { isEqual = isEqual && availableHeight == measurement.availableHeight; } - if (!std::isnan(computedWidth) || !std::isnan(measurement.computedWidth)) { + if (!YGFloatIsUndefined(computedWidth) || + !YGFloatIsUndefined(measurement.computedWidth)) { isEqual = isEqual && computedWidth == measurement.computedWidth; } - if (!std::isnan(computedHeight) || - !std::isnan(measurement.computedHeight)) { + if (!YGFloatIsUndefined(computedHeight) || + !YGFloatIsUndefined(measurement.computedHeight)) { isEqual = isEqual && computedHeight == measurement.computedHeight; } diff --git a/ReactCommon/yoga/yoga/Yoga.cpp b/ReactCommon/yoga/yoga/Yoga.cpp index ff57e99f36a215..41f89e877e4caa 100644 --- a/ReactCommon/yoga/yoga/Yoga.cpp +++ b/ReactCommon/yoga/yoga/Yoga.cpp @@ -19,7 +19,10 @@ /* define fmaxf if < VC12 */ #if _MSC_VER < 1800 __forceinline const float fmaxf(const float a, const float b) { - return (a > b) ? a : b; + if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) { + return (a > b) ? a : b; + } + return YGFloatIsUndefined(a) ? b : a; } #endif #endif @@ -118,7 +121,15 @@ static int YGDefaultLog(const YGConfigRef config, #endif bool YGFloatIsUndefined(const float value) { - return std::isnan(value); + // Value of a float in the case of it being not defined is 10.1E20. Earlier + // it used to be NAN, the benefit of which was that if NAN is involved in any + // mathematical expression the result was NAN. But since we want to have + // `-ffast-math` flag being used by compiler which assumes that the floating + // point values are not NAN and Inf, we represent YGUndefined as 10.1E20. But + // now if YGUndefined is involved in any mathematical operations this + // value(10.1E20) would change. So the following check makes sure that if the + // value is outside a range (-10E8, 10E8) then it is undefined. + return value >= 10E8 || value <= -10E8; } const YGValue* YGComputedEdgeValue( @@ -788,13 +799,6 @@ bool YGLayoutNodeInternal(const YGNodeRef node, const char *reason, const YGConfigRef config); -bool YGFloatsEqual(const float a, const float b) { - if (YGFloatIsUndefined(a)) { - return YGFloatIsUndefined(b); - } - return fabs(a - b) < 0.0001f; -} - static void YGNodePrintInternal(const YGNodeRef node, const YGPrintOptions options) { std::string str; @@ -909,12 +913,15 @@ static inline float YGNodeDimWithMargin(const YGNodeRef node, static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node, const YGFlexDirection axis, const float parentSize) { + bool isUndefined = + YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value); return !( node->getResolvedDimension(dim[axis]).unit == YGUnitAuto || node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined || (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint && - node->getResolvedDimension(dim[axis]).value < 0.0f) || + !isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) || (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent && + !isUndefined && (node->getResolvedDimension(dim[axis]).value < 0.0f || YGFloatIsUndefined(parentSize)))); } @@ -964,8 +971,9 @@ static inline float YGNodeBoundAxis(const YGNodeRef node, const float value, const float axisSize, const float widthSize) { - return fmaxf(YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize), - YGNodePaddingAndBorderForAxis(node, axis, widthSize)); + return YGFloatMax( + YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize), + YGNodePaddingAndBorderForAxis(node, axis, widthSize)); } static void YGNodeSetChildTrailingPosition(const YGNodeRef node, @@ -1035,19 +1043,19 @@ static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, child->getConfig(), YGExperimentalFeatureWebFlexBasis) && child->getLayout().computedFlexBasisGeneration != gCurrentGenerationCount)) { - child->setLayoutComputedFlexBasis(fmaxf( + child->setLayoutComputedFlexBasis(YGFloatMax( resolvedFlexBasis, YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth))); } } else if (isMainAxisRow && isRowStyleDimDefined) { // The width is definite, so use that as the flex basis. - child->setLayoutComputedFlexBasis(fmaxf( + child->setLayoutComputedFlexBasis(YGFloatMax( YGResolveValue( child->getResolvedDimension(YGDimensionWidth), parentWidth), YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth))); } else if (!isMainAxisRow && isColumnStyleDimDefined) { // The height is definite, so use that as the flex basis. - child->setLayoutComputedFlexBasis(fmaxf( + child->setLayoutComputedFlexBasis(YGFloatMax( YGResolveValue( child->getResolvedDimension(YGDimensionHeight), parentHeight), YGNodePaddingAndBorderForAxis( @@ -1162,7 +1170,7 @@ static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, "measure", config); - child->setLayoutComputedFlexBasis(fmaxf( + child->setLayoutComputedFlexBasis(YGFloatMax( child->getLayout().measuredDimensions[dim[mainAxis]], YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth))); } @@ -1252,7 +1260,8 @@ static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, // If the size of the parent is defined then try to constrain the absolute child to that size // as well. This allows text within the absolute child to wrap to the size of its parent. // This is the same behavior as many browsers implement. - if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && widthMode != YGMeasureModeUndefined && + if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && + widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) && width > 0) { childWidth = width; childWidthMeasureMode = YGMeasureModeAtMost; @@ -1368,10 +1377,10 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node, // We want to make sure we don't call measure with negative size const float innerWidth = YGFloatIsUndefined(availableWidth) ? availableWidth - : fmaxf(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); const float innerHeight = YGFloatIsUndefined(availableHeight) ? availableHeight - : fmaxf( + : YGFloatMax( 0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); if (widthMeasureMode == YGMeasureModeExactly && @@ -1474,9 +1483,12 @@ static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node, const YGMeasureMode heightMeasureMode, const float parentWidth, const float parentHeight) { - if ((widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || - (heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || - (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly)) { + if ((!YGFloatIsUndefined(availableWidth) && + widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || + (!YGFloatIsUndefined(availableHeight) && + heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || + (widthMeasureMode == YGMeasureModeExactly && + heightMeasureMode == YGMeasureModeExactly)) { const float marginAxisColumn = node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); const float marginAxisRow = @@ -1545,13 +1557,16 @@ static float YGNodeCalculateAvailableInnerDim( // We want to make sure our available height does not violate min and max // constraints const float minInnerDim = - YGResolveValue(node->getStyle().minDimensions[dimension], parentDim) - - paddingAndBorder; + YGFloatIsUndefined(YGResolveValue( + node->getStyle().minDimensions[dimension], parentDim)) + ? 0.0f + : YGResolveValue(node->getStyle().minDimensions[dimension], parentDim) - + paddingAndBorder; const float maxInnerDim = YGResolveValue(node->getStyle().maxDimensions[dimension], parentDim) - paddingAndBorder; availableInnerDim = - fmaxf(fminf(availableInnerDim, maxInnerDim), minInnerDim); + YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim); } return availableInnerDim; @@ -1752,14 +1767,17 @@ static float YGDistributeFreeSpaceSecondPass( mainAxisParentSize); float updatedMainSize = childFlexBasis; - if (collectedFlexItemsValues.remainingFreeSpace < 0) { + if (!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace < 0) { flexShrinkScaledFactor = -currentRelativeChild->resolveFlexShrink() * childFlexBasis; // Is this child able to shrink? if (flexShrinkScaledFactor != 0) { float childSize; - if (collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) { + if (!YGFloatIsUndefined( + collectedFlexItemsValues.totalFlexShrinkScaledFactors) && + collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) { childSize = childFlexBasis + flexShrinkScaledFactor; } else { childSize = childFlexBasis + @@ -1775,11 +1793,13 @@ static float YGDistributeFreeSpaceSecondPass( availableInnerMainDim, availableInnerWidth); } - } else if (collectedFlexItemsValues.remainingFreeSpace > 0) { + } else if ( + !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace > 0) { flexGrowFactor = currentRelativeChild->resolveFlexGrow(); // Is this child able to grow? - if (flexGrowFactor != 0) { + if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) { updatedMainSize = YGNodeBoundAxis( currentRelativeChild, mainAxis, @@ -1926,7 +1946,8 @@ static void YGDistributeFreeSpaceFirstPass( -currentRelativeChild->resolveFlexShrink() * childFlexBasis; // Is this child able to shrink? - if (flexShrinkScaledFactor != 0) { + if (!YGFloatIsUndefined(flexShrinkScaledFactor) && + flexShrinkScaledFactor != 0) { baseMainSize = childFlexBasis + collectedFlexItemsValues.remainingFreeSpace / collectedFlexItemsValues.totalFlexShrinkScaledFactors * @@ -1937,7 +1958,9 @@ static void YGDistributeFreeSpaceFirstPass( baseMainSize, availableInnerMainDim, availableInnerWidth); - if (baseMainSize != boundMainSize) { + if (!YGFloatIsUndefined(baseMainSize) && + !YGFloatIsUndefined(boundMainSize) && + baseMainSize != boundMainSize) { // By excluding this item's size and flex factor from remaining, // this item's // min/max constraints should also trigger in the second pass @@ -1949,11 +1972,13 @@ static void YGDistributeFreeSpaceFirstPass( flexShrinkScaledFactor; } } - } else if (collectedFlexItemsValues.remainingFreeSpace > 0) { + } else if ( + !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace > 0) { flexGrowFactor = currentRelativeChild->resolveFlexGrow(); // Is this child able to grow? - if (flexGrowFactor != 0) { + if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) { baseMainSize = childFlexBasis + collectedFlexItemsValues.remainingFreeSpace / collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor; @@ -1964,7 +1989,9 @@ static void YGDistributeFreeSpaceFirstPass( availableInnerMainDim, availableInnerWidth); - if (baseMainSize != boundMainSize) { + if (!YGFloatIsUndefined(baseMainSize) && + !YGFloatIsUndefined(boundMainSize) && + baseMainSize != boundMainSize) { // By excluding this item's size and flex factor from remaining, // this item's // min/max constraints should also trigger in the second pass @@ -2069,9 +2096,9 @@ static void YGJustifyMainAxis( if (measureModeMainDim == YGMeasureModeAtMost && collectedFlexItemsValues.remainingFreeSpace > 0) { if (style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined && - YGResolveValue( - style.minDimensions[dim[mainAxis]], mainAxisParentSize) >= 0) { - collectedFlexItemsValues.remainingFreeSpace = fmaxf( + !YGFloatIsUndefined(YGResolveValue( + style.minDimensions[dim[mainAxis]], mainAxisParentSize))) { + collectedFlexItemsValues.remainingFreeSpace = YGFloatMax( 0, YGResolveValue( style.minDimensions[dim[mainAxis]], mainAxisParentSize) - @@ -2115,7 +2142,7 @@ static void YGJustifyMainAxis( case YGJustifySpaceBetween: if (collectedFlexItemsValues.itemsOnLine > 1) { betweenMainDim = - fmaxf(collectedFlexItemsValues.remainingFreeSpace, 0) / + YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) / (collectedFlexItemsValues.itemsOnLine - 1); } else { betweenMainDim = 0; @@ -2207,7 +2234,7 @@ static void YGJustifyMainAxis( // The cross dimension is the max of the elements dimension since // there can only be one element in that cross dimension. - collectedFlexItemsValues.crossDim = fmaxf( + collectedFlexItemsValues.crossDim = YGFloatMax( collectedFlexItemsValues.crossDim, YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); } @@ -2537,8 +2564,11 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerMainDim = maxInnerMainDim; } else { if (!node->getConfig()->useLegacyStretchBehaviour && - (collectedFlexItemsValues.totalFlexGrowFactors == 0 || - node->resolveFlexGrow() == 0)) { + ((YGFloatIsUndefined( + collectedFlexItemsValues.totalFlexGrowFactors) && + collectedFlexItemsValues.totalFlexGrowFactors == 0) || + (YGFloatIsUndefined(node->resolveFlexGrow()) && + node->resolveFlexGrow() == 0))) { // If we don't have any children to flex or we can't flex the node // itself, space we've used is all space we need. Root node also // should be shrunk to minimum @@ -2743,13 +2773,13 @@ static void YGNodelayoutImpl(const YGNodeRef node, if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto && child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { - leadingCrossDim += fmaxf(0.0f, remainingCrossDim / 2); + leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2); } else if ( child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { // No-Op } else if ( child->marginLeadingValue(crossAxis).unit == YGUnitAuto) { - leadingCrossDim += fmaxf(0.0f, remainingCrossDim); + leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim); } else if (alignItem == YGAlignFlexStart) { // No-Op } else if (alignItem == YGAlignCenter) { @@ -2768,7 +2798,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, } totalLineCrossDim += collectedFlexItemsValues.crossDim; - maxLineMainDim = fmaxf(maxLineMainDim, collectedFlexItemsValues.mainDim); + maxLineMainDim = + YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim); } // STEP 8: MULTI-LINE CONTENT ALIGNMENT @@ -2831,7 +2862,7 @@ static void YGNodelayoutImpl(const YGNodeRef node, break; } if (YGNodeIsLayoutDimDefined(child, crossAxis)) { - lineHeight = fmaxf( + lineHeight = YGFloatMax( lineHeight, child->getLayout().measuredDimensions[dim[crossAxis]] + child->getMarginForAxis(crossAxis, availableInnerWidth)); @@ -2845,9 +2876,12 @@ static void YGNodelayoutImpl(const YGNodeRef node, child->getMarginForAxis( YGFlexDirectionColumn, availableInnerWidth) - ascent; - maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent); - maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent); - lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + maxAscentForCurrentLine = + YGFloatMax(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + YGFloatMax(maxDescentForCurrentLine, descent); + lineHeight = YGFloatMax( + lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); } } } @@ -2990,8 +3024,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, measureModeMainDim == YGMeasureModeAtMost && node->getStyle().overflow == YGOverflowScroll) { node->setLayoutMeasuredDimension( - fmaxf( - fminf( + YGFloatMax( + YGFloatMin( availableInnerMainDim + paddingAndBorderAxisMain, YGNodeBoundAxisWithinMinAndMax( node, mainAxis, maxLineMainDim, mainAxisParentSize)), @@ -3018,8 +3052,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, measureModeCrossDim == YGMeasureModeAtMost && node->getStyle().overflow == YGOverflowScroll) { node->setLayoutMeasuredDimension( - fmaxf( - fminf( + YGFloatMax( + YGFloatMin( availableInnerCrossDim + paddingAndBorderAxisCross, YGNodeBoundAxisWithinMinAndMax( node, @@ -3134,8 +3168,11 @@ static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureM YGMeasureMode lastSizeMode, float lastSize, float lastComputedSize) { - return lastSizeMode == YGMeasureModeAtMost && sizeMode == YGMeasureModeAtMost && - lastSize > size && (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); + return lastSizeMode == YGMeasureModeAtMost && + sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) && + !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) && + lastSize > size && + (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); } float YGRoundValueToPixelGrid(const float value, @@ -3157,9 +3194,15 @@ float YGRoundValueToPixelGrid(const float value, } else { // Finally we just round the value scaledValue = scaledValue - fractial + - (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f) ? 1.0f : 0.0f); + (!YGFloatIsUndefined(fractial) && + (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f)) + ? 1.0f + : 0.0f); } - return scaledValue / pointScaleFactor; + return (YGFloatIsUndefined(scaledValue) || + YGFloatIsUndefined(pointScaleFactor)) + ? YGUndefined + : scaledValue / pointScaleFactor; } bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, @@ -3175,7 +3218,8 @@ bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, const float marginRow, const float marginColumn, const YGConfigRef config) { - if (lastComputedHeight < 0 || lastComputedWidth < 0) { + if ((!YGFloatIsUndefined(lastComputedHeight) && lastComputedHeight < 0) || + (!YGFloatIsUndefined(lastComputedWidth) && lastComputedWidth < 0)) { return false; } bool useRoundedComparison = @@ -3533,14 +3577,19 @@ static void YGRoundToPixelGrid(const YGNodeRef node, const uint32_t childCount = YGNodeGetChildCount(node); for (uint32_t i = 0; i < childCount; i++) { - YGRoundToPixelGrid(YGNodeGetChild(node, i), pointScaleFactor, absoluteNodeLeft, absoluteNodeTop); + YGRoundToPixelGrid( + YGNodeGetChild(node, i), + pointScaleFactor, + absoluteNodeLeft, + absoluteNodeTop); } } -void YGNodeCalculateLayout(const YGNodeRef node, - const float parentWidth, - const float parentHeight, - const YGDirection parentDirection) { +void YGNodeCalculateLayout( + const YGNodeRef node, + const float parentWidth, + const float parentHeight, + const YGDirection parentDirection) { // Increment the generation count. This will force the recursive routine to // visit // all dirty nodes at least once. Subsequent visits will be skipped if the @@ -3556,16 +3605,16 @@ void YGNodeCalculateLayout(const YGNodeRef node, node->getResolvedDimension(dim[YGFlexDirectionRow]), parentWidth) + node->getMarginForAxis(YGFlexDirectionRow, parentWidth); widthMeasureMode = YGMeasureModeExactly; - } else if ( - YGResolveValue( - node->getStyle().maxDimensions[YGDimensionWidth], parentWidth) >= - 0.0f) { + } else if (!YGFloatIsUndefined(YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], + parentWidth))) { width = YGResolveValue( node->getStyle().maxDimensions[YGDimensionWidth], parentWidth); widthMeasureMode = YGMeasureModeAtMost; } else { width = parentWidth; - widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined : YGMeasureModeExactly; + widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined + : YGMeasureModeExactly; } float height = YGUndefined; @@ -3576,18 +3625,17 @@ void YGNodeCalculateLayout(const YGNodeRef node, parentHeight) + node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); heightMeasureMode = YGMeasureModeExactly; - } else if ( - YGResolveValue( - node->getStyle().maxDimensions[YGDimensionHeight], parentHeight) >= - 0.0f) { + } else if (!YGFloatIsUndefined(YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], + parentHeight))) { height = YGResolveValue( node->getStyle().maxDimensions[YGDimensionHeight], parentHeight); heightMeasureMode = YGMeasureModeAtMost; } else { height = parentHeight; - heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined : YGMeasureModeExactly; + heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined + : YGMeasureModeExactly; } - if (YGLayoutNodeInternal( node, width, diff --git a/ReactCommon/yoga/yoga/Yoga.h b/ReactCommon/yoga/yoga/Yoga.h index 689f9f3e4e717b..a632917af859b7 100644 --- a/ReactCommon/yoga/yoga/Yoga.h +++ b/ReactCommon/yoga/yoga/Yoga.h @@ -18,13 +18,14 @@ #include #endif -// Not defined in MSVC++ -#ifndef NAN -static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; -#define NAN (*(const float *) __nan) -#endif - -#define YGUndefined NAN +/** Large positive number signifies that the property(float) is undefined. + *Earlier we used to have YGundefined as NAN, but the downside of this is that + *we can't use -ffast-math compiler flag as it assumes all floating-point + *calculation involve and result into finite numbers. For more information + *regarding -ffast-math compiler flag in clang, have a look at + *https://clang.llvm.org/docs/UsersManual.html#cmdoption-ffast-math + **/ +#define YGUndefined 10E20F #include "YGEnums.h" #include "YGMacros.h"