1 #include "tickit.h"
2 
minint(int a,int b)3 static inline int minint(int a, int b) { return a < b ? a : b; }
maxint(int a,int b)4 static inline int maxint(int a, int b) { return a > b ? a : b; }
5 
tickit_rect_init_sized(TickitRect * rect,int top,int left,int lines,int cols)6 void tickit_rect_init_sized(TickitRect *rect, int top, int left, int lines, int cols)
7 {
8   rect->top   = top;
9   rect->left  = left;
10   rect->lines = lines;
11   rect->cols  = cols;
12 }
13 
tickit_rect_init_bounded(TickitRect * rect,int top,int left,int bottom,int right)14 void tickit_rect_init_bounded(TickitRect *rect, int top, int left, int bottom, int right)
15 {
16   rect->top   = top;
17   rect->left  = left;
18   rect->lines = bottom - top;
19   rect->cols  = right - left;
20 }
21 
tickit_rect_translate(TickitRect * rect,int downward,int rightward)22 void tickit_rect_translate(TickitRect *rect, int downward, int rightward)
23 {
24   rect->top += downward;
25   rect->left += rightward;
26 }
27 
tickit_rect_intersect(TickitRect * dst,const TickitRect * a,const TickitRect * b)28 bool tickit_rect_intersect(TickitRect *dst, const TickitRect *a, const TickitRect *b)
29 {
30   int top    = maxint(a->top, b->top);
31   int bottom = minint(tickit_rect_bottom(a), tickit_rect_bottom(b));
32 
33   if(top >= bottom)
34     return false;
35 
36   int left  = maxint(a->left, b->left);
37   int right = minint(tickit_rect_right(a), tickit_rect_right(b));
38 
39   if(left >= right)
40     return false;
41 
42   tickit_rect_init_bounded(dst, top, left, bottom, right);
43   return true;
44 }
45 
tickit_rect_intersects(const TickitRect * a,const TickitRect * b)46 bool tickit_rect_intersects(const TickitRect *a, const TickitRect *b)
47 {
48   return a->top < tickit_rect_bottom(b) &&
49          b->top < tickit_rect_bottom(a) &&
50          a->left < tickit_rect_right(b) &&
51          b->left < tickit_rect_right(a);
52 }
53 
tickit_rect_contains(const TickitRect * large,const TickitRect * small)54 bool tickit_rect_contains(const TickitRect *large, const TickitRect *small)
55 {
56   return (small->top                >= large->top               ) &&
57          (tickit_rect_bottom(small) <= tickit_rect_bottom(large)) &&
58          (small->left               >= large->left              ) &&
59          (tickit_rect_right(small)  <= tickit_rect_right(large) );
60 }
61 
tickit_rect_add(TickitRect ret[3],const TickitRect * a,const TickitRect * b)62 int tickit_rect_add(TickitRect ret[3], const TickitRect *a, const TickitRect *b)
63 {
64   int a_bottom = tickit_rect_bottom(a);
65   int a_right  = tickit_rect_right(a);
66   int b_bottom = tickit_rect_bottom(b);
67   int b_right  = tickit_rect_right(b);
68 
69   if(a->left > b_right  || b->left > a_right ||
70       a->top  > b_bottom || b->top  > a_bottom) {
71     ret[0] = *a;
72     ret[1] = *b;
73     return 2;
74   }
75 
76   int rows[4];
77   rows[0] = a->top;
78   rows[1] = b->top;
79   rows[2] = a_bottom;
80   rows[3] = b_bottom;
81 
82   /* Since we know top < bottom for each rect individually we can do this
83    * better than a plain sort
84    */
85   if(rows[0] > rows[1]) { int tmp = rows[0]; rows[0] = rows[1]; rows[1] = tmp; } // tops now in order
86   if(rows[2] > rows[3]) { int tmp = rows[2]; rows[2] = rows[3]; rows[3] = tmp; } // bottoms now in order
87   if(rows[1] > rows[2]) { int tmp = rows[1]; rows[1] = rows[2]; rows[2] = tmp; } // all now in order
88 
89   int rects = 0;
90   for(int i = 0; i < 3; i++ ) {
91     int this_top    = rows[i];
92     int this_bottom = rows[i+1];
93 
94     // Skip non-unique
95     if(this_top == this_bottom)
96       continue;
97 
98     int has_a = this_top >= a->top && this_bottom <= a_bottom;
99     int has_b = this_top >= b->top && this_bottom <= b_bottom;
100 
101     int this_left =  (has_a && has_b) ? minint(a->left, b->left) :
102                      (has_a)          ? a->left                  :
103                                         b->left;
104 
105     int this_right = (has_a && has_b) ? maxint(a_right, b_right) :
106                      (has_a)          ? a_right                  :
107                                         b_right;
108 
109     if(rects && ret[rects-1].left == this_left && ret[rects-1].cols == this_right - this_left) {
110       ret[rects-1].lines = this_bottom - ret[rects-1].top;
111     }
112     else {
113       tickit_rect_init_bounded(ret + rects, this_top, this_left, this_bottom, this_right);
114       rects++;
115     }
116   }
117 
118   return rects;
119 }
120 
tickit_rect_subtract(TickitRect ret[5],const TickitRect * orig,const TickitRect * hole)121 int tickit_rect_subtract(TickitRect ret[5], const TickitRect *orig, const TickitRect *hole)
122 {
123   if(tickit_rect_contains(hole, orig))
124     return 0;
125 
126   if(!tickit_rect_intersects(hole, orig)) {
127     ret[0] = *orig;
128     return 1;
129   }
130 
131   int rects = 0;
132 
133   int orig_right = tickit_rect_right(orig);
134   int hole_right = tickit_rect_right(hole);
135 
136   if(orig->top < hole->top) {
137     tickit_rect_init_bounded(ret + rects, orig->top, orig->left, hole->top, orig_right);
138     rects++;
139   }
140 
141   int orig_bottom = tickit_rect_bottom(orig);
142   int hole_bottom = tickit_rect_bottom(hole);
143 
144   int mid_top    = maxint(orig->top,   hole->top);
145   int mid_bottom = minint(orig_bottom, hole_bottom);
146 
147   if(orig->left < hole->left) {
148     tickit_rect_init_bounded(ret + rects, mid_top, orig->left, mid_bottom, hole->left);
149     rects++;
150   }
151 
152   if(orig_right > hole_right) {
153     tickit_rect_init_bounded(ret + rects, mid_top, hole_right, mid_bottom, orig_right);
154     rects++;
155   }
156 
157   if(orig_bottom > hole_bottom) {
158     tickit_rect_init_bounded(ret + rects, hole_bottom, orig->left, orig_bottom, orig_right);
159     rects++;
160   }
161 
162   return rects;
163 }
164