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