Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 99 additions & 32 deletions Source/NSGeometry.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#import "common.h"
#include <math.h>
#import "Foundation/NSGeometry.h"
#import "Foundation/NSException.h"
#import "Foundation/NSScanner.h"
#import "Foundation/NSNotification.h"
#import "GSPrivate.h"
Expand Down Expand Up @@ -100,47 +101,113 @@ static BOOL GSMacOSXCompatibleGeometry(void)
return rect;
}

NSRect
NSIntegralRectWithOptions(NSRect aRect, NSAlignmentOptions options)
enum
{
NSRect rect;
CGFloat maxX, maxY;
GSAlignUnset = 0,
GSAlignFloor,
GSAlignCeil,
GSAlignNearest
};

static CGFloat
gsAlignValue(CGFloat value, int mode, BOOL tieDown)
{
switch (mode)
{
case GSAlignFloor:
return floor(value);
case GSAlignCeil:
return ceil(value);
case GSAlignNearest:
/* Round to the nearest integer; a tie goes towards positive infinity,
or towards negative infinity for a flipped edge. */
return tieDown ? ceil(value - 0.5) : floor(value + 0.5);
}
return value;
}

if (NSIsEmptyRect(aRect))
return NSMakeRect(0, 0, 0, 0);
static BOOL
gsAlignAxis(CGFloat origin, CGFloat length,
int minMode, int maxMode, int sizeMode, BOOL tieDown,
CGFloat *outOrigin, CGFloat *outLength)
{
int count = (minMode != GSAlignUnset)
+ (maxMode != GSAlignUnset)
+ (sizeMode != GSAlignUnset);

if (options & NSAlignMinXInward)
rect.origin.x = ceil(NSMinX(aRect));
else if (options & NSAlignMinXOutward)
rect.origin.x = floor(NSMinX(aRect));
else
rect.origin.x = round(NSMinX(aRect));
if (count != 2)
{
return NO;
}

if (options & NSAlignMinYInward)
rect.origin.y = ceil(NSMinY(aRect));
else if (options & NSAlignMinYOutward)
rect.origin.y = floor(NSMinY(aRect));
else
rect.origin.y = round(NSMinY(aRect));
if (minMode != GSAlignUnset && maxMode != GSAlignUnset)
{
CGFloat lo = gsAlignValue(origin, minMode, tieDown);
CGFloat hi = gsAlignValue(origin + length, maxMode, tieDown);

if (options & NSAlignMaxXInward)
maxX = floor(NSMaxX(aRect));
else if (options & NSAlignMaxXOutward)
maxX = ceil(NSMaxX(aRect));
*outOrigin = lo;
*outLength = hi - lo;
}
else if (minMode != GSAlignUnset)
{
*outOrigin = gsAlignValue(origin, minMode, tieDown);
*outLength = gsAlignValue(length, sizeMode, NO);
}
else
maxX = round(NSMaxX(aRect));
{
CGFloat hi = gsAlignValue(origin + length, maxMode, tieDown);
CGFloat size = gsAlignValue(length, sizeMode, NO);

if (options & NSAlignMaxYInward)
maxY = floor(NSMaxY(aRect));
else if (options & NSAlignMaxYOutward)
maxY = ceil(NSMaxY(aRect));
else
maxY = round(NSMaxY(aRect));
*outOrigin = hi - size;
*outLength = size;
}
return YES;
}

