1/* 2 * Copyright notice... 3 */ 4 5#include "ctwm.h" 6 7#include <stdlib.h> 8#include <stdio.h> 9 10#include "r_area.h" 11#include "r_area_list.h" 12#include "util.h" 13 14 15/** 16 * Construct an RArea from given components. 17 */ 18RArea 19RAreaNew(int x, int y, int width, int height) 20{ 21 RArea area = { x, y, width, height }; 22 return area; 23} 24 25 26/** 27 * Return a pointer to a static newly constructed RArea. 28 * 29 * This is a thin wrapper around RAreaNew() that returns a static 30 * pointer. This is used in places that need to take RArea pointers, but 31 * we don't want to futz with making local intermediate vars. Currently 32 * exclusively used inline in RAreaListNew() calls. 33 */ 34RArea * 35RAreaNewStatic(int x, int y, int width, int height) 36{ 37 static RArea area; 38 area = RAreaNew(x, y, width, height); 39 return &area; 40} 41 42 43/** 44 * Return a facially-invalid RArea. 45 * 46 * This is used in places that need a sentinel value. 47 */ 48RArea 49RAreaInvalid(void) 50{ 51 RArea area = { -1, -1, -1, -1 }; 52 return area; 53} 54 55 56/** 57 * Is an RArea facially valid? 58 * 59 * Mostly used to check against sentinel values in places that may or may 60 * not have a real value to work with. 61 */ 62bool 63RAreaIsValid(const RArea *self) 64{ 65 return self->width >= 0 && self->height >= 0; 66} 67 68 69/** 70 * Return the right edge of an RArea. 71 */ 72int 73RAreaX2(const RArea *self) 74{ 75 return self->x + self->width - 1; 76} 77 78 79/** 80 * Return the bottom edge of an RArea. 81 */ 82int 83RAreaY2(const RArea *self) 84{ 85 return self->y + self->height - 1; 86} 87 88 89/** 90 * Return the area of an RArea. 91 */ 92int 93RAreaArea(const RArea *self) 94{ 95 return self->width * self->height; 96} 97 98 99/** 100 * Return an RArea describing the intersection of two RArea's. 101 */ 102RArea 103RAreaIntersect(const RArea *self, const RArea *other) 104{ 105 // Do they even intersect? 106 if(RAreaIsIntersect(self, other)) { 107 int x1, x2, y1, y2; 108 109 x1 = max(other->x, self->x); 110 x2 = min(RAreaX2(other), RAreaX2(self)); 111 112 y1 = max(other->y, self->y); 113 y2 = min(RAreaY2(other), RAreaY2(self)); 114 115 return RAreaNew(x1, y1, x2 - x1 + 1, y2 - y1 + 1); 116 } 117 118 // Nope, so nothing 119 return RAreaInvalid(); 120} 121 122 123/** 124 * Do two areas intersect? 125 */ 126bool 127RAreaIsIntersect(const RArea *self, const RArea *other) 128{ 129 // [other][self] 130 if(RAreaX2(other) < self->x) { 131 return false; 132 } 133 134 // [self][other] 135 if(other->x > RAreaX2(self)) { 136 return false; 137 } 138 139 // [other] 140 // [self] 141 if(RAreaY2(other) < self->y) { 142 return false; 143 } 144 145 // [self] 146 // [other] 147 if(other->y > RAreaY2(self)) { 148 return false; 149 } 150 151 return true; 152} 153 154 155/** 156 * Is a given coordinate inside a RArea? 157 */ 158bool 159RAreaContainsXY(const RArea *self, int x, int y) 160{ 161 return x >= self->x && x <= RAreaX2(self) 162 && y >= self->y && y <= RAreaY2(self); 163} 164 165 166/** 167 * Create a list of maximal horizontal stripes of two RArea's. 168 * 169 * This yields a set of RArea's that completely cover (without overlap) 170 * the pair of input RArea's (or NULL if the inputs are disjoint). That 171 * could be just a single RArea if e.g. they're the same height and 172 * touch/overlap horizontally, or they're the same width and 173 * touch/overlap vertically. Otherwise it will wind up being multiple 174 * rows (2 or 3). 175 * 176 * Only used in startup to populate the RLayout.horiz list. 177 */ 178RAreaList * 179RAreaHorizontalUnion(const RArea *self, const RArea *other) 180{ 181 // If there's horizontal space between them, they can't possibly 182 // combine. 183 // [other] [self] or [self] [other] 184 if(RAreaX2(other) < self->x - 1) { 185 return NULL; 186 } 187 if(other->x > RAreaX2(self) + 1) { 188 return NULL; 189 } 190 191 // No vertical overlap (though maybe they touch?) 192 // [other] or [self] 193 // [self] [other] 194 if(RAreaY2(other) < self->y || other->y > RAreaY2(self)) { 195 // Special case: if they're the same width, and start at the same 196 // X coordinate, _and_ are touching each other vertically, we can 197 // combine them into a single block. 198 if(self->width == other->width && self->x == other->x) { 199 // [other] 200 // [self ] 201 if(RAreaY2(other) + 1 == self->y) { 202 return RAreaListNew( 203 1, 204 RAreaNewStatic(self->x, other->y, 205 self->width, self->height + other->height), 206 NULL); 207 } 208 209 // [self ] 210 // [other] 211 if(RAreaY2(self) + 1 == other->y) { 212 return RAreaListNew( 213 1, 214 RAreaNewStatic(self->x, self->y, 215 self->width, self->height + other->height), 216 NULL); 217 } 218 219 // Nope, there must be vertical space between us 220 } 221 222 // Can't combine 223 return NULL; 224 } 225 226 // No horizontal space between the two (may be touching, or 227 // overlapping), and there's some vertical overlap. 228 // So there must be some horizontal stripes we can create. 229 { 230 // left- and right-most x coords, which define the maximum width 231 // a union could be. 232 const int min_x = min(self->x, other->x); 233 const int max_x = max(RAreaX2(self), RAreaX2(other)); 234 const int max_width = max_x - min_x + 1; 235 236 RAreaList *res = RAreaListNew(3, NULL); 237 238 // Figure which starts higher. 239 const RArea *top, *bot; 240 if(self->y < other->y) { 241 top = self; 242 bot = other; 243 } 244 else { 245 top = other; 246 bot = self; 247 } 248 // bot now starts even with or below top 249 250 // [ ] [ ] 251 // [bot][top] or [top][bot] 252 // [ ] [ ] 253 254 // Room in top before bot starts? That's one stripe. 255 if(bot->y != top->y) { 256 RAreaListAdd(res, RAreaNewStatic(top->x, top->y, 257 top->width, bot->y - top->y)); 258 } 259 260 // Next there's a stripe across both of them. 261 RAreaListAdd(res, 262 RAreaNewStatic(min_x, bot->y, 263 max_width, 264 min(RAreaY2(top), RAreaY2(bot)) - max(top->y, bot->y) + 1)); 265 266 // If their bottoms aren't coincident, there's another stripe 267 // below them of whichever one is taller. 268 if(RAreaY2(top) != RAreaY2(bot)) { 269 if(RAreaY2(bot) < RAreaY2(top)) { 270 // [ ] [ ] 271 // [bot][top] or [top][bot] 272 // [ ] [ ] 273 RAreaListAdd(res, 274 RAreaNewStatic(top->x, RAreaY2(bot) + 1, 275 top->width, RAreaY2(top) - RAreaY2(bot))); 276 } 277 else { 278 // [ ] [ ] 279 // [bot][top] or [top][bot] 280 // [ ] [ ] 281 RAreaListAdd(res, 282 RAreaNewStatic(bot->x, RAreaY2(top) + 1, 283 bot->width, RAreaY2(bot) - RAreaY2(top))); 284 } 285 } 286 287 // And there they are. 288 return res; 289 } 290} 291 292 293/** 294 * Create a list of maximal vertical stripes of two RArea's. 295 * 296 * This yields a set of RArea's that completely cover (without overlap) 297 * the pair of input RArea's (or NULL if the inputs are disjoint). This 298 * is the equivalent of RAreaHorizontalUnion(), except with vertical 299 * stripes. 300 * 301 * Only used in startup to populate the RLayout.vert list. 302 */ 303RAreaList * 304RAreaVerticalUnion(const RArea *self, const RArea *other) 305{ 306 // Vertical space between them; can't possibly combine. 307 if(RAreaY2(other) < self->y - 1) { 308 return NULL; 309 } 310 if(other->y > RAreaY2(self) + 1) { 311 return NULL; 312 } 313 314 // No horizontal overlap (though perhaps touching) 315 // [other][self] or [self][other] 316 if(RAreaX2(other) < self->x || other->x > RAreaX2(self)) { 317 // Special case: if they're the same height, and start at the same 318 // Y coordinate, _and_ are touching each other horizontally, we can 319 // combine them into a single block. 320 if(self->height == other->height && self->y == other->y) { 321 // [other][self] 322 if(RAreaX2(other) + 1 == self->x) { 323 return RAreaListNew( 324 1, 325 RAreaNewStatic(other->x, self->y, 326 self->width + other->width, self->height), 327 NULL); 328 } 329 330 // [self][other] 331 if(RAreaX2(self) + 1 == other->x) { 332 return RAreaListNew( 333 1, 334 RAreaNewStatic(self->x, self->y, 335 self->width + other->width, self->height), 336 NULL); 337 } 338 339 // Nope, not touching; horizontal space means no combining. 340 } 341 return NULL; 342 } 343 344 // No vertical space (touching or overlap), and some horizontal 345 // overlap. So there are vertical stripes we can make. 346 { 347 // top- and bottom-most y coords, giving a maximum height 348 const int min_y = min(self->y, other->y); 349 const int max_y = max(RAreaY2(self), RAreaY2(other)); 350 const int max_height = max_y - min_y + 1; 351 352 RAreaList *res = RAreaListNew(3, NULL); 353 354 // Which starts left-most 355 const RArea *left, *right; 356 if(self->x < other->x) { 357 left = self; 358 right = other; 359 } 360 else { 361 left = other; 362 right = self; 363 } 364 365 // [--left--] or [right] or [right] or [left] 366 // [right] [--left--] [left] [right] 367 368 // Room to the left before right starts? That's one stripe. 369 if(right->x != left->x) { 370 RAreaListAdd(res, 371 RAreaNewStatic(left->x, left->y, 372 right->x - left->x, left->height)); 373 } 374 375 // There's a stripe of their overlap. 376 RAreaListAdd(res, 377 RAreaNewStatic(right->x, min_y, 378 min(RAreaX2(left), RAreaX2(right)) - max(left->x, right->x) + 1, 379 max_height)); 380 381 // If they don't end at the same x coord, there's a third stripe 382 // of a piece to the right of one or the other. 383 if(RAreaX2(left) != RAreaX2(right)) { 384 if(RAreaX2(right) < RAreaX2(left)) { 385 // [--left--] or [right] 386 // [right] [--left--] 387 RAreaListAdd(res, 388 RAreaNewStatic(RAreaX2(right) + 1, left->y, 389 RAreaX2(left) - RAreaX2(right), left->height)); 390 } 391 else { 392 // [right] or [left] 393 // [left] [right] 394 RAreaListAdd(res, 395 RAreaNewStatic(RAreaX2(left) + 1, right->y, 396 RAreaX2(right) - RAreaX2(left), right->height)); 397 } 398 } 399 400 return res; 401 } 402} 403 404 405/** 406 * Pretty-print an RArea. 407 * 408 * Used for dev/debug. 409 */ 410void 411RAreaPrint(const RArea *self) 412{ 413 fprintf(stderr, "[x=%d y=%d w=%d h=%d]", self->x, self->y, self->width, 414 self->height); 415} 416