1 #include "tickit.h"
2
3 #include <string.h> // memcpy, memmove
4
5 struct TickitRectSet {
6 TickitRect *rects;
7 size_t count; /* How many we consider valid */
8 size_t size; /* How many can fit in the allocated array */
9 };
10
tickit_rectset_new(void)11 TickitRectSet *tickit_rectset_new(void)
12 {
13 TickitRectSet *ret = malloc(sizeof(TickitRectSet));
14 if(!ret)
15 return NULL;
16
17 ret->size = 4;
18 ret->rects = malloc(ret->size * sizeof(ret->rects[0]));
19 if(!ret->rects)
20 goto abort_free;
21
22 ret->count = 0;
23
24 return ret;
25
26 abort_free:
27 free(ret);
28 return NULL;
29 }
30
tickit_rectset_destroy(TickitRectSet * trs)31 void tickit_rectset_destroy(TickitRectSet *trs)
32 {
33 free(trs->rects);
34 free(trs);
35 }
36
tickit_rectset_clear(TickitRectSet * trs)37 void tickit_rectset_clear(TickitRectSet *trs)
38 {
39 trs->count = 0;
40 }
41
tickit_rectset_rects(const TickitRectSet * trs)42 size_t tickit_rectset_rects(const TickitRectSet *trs)
43 {
44 return trs->count;
45 }
46
tickit_rectset_get_rect(const TickitRectSet * trs,size_t i,TickitRect * rect)47 size_t tickit_rectset_get_rect(const TickitRectSet *trs, size_t i, TickitRect *rect)
48 {
49 if(i >= trs->count)
50 return 0;
51
52 memcpy(rect, trs->rects + i, sizeof(trs->rects[0]));
53 return 1;
54 }
55
tickit_rectset_get_rects(const TickitRectSet * trs,TickitRect rects[],size_t n)56 size_t tickit_rectset_get_rects(const TickitRectSet *trs, TickitRect rects[], size_t n)
57 {
58 if(n > trs->count)
59 n = trs->count;
60
61 memcpy(rects, trs->rects, n * sizeof(trs->rects[0]));
62 return n;
63 }
64
cmprect(const TickitRect * a,const TickitRect * b)65 static int cmprect(const TickitRect *a, const TickitRect *b)
66 {
67 if(a->top != b->top)
68 return a->top - b->top;
69 return a->left - b->left;
70 }
71
insert_rect(TickitRectSet * trs,const TickitRect * r)72 static int insert_rect(TickitRectSet *trs, const TickitRect *r)
73 {
74 if(trs->count + 1 > trs->size) {
75 TickitRect *newrects = realloc(trs->rects, trs->size * 2 * sizeof(trs->rects[0]));
76 if(!newrects)
77 return 0;
78
79 trs->rects = newrects;
80 trs->size *= 2;
81 }
82
83 /* TODO: binary search */
84 int idx;
85 for(idx = 0; idx < trs->count; idx++)
86 if(cmprect(trs->rects + idx, r) > 0)
87 break;
88
89 memmove(trs->rects + idx + 1, trs->rects + idx, (trs->count - idx) * sizeof(trs->rects[0]));
90 trs->rects[idx] = *r;
91 trs->count++;
92 return 1;
93 }
94
delete_rect(TickitRectSet * trs,int idx)95 static void delete_rect(TickitRectSet *trs, int idx)
96 {
97 memmove(trs->rects + idx, trs->rects + idx + 1, (trs->count - idx - 1) * sizeof(trs->rects[0]));
98 trs->count--;
99 }
100
tickit_rectset_add(TickitRectSet * trs,const TickitRect * rect)101 void tickit_rectset_add(TickitRectSet *trs, const TickitRect *rect)
102 {
103 int top = rect->top;
104 int bottom = tickit_rect_bottom(rect);
105 int left = rect->left;
106 int right = tickit_rect_right(rect);
107
108 restart:
109 for(int i = 0; i < trs->count; i++) {
110 TickitRect *r = trs->rects + i;
111 int r_bottom = tickit_rect_bottom(r);
112 int r_right = tickit_rect_right(r);
113
114 // Because the rects are ordered, there is no point continuing if we're
115 // already past it
116 if(bottom < r->top)
117 break;
118
119 if(top > r_bottom || left > r_right || right < r->left)
120 continue;
121
122 if(tickit_rect_contains(r, rect))
123 // Already entirely covered, just return
124 return;
125
126 int top_eq = top == r->top;
127 int bottom_eq = bottom == r_bottom;
128 int left_eq = left == r->left;
129 int right_eq = right == r_right;
130
131 if((top_eq && bottom_eq) || (left_eq && right_eq)) {
132 // Stretch an existing rectangle horizontally or vertically
133 // Tempting to think we can just do this in-place but needs to account
134 // for being able to merge multiple at once
135 if(r->top < top) top = r->top;
136 if(r_bottom > bottom) bottom = r_bottom;
137 if(r->left < left) left = r->left;
138 if(r_right > right) right = r_right;
139
140 delete_rect(trs, i);
141 goto restart;
142 }
143
144 if(top == r_bottom || bottom == r->top)
145 // No actual interaction, just add it. This case handles the recursion
146 // implied at the end of this loop
147 continue;
148
149 // Non-simple interaction. Split r and rect into the 2 or 3 separate rects
150 // it now must be composed of, delete r, then recurse on those to-be-added
151 // rects instead.
152 TickitRect to_add[3];
153 int n = tickit_rect_add(to_add, r, rect); // TODO: top/left/bottom/right ?
154
155 delete_rect(trs, i);
156
157 for(i = 0; i < n; i++)
158 tickit_rectset_add(trs, to_add + i);
159 return;
160 }
161
162 // If we got this far then we need to add it
163 TickitRect new;
164 tickit_rect_init_bounded(&new, top, left, bottom, right);
165
166 // TODO: error handling
167 insert_rect(trs, &new);
168 }
169
tickit_rectset_subtract(TickitRectSet * trs,const TickitRect * rect)170 void tickit_rectset_subtract(TickitRectSet *trs, const TickitRect *rect)
171 {
172 for(int i = 0; i < trs->count; i++) {
173 TickitRect *r = trs->rects + i;
174 if(!tickit_rect_intersects(r, rect))
175 continue;
176
177 TickitRect remains[4];
178 int n = tickit_rect_subtract(remains, r, rect);
179
180 delete_rect(trs, i);
181 i--;
182
183 // It doesn't matter if insert starts putting these before i; the worst
184 // that will happen is that a few rects that we inspected once just move
185 // underneath us and we have to inspect them a second time
186 int j;
187 for(j = 0; j < n; j++)
188 tickit_rectset_add(trs, remains + j);
189 }
190 }
191
tickit_rectset_translate(TickitRectSet * trs,int downward,int rightward)192 void tickit_rectset_translate(TickitRectSet *trs, int downward, int rightward)
193 {
194 for(int i = 0; i < trs->count; i++) {
195 trs->rects[i].top += downward;
196 trs->rects[i].left += rightward;
197 }
198 }
199
tickit_rectset_intersects(const TickitRectSet * trs,const TickitRect * rect)200 bool tickit_rectset_intersects(const TickitRectSet *trs, const TickitRect *rect)
201 {
202 for(int i = 0; i < trs->count; i++)
203 if(tickit_rect_intersects(trs->rects + i, rect))
204 return true;
205
206 return false;
207 }
208
tickit_rectset_contains(const TickitRectSet * trs,const TickitRect * rectptr)209 bool tickit_rectset_contains(const TickitRectSet *trs, const TickitRect *rectptr)
210 {
211 // We might want to modify it
212 TickitRect rect = *rectptr;
213
214 for(int i = 0; i < trs->count; i++) {
215 TickitRect *r = trs->rects + i;
216 if(!tickit_rect_intersects(r, &rect))
217 continue;
218
219 // Because rects are in order, if there's any part of rect above or to
220 // the left of here we know we didn't match it
221 if(rect.top < r->top ||
222 rect.left < r->left)
223 return false;
224
225 int r_bottom = tickit_rect_bottom(r);
226 int rect_bottom = tickit_rect_bottom(&rect);
227
228 if(rect.top < r_bottom && r_bottom < rect_bottom) {
229 int rect_right = tickit_rect_right(&rect);
230
231 TickitRect lower;
232 tickit_rect_init_bounded(&lower, r_bottom, rect.left, rect_bottom, rect_right);
233
234 if(!tickit_rectset_contains(trs, &lower))
235 return false;
236
237 // tickit_rect_init_bounded(&rect, rect.top, rect.left, r_bottom, r_right)
238 rect.lines = r_bottom - rect.top;
239 }
240
241 return tickit_rect_contains(r, &rect);
242 }
243
244 return false;
245 }
246