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