-
Notifications
You must be signed in to change notification settings - Fork 0
/
EBGrid.m
143 lines (116 loc) · 7.27 KB
/
EBGrid.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#import "EBGrid.h"
EBGridOptions EBGridOptionsMake(CGFloat leftBorder, CGFloat rightBorder, CGFloat topBorder, CGFloat bottomBorder, CGSize cellSize, CGSize cellSpacing)
{
EBGridOptions result = {.leftBorder = leftBorder, .rightBorder = rightBorder, .topBorder = topBorder, .bottomBorder = bottomBorder, .cellSize = cellSize, .cellSpacing = cellSpacing};
return result;
}
NSUInteger EBGridColumnCount(EBGridInfo gridInfo)
{
CGFloat usableWidth = EBCapMin(0, gridInfo.containerWidth - gridInfo.gridOptions.leftBorder - gridInfo.gridOptions.rightBorder);
NSUInteger columnCount = EBCapMin(1, (NSUInteger)(usableWidth / (gridInfo.gridOptions.cellSize.width + gridInfo.gridOptions.cellSpacing.width)));
CGFloat minUsedWidth = (columnCount * gridInfo.gridOptions.cellSize.width) + ((columnCount - 1) * gridInfo.gridOptions.cellSpacing.width);
/* The last cell doesn't have horizontal spacing, so it's possible that we miscalculated columnCount and
it's 1 less than the correct value, so we'll correct that here. */
if ((minUsedWidth + gridInfo.gridOptions.cellSize.width + gridInfo.gridOptions.cellSpacing.width) <= usableWidth)
columnCount++;
return columnCount;
}
NSUInteger EBGridRowCount(EBGridInfo gridInfo)
{
NSUInteger columnCount = EBGridColumnCount(gridInfo);
return EBCapMin(1, (gridInfo.elementCount / columnCount) + ((gridInfo.elementCount % columnCount) ? 1 : 0));
}
CGSize EBGridContainerSize(EBGridInfo gridInfo)
{
NSUInteger rowCount = EBGridRowCount(gridInfo);
return CGSizeMake(gridInfo.containerWidth, gridInfo.gridOptions.topBorder + gridInfo.gridOptions.bottomBorder + (rowCount * gridInfo.gridOptions.cellSize.height) + ((rowCount - 1) * gridInfo.gridOptions.cellSpacing.height));
}
static CGFloat calcAdditionalXSpacing(EBGridInfo gridInfo)
{
NSUInteger columnCount = EBGridColumnCount(gridInfo);
CGFloat minUsedWidth = gridInfo.gridOptions.leftBorder + gridInfo.gridOptions.rightBorder + (columnCount * gridInfo.gridOptions.cellSize.width) + ((columnCount - 1) * gridInfo.gridOptions.cellSpacing.width);
return EBCapMin((gridInfo.containerWidth - minUsedWidth), 0) / (columnCount + 1);
}
CGRect EBGridRectForCellIndex(EBGridInfo gridInfo, NSUInteger cellIndex)
{
NSUInteger columnCount = EBGridColumnCount(gridInfo);
NSUInteger rowCount = EBGridRowCount(gridInfo);
NSUInteger xIndex = cellIndex % columnCount;
NSUInteger yIndex = cellIndex / columnCount;
CGFloat additionalXSpacing = (rowCount > 1 ? calcAdditionalXSpacing(gridInfo) : 0);
return CGRectMake(gridInfo.gridOptions.leftBorder + additionalXSpacing + (xIndex * (gridInfo.gridOptions.cellSize.width + gridInfo.gridOptions.cellSpacing.width + additionalXSpacing)),
gridInfo.gridOptions.topBorder + (yIndex * (gridInfo.gridOptions.cellSize.height + gridInfo.gridOptions.cellSpacing.height)), gridInfo.gridOptions.cellSize.width, gridInfo.gridOptions.cellSize.height);
}
NSIndexSet *EBGridCellIndexesForRect(EBGridInfo gridInfo, CGRect rect)
{
NSUInteger minXIndex, maxXIndex;
NSUInteger minYIndex, maxYIndex;
BOOL cellIndexesForRectResult = EBGridMinMaxCellIndexesForRect(gridInfo, rect, &minXIndex, &maxXIndex, &minYIndex, &maxYIndex);
EBConfirmOrPerform(cellIndexesForRectResult, return [NSIndexSet new]);
NSUInteger columnCount = EBGridColumnCount(gridInfo);
NSMutableIndexSet *result = [NSMutableIndexSet new];
for (NSUInteger currentYIndex = minYIndex;; currentYIndex++)
{
[result addIndexesInRange: NSMakeRange((currentYIndex * columnCount) + minXIndex, maxXIndex - minXIndex + 1)];
if (currentYIndex == maxYIndex)
break;
}
return result;
}
NSRange EBGridCellIndexRangeForRect(EBGridInfo gridInfo, CGRect rect)
{
NSUInteger minXIndex, maxXIndex;
NSUInteger minYIndex, maxYIndex;
BOOL cellIndexesForRectResult = EBGridMinMaxCellIndexesForRect(gridInfo, rect, &minXIndex, &maxXIndex, &minYIndex, &maxYIndex);
EBConfirmOrPerform(cellIndexesForRectResult, return NSMakeRange(0, 0));
NSUInteger columnCount = EBGridColumnCount(gridInfo);
NSUInteger firstIndex = (minYIndex * columnCount) + minXIndex;
NSUInteger lastIndex = (maxYIndex * columnCount) + maxXIndex;
EBAssertOrRecover(lastIndex >= firstIndex, return NSMakeRange(0, 0));
// #warning debug
// NSLog(@"visible range: %ju-%ju", (uintmax_t)firstIndex, (uintmax_t)lastIndex);
return NSMakeRange(firstIndex, lastIndex - firstIndex + 1);
}
BOOL EBGridMinMaxCellIndexesForRect(EBGridInfo gridInfo, CGRect rect, NSUInteger *outMinXIndex, NSUInteger *outMaxXIndex, NSUInteger *outMinYIndex, NSUInteger *outMaxYIndex)
{
NSCParameterAssert(outMinXIndex);
NSCParameterAssert(outMaxXIndex);
NSCParameterAssert(outMinYIndex);
NSCParameterAssert(outMaxYIndex);
NSUInteger columnCount = EBGridColumnCount(gridInfo);
NSUInteger rowCount = EBGridRowCount(gridInfo);
CGFloat additionalXSpacing = (rowCount > 1 ? calcAdditionalXSpacing(gridInfo) : 0);
CGFloat combinedCellWidth = (gridInfo.gridOptions.cellSize.width + gridInfo.gridOptions.cellSpacing.width + additionalXSpacing);
CGFloat combinedCellHeight = (gridInfo.gridOptions.cellSize.height + gridInfo.gridOptions.cellSpacing.height);
CGFloat minX = CGRectGetMinX(rect) - gridInfo.gridOptions.leftBorder - additionalXSpacing;
CGFloat maxX = CGRectGetMaxX(rect) - gridInfo.gridOptions.leftBorder - additionalXSpacing - 1;
CGFloat minY = CGRectGetMinY(rect) - gridInfo.gridOptions.topBorder;
CGFloat maxY = CGRectGetMaxY(rect) - gridInfo.gridOptions.topBorder - 1;
NSInteger minXIndex = floor(minX / combinedCellWidth);
NSInteger maxXIndex = floor(maxX / combinedCellWidth);
NSInteger minYIndex = floor(minY / combinedCellHeight);
NSInteger maxYIndex = floor(maxY / combinedCellHeight);
BOOL minXInCell = EBValueInRangeExclusive(0, gridInfo.gridOptions.cellSize.width, fmod(minX, combinedCellWidth)) && EBValueInRange(0, columnCount - 1, minXIndex);
BOOL maxXInCell = EBValueInRangeExclusive(0, gridInfo.gridOptions.cellSize.width, fmod(maxX, combinedCellWidth)) && EBValueInRange(0, columnCount - 1, maxXIndex);
BOOL minYInCell = EBValueInRangeExclusive(0, gridInfo.gridOptions.cellSize.height, fmod(minY, combinedCellHeight)) && EBValueInRange(0, rowCount - 1, minYIndex);
BOOL maxYInCell = EBValueInRangeExclusive(0, gridInfo.gridOptions.cellSize.height, fmod(maxY, combinedCellHeight)) && EBValueInRange(0, rowCount - 1, maxYIndex);
/* Check if we're entirely within spacing */
if ((!minXInCell && !maxXInCell && minXIndex == maxXIndex) || (!minYInCell && !maxYInCell && minYIndex == maxYIndex))
return NO;
if (!minXInCell)
minXIndex++;
if (!minYInCell)
minYIndex++;
minXIndex = EBCapRange(0, columnCount - 1, minXIndex);
maxXIndex = EBCapRange(0, columnCount - 1, maxXIndex);
minYIndex = EBCapRange(0, rowCount - 1, minYIndex);
maxYIndex = EBCapRange(0, rowCount - 1, maxYIndex);
/* Sanity-check our result */
EBAssertOrRecover(minXIndex <= maxXIndex, return NO);
EBAssertOrRecover(minYIndex <= maxYIndex, return NO);
*outMinXIndex = minXIndex;
*outMaxXIndex = maxXIndex;
*outMinYIndex = minYIndex;
*outMaxYIndex = maxYIndex;
return YES;
}