rect.size.width = maxX - rect.origin.x;
rect.size.height = maxY - rect.origin.y;
NSRect
NSIntegralRectWithOptions(NSRect aRect, NSAlignmentOptions options)
{
BOOL flipped = (options & NSAlignRectFlipped) ? YES : NO;
int minXMode, maxXMode, widthMode;
int minYMode, maxYMode, heightMode;
CGFloat ox, oy, ow, oh;

minXMode = (options & NSAlignMinXInward) ? GSAlignCeil
: (options & NSAlignMinXOutward) ? GSAlignFloor
: (options & NSAlignMinXNearest) ? GSAlignNearest
: GSAlignUnset;
maxXMode = (options & NSAlignMaxXInward) ? GSAlignFloor
: (options & NSAlignMaxXOutward) ? GSAlignCeil
: (options & NSAlignMaxXNearest) ? GSAlignNearest
: GSAlignUnset;
widthMode = (options & NSAlignWidthInward) ? GSAlignFloor
: (options & NSAlignWidthOutward) ? GSAlignCeil
: (options & NSAlignWidthNearest) ? GSAlignNearest
: GSAlignUnset;
minYMode = (options & NSAlignMinYInward) ? GSAlignCeil
: (options & NSAlignMinYOutward) ? GSAlignFloor
: (options & NSAlignMinYNearest) ? GSAlignNearest
: GSAlignUnset;
maxYMode = (options & NSAlignMaxYInward) ? GSAlignFloor
: (options & NSAlignMaxYOutward) ? GSAlignCeil
: (options & NSAlignMaxYNearest) ? GSAlignNearest
: GSAlignUnset;
heightMode = (options & NSAlignHeightInward) ? GSAlignFloor
: (options & NSAlignHeightOutward) ? GSAlignCeil
: (options & NSAlignHeightNearest) ? GSAlignNearest
: GSAlignUnset;

if (!gsAlignAxis(NSMinX(aRect), NSWidth(aRect),
minXMode, maxXMode, widthMode, NO, &ox, &ow)
|| !gsAlignAxis(NSMinY(aRect), NSHeight(aRect),
minYMode, maxYMode, heightMode, flipped, &oy, &oh))
{
[NSException raise: NSInvalidArgumentException
format: @"NSIntegralRectWithOptions: each axis needs exactly"
@" two of its minimum edge, maximum edge and size"];
}

return rect;
return NSMakeRect(ox, oy, ow, oh);
}


Expand Down
40 changes: 40 additions & 0 deletions Tests/base/Functions/NSGeometry2.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,46 @@ int main(void)
"NSIntegralRect of an empty rect is the zero rect");
END_SET("NSIntegralRect")

START_SET("NSIntegralRectWithOptions")
/* Each axis is aligned to whole units by the options; the expected values
were checked against Apple Foundation. */
NSRect o = R(10.3, 20.7, 5.4, 6.8);

PASS(NSEqualRects(NSIntegralRectWithOptions(o, NSAlignAllEdgesNearest),
R(10, 21, 6, 7)), "all edges nearest rounds each edge to whole units");
PASS(NSEqualRects(NSIntegralRectWithOptions(o, NSAlignAllEdgesInward),
R(11, 21, 4, 6)), "all edges inward shrinks to whole units");
PASS(NSEqualRects(NSIntegralRectWithOptions(o, NSAlignAllEdgesOutward),
R(10, 20, 6, 8)), "all edges outward grows to whole units");

PASS(NSEqualRects(NSIntegralRectWithOptions(o,
NSAlignMinXNearest | NSAlignWidthNearest
| NSAlignMinYNearest | NSAlignHeightNearest), R(10, 21, 5, 7)),
"a min edge with a size rounds the origin and size independently");
PASS(NSEqualRects(NSIntegralRectWithOptions(o,
NSAlignMaxXNearest | NSAlignWidthNearest
| NSAlignMaxYNearest | NSAlignHeightNearest), R(11, 21, 5, 7)),
"a max edge with a size anchors the far edge and rounds the size");
PASS(NSEqualRects(NSIntegralRectWithOptions(o,
NSAlignAllEdgesNearest | NSAlignRectFlipped), R(10, 21, 6, 6)),
"the flipped flag rounds a tie on the max Y edge downward");

PASS(NSEqualRects(NSIntegralRectWithOptions(R(5.3, 6.7, 0, 0),
NSAlignAllEdgesNearest), R(5, 7, 0, 0)),
"a zero-size rect keeps its aligned origin");
PASS(NSEqualRects(NSIntegralRectWithOptions(R(5.3, 6.7, 0, 0),
NSAlignAllEdgesOutward), R(5, 6, 1, 1)),
"outward grows a zero-size rect to one unit");

PASS(NSEqualRects(NSIntegralRectWithOptions(R(-2.5, -3.5, 1, 1),
NSAlignAllEdgesNearest), R(-2, -3, 1, 1)),
"a tie rounds towards positive infinity");

PASS_EXCEPTION(({ NSRect x = NSIntegralRectWithOptions(o, NSAlignMinXInward);
(void)x; }), NSInvalidArgumentException,
"an axis without exactly two of the min, max and size raises");
END_SET("NSIntegralRectWithOptions")

START_SET("NSUnionRect")
PASS(NSEqualRects(NSUnionRect(R(0, 0, 4, 4), R(2, 2, 4, 4)), R(0, 0, 6, 6)),
"NSUnionRect is the bounding box of two overlapping rects");
Expand Down
Loading