RenderCore/Geometry/CKSizeRange.mm (76 lines of code) (raw):
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import "CKSizeRange.h"
#import <functional>
#import <RenderCore/RCDimension.h>
#import <RenderCore/RCEqualityHelpers.h>
#import <RenderCore/CKMacros.h>
#define RCCAssertPositiveReal(description, num) \
RCCAssert(num >= 0 && num < CGFLOAT_MAX, @"%@ (%f) must be a real positive integer.", description, num)
#define RCCAssertInfOrPositiveReal(description, num) \
RCCAssert(isinf(num) || (num >= 0 && num < CGFLOAT_MAX), @"%@ (%f) must be infinite or a real positive integer.", description, num)
#define RCCAssertWidth(min, max) \
RCCAssert(min.width <= max.width, @"Range min width (%f) must not be larger than max width (%f).", min.width, max.width)
#define RCCAssertHeight(min, max) \
RCCAssert(min.height <= max.height, @"Range min height (%f) must not be larger than max height (%f).", min.height, max.height)
CKSizeRange::CKSizeRange(const CGSize &_min, const CGSize &_max) : min(_min), max(_max)
{
RCCAssertPositiveReal(@"Range min width", min.width);
RCCAssertPositiveReal(@"Range min height", min.height);
RCCAssertInfOrPositiveReal(@"Range max width", max.width);
RCCAssertInfOrPositiveReal(@"Range max height", max.height);
RCCAssertWidth(min, max);
RCCAssertHeight(min, max);
}
CGSize CKSizeRange::clamp(const CGSize &size) const
{
return {
MAX(min.width, MIN(max.width, size.width)),
MAX(min.height, MIN(max.height, size.height))
};
}
struct _Range {
CGFloat min;
CGFloat max;
/**
Intersects another dimension range. If the other range does not overlap, this size range "wins" by returning a
single point within its own range that is closest to the non-overlapping range.
*/
_Range intersect(const _Range &other) const
{
CGFloat newMin = MAX(min, other.min);
CGFloat newMax = MIN(max, other.max);
if (!(newMin > newMax)) {
return {newMin, newMax};
} else {
// No intersection. If we're before the other range, return our max; otherwise our min.
if (min < other.min) {
return {max, max};
} else {
return {min, min};
}
}
}
};
CKSizeRange CKSizeRange::intersect(const CKSizeRange &other) const
{
auto w = _Range({min.width, max.width}).intersect({other.min.width, other.max.width});
auto h = _Range({min.height, max.height}).intersect({other.min.height, other.max.height});
return {{w.min, h.min}, {w.max, h.max}};
}
bool CKSizeRange::operator==(const CKSizeRange &other) const
{
return CGSizeEqualToSize(min, other.min) && CGSizeEqualToSize(max, other.max);
}
NSString *CKSizeRange::description() const
{
return [NSString stringWithFormat:@"<CKSizeRange: min=%@, max=%@>", NSStringFromCGSize(min), NSStringFromCGSize(max)];
}
size_t CKSizeRange::hash() const
{
std::hash<CGFloat> hasher;
NSUInteger subhashes[] = {
hasher(min.width),
hasher(min.height),
hasher(max.width),
hasher(max.height)
};
return RCIntegerArrayHash(subhashes, CK_ARRAY_COUNT(subhashes));
}
size_t std::hash<CKSizeRange>::operator()(const CKSizeRange &s) noexcept
{
return s.hash();
};